@twelvehart/supermemory-runtime 1.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/.env.example +57 -0
  2. package/README.md +374 -0
  3. package/dist/index.js +189 -0
  4. package/dist/mcp/index.js +1132 -0
  5. package/docker-compose.prod.yml +91 -0
  6. package/docker-compose.yml +358 -0
  7. package/drizzle/0000_dapper_the_professor.sql +159 -0
  8. package/drizzle/0001_api_keys.sql +51 -0
  9. package/drizzle/meta/0000_snapshot.json +1532 -0
  10. package/drizzle/meta/_journal.json +13 -0
  11. package/drizzle.config.ts +20 -0
  12. package/package.json +114 -0
  13. package/scripts/add-extraction-job.ts +122 -0
  14. package/scripts/benchmark-pgvector.ts +122 -0
  15. package/scripts/bootstrap.sh +209 -0
  16. package/scripts/check-runtime-pack.ts +111 -0
  17. package/scripts/claude-mcp-config.ts +336 -0
  18. package/scripts/docker-entrypoint.sh +183 -0
  19. package/scripts/doctor.ts +377 -0
  20. package/scripts/init-db.sql +33 -0
  21. package/scripts/install.sh +1110 -0
  22. package/scripts/mcp-setup.ts +271 -0
  23. package/scripts/migrations/001_create_pgvector_extension.sql +31 -0
  24. package/scripts/migrations/002_create_memory_embeddings_table.sql +75 -0
  25. package/scripts/migrations/003_create_hnsw_index.sql +94 -0
  26. package/scripts/migrations/004_create_memory_embeddings_standalone.sql +70 -0
  27. package/scripts/migrations/005_create_chunks_table.sql +95 -0
  28. package/scripts/migrations/006_create_processing_queue.sql +45 -0
  29. package/scripts/migrations/generate_test_data.sql +42 -0
  30. package/scripts/migrations/phase1_comprehensive_test.sql +204 -0
  31. package/scripts/migrations/run_migrations.sh +286 -0
  32. package/scripts/migrations/test_hnsw_index.sql +255 -0
  33. package/scripts/pre-commit-secrets +282 -0
  34. package/scripts/run-extraction-worker.ts +46 -0
  35. package/scripts/run-phase1-tests.sh +291 -0
  36. package/scripts/setup.ts +222 -0
  37. package/scripts/smoke-install.sh +12 -0
  38. package/scripts/test-health-endpoint.sh +328 -0
  39. package/src/api/index.ts +2 -0
  40. package/src/api/middleware/auth.ts +80 -0
  41. package/src/api/middleware/csrf.ts +308 -0
  42. package/src/api/middleware/errorHandler.ts +166 -0
  43. package/src/api/middleware/rateLimit.ts +360 -0
  44. package/src/api/middleware/validation.ts +514 -0
  45. package/src/api/routes/documents.ts +286 -0
  46. package/src/api/routes/profiles.ts +237 -0
  47. package/src/api/routes/search.ts +71 -0
  48. package/src/api/stores/index.ts +58 -0
  49. package/src/config/bootstrap-env.ts +3 -0
  50. package/src/config/env.ts +71 -0
  51. package/src/config/feature-flags.ts +25 -0
  52. package/src/config/index.ts +140 -0
  53. package/src/config/secrets.config.ts +291 -0
  54. package/src/db/client.ts +92 -0
  55. package/src/db/index.ts +73 -0
  56. package/src/db/postgres.ts +72 -0
  57. package/src/db/schema/chunks.schema.ts +31 -0
  58. package/src/db/schema/containers.schema.ts +46 -0
  59. package/src/db/schema/documents.schema.ts +49 -0
  60. package/src/db/schema/embeddings.schema.ts +32 -0
  61. package/src/db/schema/index.ts +11 -0
  62. package/src/db/schema/memories.schema.ts +72 -0
  63. package/src/db/schema/profiles.schema.ts +34 -0
  64. package/src/db/schema/queue.schema.ts +59 -0
  65. package/src/db/schema/relationships.schema.ts +42 -0
  66. package/src/db/schema.ts +223 -0
  67. package/src/db/worker-connection.ts +47 -0
  68. package/src/index.ts +235 -0
  69. package/src/mcp/CLAUDE.md +1 -0
  70. package/src/mcp/index.ts +1380 -0
  71. package/src/mcp/legacyState.ts +22 -0
  72. package/src/mcp/rateLimit.ts +358 -0
  73. package/src/mcp/resources.ts +309 -0
  74. package/src/mcp/results.ts +104 -0
  75. package/src/mcp/tools.ts +401 -0
  76. package/src/queues/config.ts +119 -0
  77. package/src/queues/index.ts +289 -0
  78. package/src/sdk/client.ts +225 -0
  79. package/src/sdk/errors.ts +266 -0
  80. package/src/sdk/http.ts +560 -0
  81. package/src/sdk/index.ts +244 -0
  82. package/src/sdk/resources/base.ts +65 -0
  83. package/src/sdk/resources/connections.ts +204 -0
  84. package/src/sdk/resources/documents.ts +163 -0
  85. package/src/sdk/resources/index.ts +10 -0
  86. package/src/sdk/resources/memories.ts +150 -0
  87. package/src/sdk/resources/search.ts +60 -0
  88. package/src/sdk/resources/settings.ts +36 -0
  89. package/src/sdk/types.ts +674 -0
  90. package/src/services/chunking/index.ts +451 -0
  91. package/src/services/chunking.service.ts +650 -0
  92. package/src/services/csrf.service.ts +252 -0
  93. package/src/services/documents.repository.ts +219 -0
  94. package/src/services/documents.service.ts +191 -0
  95. package/src/services/embedding.service.ts +404 -0
  96. package/src/services/extraction.service.ts +300 -0
  97. package/src/services/extractors/code.extractor.ts +451 -0
  98. package/src/services/extractors/index.ts +9 -0
  99. package/src/services/extractors/markdown.extractor.ts +461 -0
  100. package/src/services/extractors/pdf.extractor.ts +315 -0
  101. package/src/services/extractors/text.extractor.ts +118 -0
  102. package/src/services/extractors/url.extractor.ts +243 -0
  103. package/src/services/index.ts +235 -0
  104. package/src/services/ingestion.service.ts +177 -0
  105. package/src/services/llm/anthropic.ts +400 -0
  106. package/src/services/llm/base.ts +460 -0
  107. package/src/services/llm/contradiction-detector.service.ts +526 -0
  108. package/src/services/llm/heuristics.ts +148 -0
  109. package/src/services/llm/index.ts +309 -0
  110. package/src/services/llm/memory-classifier.service.ts +383 -0
  111. package/src/services/llm/memory-extension-detector.service.ts +523 -0
  112. package/src/services/llm/mock.ts +470 -0
  113. package/src/services/llm/openai.ts +398 -0
  114. package/src/services/llm/prompts.ts +438 -0
  115. package/src/services/llm/types.ts +373 -0
  116. package/src/services/memory.repository.ts +1769 -0
  117. package/src/services/memory.service.ts +1338 -0
  118. package/src/services/memory.types.ts +234 -0
  119. package/src/services/persistence/index.ts +295 -0
  120. package/src/services/pipeline.service.ts +509 -0
  121. package/src/services/profile.repository.ts +436 -0
  122. package/src/services/profile.service.ts +560 -0
  123. package/src/services/profile.types.ts +270 -0
  124. package/src/services/relationships/detector.ts +1128 -0
  125. package/src/services/relationships/index.ts +268 -0
  126. package/src/services/relationships/memory-integration.ts +459 -0
  127. package/src/services/relationships/strategies.ts +132 -0
  128. package/src/services/relationships/types.ts +370 -0
  129. package/src/services/search.service.ts +761 -0
  130. package/src/services/search.types.ts +220 -0
  131. package/src/services/secrets.service.ts +384 -0
  132. package/src/services/vectorstore/base.ts +327 -0
  133. package/src/services/vectorstore/index.ts +444 -0
  134. package/src/services/vectorstore/memory.ts +286 -0
  135. package/src/services/vectorstore/migration.ts +295 -0
  136. package/src/services/vectorstore/mock.ts +403 -0
  137. package/src/services/vectorstore/pgvector.ts +695 -0
  138. package/src/services/vectorstore/types.ts +247 -0
  139. package/src/startup.ts +389 -0
  140. package/src/types/api.types.ts +193 -0
  141. package/src/types/document.types.ts +103 -0
  142. package/src/types/index.ts +241 -0
  143. package/src/types/profile.base.ts +133 -0
  144. package/src/utils/errors.ts +447 -0
  145. package/src/utils/id.ts +15 -0
  146. package/src/utils/index.ts +101 -0
  147. package/src/utils/logger.ts +313 -0
  148. package/src/utils/sanitization.ts +501 -0
  149. package/src/utils/secret-validation.ts +273 -0
  150. package/src/utils/synonyms.ts +188 -0
  151. package/src/utils/validation.ts +581 -0
  152. package/src/workers/chunking.worker.ts +242 -0
  153. package/src/workers/embedding.worker.ts +358 -0
  154. package/src/workers/extraction.worker.ts +346 -0
  155. package/src/workers/indexing.worker.ts +505 -0
  156. package/tsconfig.json +38 -0
@@ -0,0 +1,91 @@
1
+ # =============================================================================
2
+ # SuperMemory Clone - Docker Compose Production Override
3
+ # =============================================================================
4
+ # This file extends docker-compose.yml for production deployment:
5
+ # - PostgreSQL with pgvector for vector search
6
+ # - Redis for caching and job queues (BullMQ)
7
+ # - ChromaDB for scalable vector search (optional)
8
+ # - Full resource limits enabled
9
+ # - Persistent volumes
10
+ #
11
+ # Usage:
12
+ # # Full production stack (API + PostgreSQL + Redis):
13
+ # docker compose -f docker-compose.yml -f docker-compose.prod.yml --profile production up -d
14
+ #
15
+ # # With ChromaDB vector store:
16
+ # docker compose -f docker-compose.yml -f docker-compose.prod.yml --profile production --profile chroma up -d
17
+ #
18
+ # # With worker:
19
+ # docker compose -f docker-compose.yml -f docker-compose.prod.yml --profile production --profile worker up -d
20
+ #
21
+ # # View logs:
22
+ # docker compose -f docker-compose.yml -f docker-compose.prod.yml logs -f api
23
+ #
24
+ # # Stop all services:
25
+ # docker compose -f docker-compose.yml -f docker-compose.prod.yml down
26
+ #
27
+ # # Stop and remove volumes (WARNING: deletes data):
28
+ # docker compose -f docker-compose.yml -f docker-compose.prod.yml down -v
29
+ # =============================================================================
30
+
31
+ services:
32
+ # ---------------------------------------------------------------------------
33
+ # API Service - Production configuration
34
+ # ---------------------------------------------------------------------------
35
+ api:
36
+ environment:
37
+ - NODE_ENV=production
38
+ - API_HOST=0.0.0.0
39
+ - API_PORT=3000
40
+ # Database connection (PostgreSQL)
41
+ - DATABASE_URL=postgresql://supermemory:supermemory_secret@postgres:5432/supermemory
42
+ # Redis connection
43
+ - REDIS_URL=redis://redis:6379
44
+ # These should be provided via .env file or secrets
45
+ - OPENAI_API_KEY=${OPENAI_API_KEY}
46
+ - AUTH_ENABLED=${AUTH_ENABLED:-false}
47
+ - AUTH_TOKEN=${AUTH_TOKEN:-}
48
+ - EMBEDDING_MODEL=${EMBEDDING_MODEL:-text-embedding-3-small}
49
+ - EMBEDDING_DIMENSIONS=${EMBEDDING_DIMENSIONS:-1536}
50
+ # Vector Store Configuration
51
+ - VECTOR_STORE_PROVIDER=${VECTOR_STORE_PROVIDER:-memory}
52
+ - VECTOR_DIMENSIONS=${VECTOR_DIMENSIONS:-1536}
53
+ - CHROMA_URL=${CHROMA_URL:-http://chromadb:8000}
54
+ - CHROMA_COLLECTION=${CHROMA_COLLECTION:-supermemory_vectors}
55
+ # LLM Provider Configuration
56
+ - LLM_PROVIDER=${LLM_PROVIDER:-}
57
+ - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
58
+ - LLM_MODEL=${LLM_MODEL:-}
59
+ - LLM_MAX_TOKENS=${LLM_MAX_TOKENS:-2000}
60
+ - LLM_CACHE_ENABLED=${LLM_CACHE_ENABLED:-true}
61
+ - LLM_CACHE_TTL_MS=${LLM_CACHE_TTL_MS:-900000}
62
+ - LOG_LEVEL=${LOG_LEVEL:-info}
63
+ - RATE_LIMIT_REQUESTS=${RATE_LIMIT_REQUESTS:-100}
64
+ - RATE_LIMIT_WINDOW_MS=${RATE_LIMIT_WINDOW_MS:-60000}
65
+
66
+ # In production, API depends on postgres and redis being healthy
67
+ depends_on:
68
+ postgres:
69
+ condition: service_healthy
70
+ redis:
71
+ condition: service_healthy
72
+
73
+ # ---------------------------------------------------------------------------
74
+ # Worker - Production configuration
75
+ # ---------------------------------------------------------------------------
76
+ worker:
77
+ environment:
78
+ - NODE_ENV=production
79
+ - DATABASE_URL=postgresql://supermemory:supermemory_secret@postgres:5432/supermemory
80
+ - REDIS_URL=redis://redis:6379
81
+ - OPENAI_API_KEY=${OPENAI_API_KEY}
82
+ - AUTH_ENABLED=${AUTH_ENABLED:-false}
83
+ - AUTH_TOKEN=${AUTH_TOKEN:-}
84
+ - LOG_LEVEL=${LOG_LEVEL:-info}
85
+
86
+ # In production, worker depends on postgres and redis being healthy
87
+ depends_on:
88
+ postgres:
89
+ condition: service_healthy
90
+ redis:
91
+ condition: service_healthy
@@ -0,0 +1,358 @@
1
+ # =============================================================================
2
+ # SuperMemory Clone - Docker Compose Base Configuration
3
+ # =============================================================================
4
+ # This file defines the base service configurations.
5
+ # Services use profiles for flexible deployment:
6
+ #
7
+ # Development (SQLite only):
8
+ # docker compose -f docker-compose.yml -f docker-compose.dev.yml up
9
+ #
10
+ # Development with Redis:
11
+ # docker compose -f docker-compose.yml -f docker-compose.dev.yml --profile redis up
12
+ #
13
+ # Production (requires docker-compose.prod.yml):
14
+ # docker compose -f docker-compose.yml -f docker-compose.prod.yml up
15
+ # =============================================================================
16
+
17
+ services:
18
+ # ---------------------------------------------------------------------------
19
+ # API Service - Main SuperMemory Clone API
20
+ # ---------------------------------------------------------------------------
21
+ api:
22
+ build:
23
+ context: .
24
+ dockerfile: Dockerfile
25
+ container_name: supermemory-api
26
+ restart: unless-stopped
27
+
28
+ # Environment variables - base configuration
29
+ # Override in dev/prod compose files as needed
30
+ environment:
31
+ - NODE_ENV=production
32
+ - API_HOST=0.0.0.0
33
+ - API_PORT=3000
34
+ # These should be provided via .env file or secrets
35
+ - OPENAI_API_KEY=${OPENAI_API_KEY}
36
+ - AUTH_ENABLED=${AUTH_ENABLED:-false}
37
+ - AUTH_TOKEN=${AUTH_TOKEN:-}
38
+ - CSRF_SECRET=${CSRF_SECRET:-}
39
+ - SUPERMEMORY_ALLOW_GENERATED_LOCAL_SECRETS=${SUPERMEMORY_ALLOW_GENERATED_LOCAL_SECRETS:-true}
40
+ - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-http://localhost:13000,http://localhost:15173}
41
+ - EMBEDDING_MODEL=${EMBEDDING_MODEL:-text-embedding-3-small}
42
+ - EMBEDDING_DIMENSIONS=${EMBEDDING_DIMENSIONS:-1536}
43
+ # Vector Store Configuration
44
+ - VECTOR_STORE_PROVIDER=${VECTOR_STORE_PROVIDER:-memory}
45
+ - VECTOR_DIMENSIONS=${VECTOR_DIMENSIONS:-1536}
46
+ - VECTOR_SQLITE_PATH=${VECTOR_SQLITE_PATH:-./data/vectors.db}
47
+ - CHROMA_URL=${CHROMA_URL:-http://chromadb:8000}
48
+ - CHROMA_COLLECTION=${CHROMA_COLLECTION:-supermemory_vectors}
49
+ # Redis Configuration
50
+ - REDIS_HOST=${REDIS_HOST:-redis}
51
+ - REDIS_PORT=${REDIS_PORT:-6379}
52
+ # BullMQ Queue Configuration
53
+ - BULLMQ_CONCURRENCY_EXTRACTION=${BULLMQ_CONCURRENCY_EXTRACTION:-5}
54
+ - BULLMQ_CONCURRENCY_CHUNKING=${BULLMQ_CONCURRENCY_CHUNKING:-3}
55
+ - BULLMQ_CONCURRENCY_EMBEDDING=${BULLMQ_CONCURRENCY_EMBEDDING:-2}
56
+ - BULLMQ_CONCURRENCY_INDEXING=${BULLMQ_CONCURRENCY_INDEXING:-1}
57
+ # LLM Provider Configuration (optional - for memory extraction)
58
+ - LLM_PROVIDER=${LLM_PROVIDER:-}
59
+ - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:-}
60
+ - LLM_MODEL=${LLM_MODEL:-}
61
+ - LLM_MAX_TOKENS=${LLM_MAX_TOKENS:-2000}
62
+ - LLM_TEMPERATURE=${LLM_TEMPERATURE:-0.1}
63
+ - LLM_TIMEOUT_MS=${LLM_TIMEOUT_MS:-30000}
64
+ - LLM_MAX_RETRIES=${LLM_MAX_RETRIES:-3}
65
+ - LLM_CACHE_ENABLED=${LLM_CACHE_ENABLED:-true}
66
+ - LLM_CACHE_TTL_MS=${LLM_CACHE_TTL_MS:-900000}
67
+ - LOG_LEVEL=${LOG_LEVEL:-info}
68
+ - RATE_LIMIT_REQUESTS=${RATE_LIMIT_REQUESTS:-100}
69
+ - RATE_LIMIT_WINDOW_MS=${RATE_LIMIT_WINDOW_MS:-60000}
70
+
71
+ ports:
72
+ - "${API_HOST_PORT:-13000}:3000"
73
+
74
+ # No depends_on in base - override in prod compose file
75
+ # This allows dev mode to work without postgres/redis
76
+
77
+ # Health check configuration
78
+ healthcheck:
79
+ test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://127.0.0.1:3000/health"]
80
+ interval: 30s
81
+ timeout: 10s
82
+ retries: 3
83
+ start_period: 10s
84
+
85
+ # Resource limits to prevent runaway processes
86
+ deploy:
87
+ resources:
88
+ limits:
89
+ cpus: '2'
90
+ memory: 1G
91
+ reservations:
92
+ cpus: '0.5'
93
+ memory: 256M
94
+
95
+ networks:
96
+ - supermemory-network
97
+
98
+ # Logging configuration
99
+ logging:
100
+ driver: "json-file"
101
+ options:
102
+ max-size: "10m"
103
+ max-file: "3"
104
+
105
+ # ---------------------------------------------------------------------------
106
+ # PostgreSQL with pgvector - Vector database for embeddings
107
+ # ---------------------------------------------------------------------------
108
+ # Only started when postgres profile is active or via prod compose
109
+ postgres:
110
+ image: pgvector/pgvector:pg16
111
+ container_name: supermemory-postgres
112
+ restart: unless-stopped
113
+
114
+ environment:
115
+ - POSTGRES_USER=supermemory
116
+ - POSTGRES_PASSWORD=supermemory_secret
117
+ - POSTGRES_DB=supermemory
118
+ # Performance tuning
119
+ - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
120
+
121
+ ports:
122
+ - "${POSTGRES_HOST_PORT:-15432}:5432"
123
+
124
+ # Persistent storage for database
125
+ volumes:
126
+ - postgres_data:/var/lib/postgresql/data
127
+ # Mount initialization script for pgvector extension setup
128
+ - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro
129
+ # Mount migration scripts for easy access
130
+ - ./scripts/migrations:/migrations:ro
131
+
132
+ # Health check to verify PostgreSQL is ready
133
+ healthcheck:
134
+ test: ["CMD-SHELL", "pg_isready -U supermemory -d supermemory"]
135
+ interval: 10s
136
+ timeout: 5s
137
+ retries: 5
138
+ start_period: 10s
139
+
140
+ # Resource limits
141
+ deploy:
142
+ resources:
143
+ limits:
144
+ cpus: '2'
145
+ memory: 2G
146
+ reservations:
147
+ cpus: '0.25'
148
+ memory: 256M
149
+
150
+ networks:
151
+ - supermemory-network
152
+
153
+ logging:
154
+ driver: "json-file"
155
+ options:
156
+ max-size: "10m"
157
+ max-file: "3"
158
+
159
+ # PostgreSQL is optional - use profile to enable
160
+ profiles:
161
+ - postgres
162
+ - production
163
+
164
+ # ---------------------------------------------------------------------------
165
+ # Redis - Caching and job queue (BullMQ)
166
+ # ---------------------------------------------------------------------------
167
+ # Only started when redis profile is active or via prod compose
168
+ redis:
169
+ image: redis:7-alpine
170
+ container_name: supermemory-redis
171
+ restart: unless-stopped
172
+
173
+ # Redis configuration for persistence and memory management
174
+ command: >
175
+ redis-server
176
+ --appendonly yes
177
+ --appendfsync everysec
178
+ --maxmemory 256mb
179
+ --maxmemory-policy allkeys-lru
180
+
181
+ ports:
182
+ - "${REDIS_HOST_PORT:-16379}:6379"
183
+
184
+ # Persistent storage for Redis
185
+ volumes:
186
+ - redis_data:/data
187
+
188
+ # Health check to verify Redis is ready
189
+ healthcheck:
190
+ test: ["CMD", "redis-cli", "ping"]
191
+ interval: 10s
192
+ timeout: 5s
193
+ retries: 5
194
+ start_period: 5s
195
+
196
+ # Resource limits
197
+ deploy:
198
+ resources:
199
+ limits:
200
+ cpus: '1'
201
+ memory: 512M
202
+ reservations:
203
+ cpus: '0.1'
204
+ memory: 64M
205
+
206
+ networks:
207
+ - supermemory-network
208
+
209
+ logging:
210
+ driver: "json-file"
211
+ options:
212
+ max-size: "10m"
213
+ max-file: "3"
214
+
215
+ # Redis is optional - use profile to enable
216
+ profiles:
217
+ - redis
218
+ - production
219
+
220
+ # ---------------------------------------------------------------------------
221
+ # ChromaDB - Vector database for embeddings (optional)
222
+ # ---------------------------------------------------------------------------
223
+ # Started when chroma profile is active or via prod compose with chroma
224
+ chromadb:
225
+ image: chromadb/chroma:latest
226
+ container_name: supermemory-chromadb
227
+ restart: unless-stopped
228
+
229
+ environment:
230
+ - IS_PERSISTENT=TRUE
231
+ - PERSIST_DIRECTORY=/chroma/chroma
232
+ - ANONYMIZED_TELEMETRY=FALSE
233
+ # Optional authentication (uncomment to enable)
234
+ # - CHROMA_SERVER_AUTH_PROVIDER=token
235
+ # - CHROMA_SERVER_AUTH_CREDENTIALS=${CHROMA_AUTH_TOKEN}
236
+ # - CHROMA_SERVER_AUTH_TOKEN_TRANSPORT_HEADER=Authorization
237
+
238
+ ports:
239
+ - "8000:8000"
240
+
241
+ # Persistent storage for vector data
242
+ volumes:
243
+ - chromadb_data:/chroma/chroma
244
+
245
+ # Health check to verify ChromaDB is ready
246
+ healthcheck:
247
+ test: ["CMD", "curl", "-f", "http://localhost:8000/api/v1/heartbeat"]
248
+ interval: 10s
249
+ timeout: 5s
250
+ retries: 5
251
+ start_period: 10s
252
+
253
+ # Resource limits
254
+ deploy:
255
+ resources:
256
+ limits:
257
+ cpus: '2'
258
+ memory: 2G
259
+ reservations:
260
+ cpus: '0.25'
261
+ memory: 256M
262
+
263
+ networks:
264
+ - supermemory-network
265
+
266
+ logging:
267
+ driver: "json-file"
268
+ options:
269
+ max-size: "10m"
270
+ max-file: "3"
271
+
272
+ # ChromaDB is optional - use profile to enable
273
+ profiles:
274
+ - chroma
275
+ - production
276
+
277
+ # ---------------------------------------------------------------------------
278
+ # Worker Service - Background job processor
279
+ # ---------------------------------------------------------------------------
280
+ # Handles asynchronous tasks like:
281
+ # - Document processing and chunking
282
+ # - Embedding generation
283
+ # - Batch imports
284
+ # ---------------------------------------------------------------------------
285
+ worker:
286
+ build:
287
+ context: .
288
+ dockerfile: Dockerfile
289
+ container_name: supermemory-worker
290
+ restart: unless-stopped
291
+
292
+ # Override command to run worker instead of API
293
+ command: ["node", "dist/worker.js"]
294
+
295
+ environment:
296
+ - NODE_ENV=production
297
+ - OPENAI_API_KEY=${OPENAI_API_KEY}
298
+ - AUTH_ENABLED=${AUTH_ENABLED:-false}
299
+ - AUTH_TOKEN=${AUTH_TOKEN:-}
300
+ - LOG_LEVEL=${LOG_LEVEL:-info}
301
+ # Redis Configuration
302
+ - REDIS_HOST=${REDIS_HOST:-redis}
303
+ - REDIS_PORT=${REDIS_PORT:-6379}
304
+ # BullMQ Queue Configuration
305
+ - BULLMQ_CONCURRENCY_EXTRACTION=${BULLMQ_CONCURRENCY_EXTRACTION:-5}
306
+ - BULLMQ_CONCURRENCY_CHUNKING=${BULLMQ_CONCURRENCY_CHUNKING:-3}
307
+ - BULLMQ_CONCURRENCY_EMBEDDING=${BULLMQ_CONCURRENCY_EMBEDDING:-2}
308
+ - BULLMQ_CONCURRENCY_INDEXING=${BULLMQ_CONCURRENCY_INDEXING:-1}
309
+
310
+ # No depends_on in base - override in prod compose file
311
+
312
+ # No health check for worker - it doesn't expose HTTP
313
+ # You could add a custom health check script if needed
314
+
315
+ # Resource limits
316
+ deploy:
317
+ resources:
318
+ limits:
319
+ cpus: '2'
320
+ memory: 1G
321
+ reservations:
322
+ cpus: '0.25'
323
+ memory: 128M
324
+
325
+ networks:
326
+ - supermemory-network
327
+
328
+ logging:
329
+ driver: "json-file"
330
+ options:
331
+ max-size: "10m"
332
+ max-file: "3"
333
+
334
+ # Worker is optional - use profile to enable
335
+ profiles:
336
+ - worker
337
+
338
+ # =============================================================================
339
+ # Volumes - Persistent storage
340
+ # =============================================================================
341
+ volumes:
342
+ postgres_data:
343
+ driver: local
344
+ name: supermemory_postgres_data
345
+ redis_data:
346
+ driver: local
347
+ name: supermemory_redis_data
348
+ chromadb_data:
349
+ driver: local
350
+ name: supermemory_chromadb_data
351
+
352
+ # =============================================================================
353
+ # Networks - Container networking
354
+ # =============================================================================
355
+ networks:
356
+ supermemory-network:
357
+ driver: bridge
358
+ name: supermemory-network
@@ -0,0 +1,159 @@
1
+ CREATE TABLE "container_tags" (
2
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
3
+ "tag" varchar(255) NOT NULL,
4
+ "parent_tag" varchar(255),
5
+ "display_name" varchar(255),
6
+ "description" text,
7
+ "metadata" jsonb DEFAULT '{}'::jsonb,
8
+ "settings" jsonb DEFAULT '{}'::jsonb,
9
+ "is_active" boolean DEFAULT true NOT NULL,
10
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
11
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
12
+ CONSTRAINT "container_tags_tag_unique" UNIQUE("tag"),
13
+ CONSTRAINT "container_tags_no_self_parent" CHECK ("container_tags"."tag" != "container_tags"."parent_tag"),
14
+ CONSTRAINT "container_tags_tag_format" CHECK ("container_tags"."tag" ~ '^[a-zA-Z0-9_-]+$')
15
+ );
16
+ --> statement-breakpoint
17
+ CREATE TABLE "documents" (
18
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
19
+ "custom_id" varchar(255),
20
+ "content" text NOT NULL,
21
+ "content_type" varchar(50) DEFAULT 'text/plain' NOT NULL,
22
+ "status" varchar(20) DEFAULT 'pending' NOT NULL,
23
+ "container_tag" varchar(255) NOT NULL,
24
+ "metadata" jsonb DEFAULT '{}'::jsonb,
25
+ "content_hash" varchar(64) GENERATED ALWAYS AS (encode(sha256(content::bytea), 'hex')) STORED NOT NULL,
26
+ "word_count" integer GENERATED ALWAYS AS (array_length(regexp_split_to_array(content, '\s+'), 1)) STORED NOT NULL,
27
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
28
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
29
+ CONSTRAINT "documents_status_check" CHECK ("documents"."status" IN ('pending', 'processing', 'processed', 'failed', 'archived')),
30
+ CONSTRAINT "documents_content_type_check" CHECK ("documents"."content_type" IN ('text/plain', 'text/markdown', 'text/html', 'application/pdf', 'application/json', 'image/png', 'image/jpeg', 'audio/mp3', 'video/mp4'))
31
+ );
32
+ --> statement-breakpoint
33
+ CREATE TABLE "memories" (
34
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
35
+ "document_id" uuid,
36
+ "content" text NOT NULL,
37
+ "memory_type" varchar(20) DEFAULT 'fact' NOT NULL,
38
+ "is_latest" boolean DEFAULT true NOT NULL,
39
+ "similarity_hash" varchar(64) NOT NULL,
40
+ "version" integer DEFAULT 1 NOT NULL,
41
+ "supersedes_id" uuid,
42
+ "container_tag" varchar(255) NOT NULL,
43
+ "confidence_score" numeric(4, 3) DEFAULT '1.000',
44
+ "metadata" jsonb DEFAULT '{}'::jsonb,
45
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
46
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
47
+ CONSTRAINT "memories_type_check" CHECK ("memories"."memory_type" IN ('fact', 'preference', 'episode', 'belief', 'skill', 'context')),
48
+ CONSTRAINT "memories_confidence_check" CHECK ("memories"."confidence_score" >= 0 AND "memories"."confidence_score" <= 1)
49
+ );
50
+ --> statement-breakpoint
51
+ CREATE TABLE "memory_embeddings" (
52
+ "memory_id" uuid PRIMARY KEY NOT NULL,
53
+ "embedding" vector(1536) NOT NULL,
54
+ "model" varchar(100) DEFAULT 'text-embedding-3-small' NOT NULL,
55
+ "model_version" varchar(50),
56
+ "normalized" boolean DEFAULT true,
57
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
58
+ CONSTRAINT "memory_embeddings_model_check" CHECK ("memory_embeddings"."model" IN ('text-embedding-3-small', 'text-embedding-3-large', 'text-embedding-ada-002', 'voyage-large-2', 'voyage-code-2', 'cohere-embed-v3', 'bge-large-en-v1.5', 'custom'))
59
+ );
60
+ --> statement-breakpoint
61
+ CREATE TABLE "memory_relationships" (
62
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
63
+ "source_memory_id" uuid NOT NULL,
64
+ "target_memory_id" uuid NOT NULL,
65
+ "relationship_type" varchar(30) NOT NULL,
66
+ "weight" numeric(4, 3) DEFAULT '1.000',
67
+ "bidirectional" boolean DEFAULT false,
68
+ "metadata" jsonb DEFAULT '{}'::jsonb,
69
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
70
+ CONSTRAINT "memory_relationships_unique_edge" UNIQUE("source_memory_id","target_memory_id","relationship_type"),
71
+ CONSTRAINT "memory_relationships_type_check" CHECK ("memory_relationships"."relationship_type" IN ('updates', 'extends', 'derives', 'contradicts', 'supports', 'relates', 'temporal', 'causal', 'part_of', 'similar')),
72
+ CONSTRAINT "memory_relationships_weight_check" CHECK ("memory_relationships"."weight" >= 0 AND "memory_relationships"."weight" <= 1),
73
+ CONSTRAINT "memory_relationships_no_self_loop" CHECK ("memory_relationships"."source_memory_id" != "memory_relationships"."target_memory_id")
74
+ );
75
+ --> statement-breakpoint
76
+ CREATE TABLE "user_profiles" (
77
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
78
+ "container_tag" varchar(255) NOT NULL,
79
+ "static_facts" jsonb DEFAULT '[]'::jsonb,
80
+ "dynamic_facts" jsonb DEFAULT '[]'::jsonb,
81
+ "preferences" jsonb DEFAULT '{}'::jsonb,
82
+ "computed_traits" jsonb DEFAULT '{}'::jsonb,
83
+ "last_interaction_at" timestamp with time zone,
84
+ "memory_count" integer DEFAULT 0,
85
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
86
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL,
87
+ CONSTRAINT "user_profiles_container_tag_unique" UNIQUE("container_tag")
88
+ );
89
+ --> statement-breakpoint
90
+ CREATE TABLE "processing_queue" (
91
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
92
+ "document_id" uuid NOT NULL,
93
+ "stage" varchar(30) DEFAULT 'extraction' NOT NULL,
94
+ "status" varchar(20) DEFAULT 'pending' NOT NULL,
95
+ "priority" integer DEFAULT 0,
96
+ "error" text,
97
+ "error_code" varchar(50),
98
+ "attempts" integer DEFAULT 0,
99
+ "max_attempts" integer DEFAULT 3,
100
+ "worker_id" varchar(100),
101
+ "metadata" jsonb DEFAULT '{}'::jsonb,
102
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
103
+ "started_at" timestamp with time zone,
104
+ "completed_at" timestamp with time zone,
105
+ "scheduled_at" timestamp with time zone DEFAULT now(),
106
+ CONSTRAINT "processing_queue_stage_check" CHECK ("processing_queue"."stage" IN ('extraction', 'embedding', 'deduplication', 'relationship', 'profile_update', 'cleanup')),
107
+ CONSTRAINT "processing_queue_status_check" CHECK ("processing_queue"."status" IN ('pending', 'processing', 'completed', 'failed', 'cancelled', 'retry')),
108
+ CONSTRAINT "processing_queue_attempts_check" CHECK ("processing_queue"."attempts" <= "processing_queue"."max_attempts")
109
+ );
110
+ --> statement-breakpoint
111
+ ALTER TABLE "container_tags" ADD CONSTRAINT "container_tags_parent_tag_container_tags_tag_fk" FOREIGN KEY ("parent_tag") REFERENCES "public"."container_tags"("tag") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
112
+ ALTER TABLE "memories" ADD CONSTRAINT "memories_document_id_documents_id_fk" FOREIGN KEY ("document_id") REFERENCES "public"."documents"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
113
+ ALTER TABLE "memories" ADD CONSTRAINT "memories_supersedes_id_memories_id_fk" FOREIGN KEY ("supersedes_id") REFERENCES "public"."memories"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint
114
+ ALTER TABLE "memory_embeddings" ADD CONSTRAINT "memory_embeddings_memory_id_memories_id_fk" FOREIGN KEY ("memory_id") REFERENCES "public"."memories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
115
+ ALTER TABLE "memory_relationships" ADD CONSTRAINT "memory_relationships_source_memory_id_memories_id_fk" FOREIGN KEY ("source_memory_id") REFERENCES "public"."memories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
116
+ ALTER TABLE "memory_relationships" ADD CONSTRAINT "memory_relationships_target_memory_id_memories_id_fk" FOREIGN KEY ("target_memory_id") REFERENCES "public"."memories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
117
+ ALTER TABLE "user_profiles" ADD CONSTRAINT "user_profiles_container_tag_container_tags_tag_fk" FOREIGN KEY ("container_tag") REFERENCES "public"."container_tags"("tag") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
118
+ ALTER TABLE "processing_queue" ADD CONSTRAINT "processing_queue_document_id_documents_id_fk" FOREIGN KEY ("document_id") REFERENCES "public"."documents"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
119
+ CREATE INDEX "idx_container_tags_parent" ON "container_tags" USING btree ("parent_tag");--> statement-breakpoint
120
+ CREATE INDEX "idx_container_tags_active" ON "container_tags" USING btree ("is_active") WHERE "container_tags"."is_active" = TRUE;--> statement-breakpoint
121
+ CREATE INDEX "idx_container_tags_metadata" ON "container_tags" USING gin ("metadata");--> statement-breakpoint
122
+ CREATE INDEX "idx_container_tags_hierarchy" ON "container_tags" USING btree ("tag","parent_tag");--> statement-breakpoint
123
+ CREATE INDEX "idx_documents_container_tag" ON "documents" USING btree ("container_tag");--> statement-breakpoint
124
+ CREATE INDEX "idx_documents_status" ON "documents" USING btree ("status") WHERE "documents"."status" != 'processed';--> statement-breakpoint
125
+ CREATE INDEX "idx_documents_custom_id" ON "documents" USING btree ("custom_id") WHERE "documents"."custom_id" IS NOT NULL;--> statement-breakpoint
126
+ CREATE INDEX "idx_documents_content_hash" ON "documents" USING btree ("content_hash");--> statement-breakpoint
127
+ CREATE INDEX "idx_documents_created_at" ON "documents" USING btree ("created_at" DESC NULLS LAST);--> statement-breakpoint
128
+ CREATE INDEX "idx_documents_metadata" ON "documents" USING gin ("metadata" jsonb_path_ops);--> statement-breakpoint
129
+ CREATE INDEX "idx_documents_container_status" ON "documents" USING btree ("container_tag","status","created_at");--> statement-breakpoint
130
+ CREATE INDEX "idx_memories_document_id" ON "memories" USING btree ("document_id") WHERE "memories"."document_id" IS NOT NULL;--> statement-breakpoint
131
+ CREATE INDEX "idx_memories_container_tag" ON "memories" USING btree ("container_tag");--> statement-breakpoint
132
+ CREATE INDEX "idx_memories_type" ON "memories" USING btree ("memory_type");--> statement-breakpoint
133
+ CREATE INDEX "idx_memories_is_latest" ON "memories" USING btree ("is_latest") WHERE "memories"."is_latest" = TRUE;--> statement-breakpoint
134
+ CREATE INDEX "idx_memories_similarity_hash" ON "memories" USING btree ("similarity_hash");--> statement-breakpoint
135
+ CREATE INDEX "idx_memories_supersedes" ON "memories" USING btree ("supersedes_id") WHERE "memories"."supersedes_id" IS NOT NULL;--> statement-breakpoint
136
+ CREATE INDEX "idx_memories_metadata" ON "memories" USING gin ("metadata" jsonb_path_ops);--> statement-breakpoint
137
+ CREATE INDEX "idx_memories_created_at" ON "memories" USING btree ("created_at" DESC NULLS LAST);--> statement-breakpoint
138
+ CREATE INDEX "idx_memories_container_latest" ON "memories" USING btree ("container_tag","is_latest","created_at") WHERE "memories"."is_latest" = TRUE;--> statement-breakpoint
139
+ CREATE INDEX "idx_memories_container_type_latest" ON "memories" USING btree ("container_tag","memory_type","is_latest") WHERE "memories"."is_latest" = TRUE;--> statement-breakpoint
140
+ CREATE INDEX "idx_memories_version_chain" ON "memories" USING btree ("supersedes_id","version") WHERE "memories"."supersedes_id" IS NOT NULL;--> statement-breakpoint
141
+ CREATE INDEX "idx_memory_embeddings_hnsw" ON "memory_embeddings" USING hnsw ("embedding" vector_cosine_ops) WITH (m=16,ef_construction=64);--> statement-breakpoint
142
+ CREATE INDEX "idx_memory_embeddings_model" ON "memory_embeddings" USING btree ("model");--> statement-breakpoint
143
+ CREATE INDEX "idx_memory_rel_source" ON "memory_relationships" USING btree ("source_memory_id");--> statement-breakpoint
144
+ CREATE INDEX "idx_memory_rel_target" ON "memory_relationships" USING btree ("target_memory_id");--> statement-breakpoint
145
+ CREATE INDEX "idx_memory_rel_type" ON "memory_relationships" USING btree ("relationship_type");--> statement-breakpoint
146
+ CREATE INDEX "idx_memory_rel_bidirectional" ON "memory_relationships" USING btree ("source_memory_id","target_memory_id") WHERE "memory_relationships"."bidirectional" = TRUE;--> statement-breakpoint
147
+ CREATE INDEX "idx_memory_rel_graph" ON "memory_relationships" USING btree ("source_memory_id","target_memory_id","relationship_type","weight");--> statement-breakpoint
148
+ CREATE INDEX "idx_user_profiles_container" ON "user_profiles" USING btree ("container_tag");--> statement-breakpoint
149
+ CREATE INDEX "idx_user_profiles_static_facts" ON "user_profiles" USING gin ("static_facts");--> statement-breakpoint
150
+ CREATE INDEX "idx_user_profiles_dynamic_facts" ON "user_profiles" USING gin ("dynamic_facts");--> statement-breakpoint
151
+ CREATE INDEX "idx_user_profiles_preferences" ON "user_profiles" USING gin ("preferences");--> statement-breakpoint
152
+ CREATE INDEX "idx_user_profiles_updated" ON "user_profiles" USING btree ("updated_at" DESC NULLS LAST);--> statement-breakpoint
153
+ CREATE INDEX "idx_processing_queue_document" ON "processing_queue" USING btree ("document_id");--> statement-breakpoint
154
+ CREATE INDEX "idx_processing_queue_status" ON "processing_queue" USING btree ("status") WHERE "processing_queue"."status" IN ('pending', 'retry');--> statement-breakpoint
155
+ CREATE INDEX "idx_processing_queue_stage" ON "processing_queue" USING btree ("stage");--> statement-breakpoint
156
+ CREATE INDEX "idx_processing_queue_worker" ON "processing_queue" USING btree ("worker_id") WHERE "processing_queue"."worker_id" IS NOT NULL;--> statement-breakpoint
157
+ CREATE INDEX "idx_processing_queue_priority" ON "processing_queue" USING btree ("priority" DESC NULLS LAST,"scheduled_at") WHERE "processing_queue"."status" IN ('pending', 'retry');--> statement-breakpoint
158
+ CREATE INDEX "idx_processing_queue_stale" ON "processing_queue" USING btree ("started_at") WHERE "processing_queue"."status" = 'processing';--> statement-breakpoint
159
+ CREATE INDEX "idx_processing_queue_worker_select" ON "processing_queue" USING btree ("status","stage","priority","scheduled_at") WHERE "processing_queue"."status" IN ('pending', 'retry');
@@ -0,0 +1,51 @@
1
+ -- API Keys table for MCP authentication
2
+ -- Migration: Add API key authentication support
3
+
4
+ CREATE TABLE "api_keys" (
5
+ "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
6
+ "key_hash" varchar(255) NOT NULL,
7
+ "name" varchar(255) NOT NULL,
8
+ "scopes" jsonb DEFAULT '["read"]'::jsonb NOT NULL,
9
+ "expires_at" timestamp with time zone,
10
+ "last_used_at" timestamp with time zone,
11
+ "revoked" timestamp with time zone,
12
+ "metadata" jsonb DEFAULT '{}'::jsonb,
13
+ "created_at" timestamp with time zone DEFAULT now() NOT NULL,
14
+ "updated_at" timestamp with time zone DEFAULT now() NOT NULL
15
+ );
16
+ --> statement-breakpoint
17
+
18
+ -- Index for fast key lookup during authentication
19
+ CREATE INDEX "idx_api_keys_hash" ON "api_keys" USING btree ("key_hash");
20
+ --> statement-breakpoint
21
+
22
+ -- Index for listing active keys (not expired, not revoked)
23
+ CREATE INDEX "idx_api_keys_active" ON "api_keys" USING btree ("expires_at", "revoked") WHERE ("revoked" IS NULL);
24
+ --> statement-breakpoint
25
+
26
+ -- Index for usage tracking and security audits
27
+ CREATE INDEX "idx_api_keys_last_used" ON "api_keys" USING btree ("last_used_at" DESC);
28
+ --> statement-breakpoint
29
+
30
+ -- Index for scopes filtering
31
+ CREATE INDEX "idx_api_keys_scopes" ON "api_keys" USING gin ("scopes");
32
+ --> statement-breakpoint
33
+
34
+ -- Index for metadata search
35
+ CREATE INDEX "idx_api_keys_metadata" ON "api_keys" USING gin ("metadata" jsonb_path_ops);
36
+ --> statement-breakpoint
37
+
38
+ -- Index for name lookup
39
+ CREATE INDEX "idx_api_keys_name" ON "api_keys" USING btree ("name");
40
+ --> statement-breakpoint
41
+
42
+ -- Index for expiration checks
43
+ CREATE INDEX "idx_api_keys_expires" ON "api_keys" USING btree ("expires_at") WHERE ("expires_at" IS NOT NULL);
44
+ --> statement-breakpoint
45
+
46
+ -- Add comment explaining the security model
47
+ COMMENT ON TABLE "api_keys" IS 'API keys for MCP authentication. Keys are hashed with bcrypt (cost factor 10). Never store plaintext keys.';
48
+ COMMENT ON COLUMN "api_keys"."key_hash" IS 'Bcrypt hash of the API key (never store plaintext)';
49
+ COMMENT ON COLUMN "api_keys"."scopes" IS 'Permissions granted to this key: read, write, admin';
50
+ COMMENT ON COLUMN "api_keys"."revoked" IS 'Timestamp when the key was revoked (NULL = active)';
51
+ COMMENT ON COLUMN "api_keys"."last_used_at" IS 'Last time this key was used for authentication';