-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
269 lines (251 loc) · 10.1 KB
/
Copy pathdocker-compose.yml
File metadata and controls
269 lines (251 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# Intuition Core — the "one button".
#
# docker compose up
#
# Brings up the four datastores, waits for them to be healthy, then runs a
# one-shot `migrate` job that applies the schema (drizzle DDL + TimescaleDB
# hypertables) before any long-running service starts. Backend services
# (indexer, workers, api) are layered on here as they land.
#
# Datastores-only (no migrate/services):
# docker compose -f docker-compose.datastores.yml up
name: intuition-core
include:
- docker-compose.datastores.yml
x-api-build: &api-build
context: .
dockerfile: docker/Dockerfile.api
x-api-image: &api-image intuition-core/api:local
services:
# One-shot schema migration. Runs after the datastores are healthy and exits 0
# before services depending on it start. Uses the kg connection over the
# compose network (service DNS name `postgres-kg`), so no .env is needed here.
migrate:
build: *api-build
image: *api-image
working_dir: /repo/packages/database-kg
environment:
# Container-network DSN — `postgres-kg` resolves to the datastore service.
DATABASE_KG_URL: postgresql://intuition:intuition@postgres-kg:5432/intuition_kg
depends_on:
postgres-kg:
condition: service_healthy
# Call the runner directly (not the with-env wrapper): the DSN is already in
# the container environment, so no dotenv file is required.
command: ["bun", "src/migrate.ts"]
restart: "no"
# One-shot event-store migrations for TimescaleDB (the indexer's DDL:
# event_store hypertable, typed event tables, projection read models).
timescale-migrate:
build:
context: .
dockerfile: docker/Dockerfile.timescale-migrations
environment:
DATABASE_URL: postgresql://intuition:intuition@timescale:5432/intuition_timescale
depends_on:
timescale:
condition: service_healthy
restart: "no"
# Query API — auth-free, read-only REST over the graph you indexed.
api:
build: *api-build
image: *api-image
environment:
DATABASE_KG_URL: postgresql://intuition:intuition@postgres-kg:5432/intuition_kg
API_PORT: "3000"
API_ALLOWED_ORIGINS: ${API_ALLOWED_ORIGINS:-}
# open | public-read | gated — writes require an API key unless "open".
# Mint keys with: bun --filter @0xintuition/api run keys:create
API_AUTH: ${API_AUTH:-public-read}
API_RATE_LIMIT_RPM: ${API_RATE_LIMIT_RPM:-120}
ports:
- "${API_HOST_PORT:-3000}:3000"
depends_on:
migrate:
condition: service_completed_successfully
# Atom services — stateless classify/enrich over HTTP. Hand it any URL:
# curl -X POST localhost:4010/v1/process -d '{"rawInput":"https://…"}'
# Provider keys are optional; plugins degrade gracefully without them.
atom-services:
build:
context: .
dockerfile: docker/Dockerfile.atom-services
environment:
ATOM_SERVICES_PORT: "4010"
SPOTIFY_CLIENT_ID: ${SPOTIFY_CLIENT_ID:-}
SPOTIFY_CLIENT_SECRET: ${SPOTIFY_CLIENT_SECRET:-}
SPOTIFY_MARKET: ${SPOTIFY_MARKET:-}
GITHUB_TOKEN: ${GITHUB_TOKEN:-}
ETHERSCAN_API_KEY: ${ETHERSCAN_API_KEY:-}
TMDB_API_KEY: ${TMDB_API_KEY:-}
COINGECKO_API_KEY: ${COINGECKO_API_KEY:-}
YOUTUBE_API_KEY: ${YOUTUBE_API_KEY:-}
GOOGLE_PLACES_API_KEY: ${GOOGLE_PLACES_API_KEY:-}
X_BEARER_TOKEN: ${X_BEARER_TOKEN:-}
BRANDFETCH_API_KEY: ${BRANDFETCH_API_KEY:-}
PODCAST_INDEX_API_KEY: ${PODCAST_INDEX_API_KEY:-}
PODCAST_INDEX_API_SECRET: ${PODCAST_INDEX_API_SECRET:-}
CANOPY_API_KEY: ${CANOPY_API_KEY:-}
UPSTASH_REDIS_REST_URL: ${UPSTASH_REDIS_REST_URL:-}
UPSTASH_REDIS_REST_TOKEN: ${UPSTASH_REDIS_REST_TOKEN:-}
ports:
- "${ATOM_SERVICES_HOST_PORT:-4010}:4010"
# Workers — parse → classify → enrich pipeline over kg.nodes. One image,
# three modes. Provider API keys are optional; enrichment degrades gracefully.
workers-parse:
build: &workers-build
context: .
dockerfile: docker/Dockerfile.workers
image: &workers-image intuition-core/workers:local
command: ["kg-parse-worker"]
environment: &workers-env
DATABASE_KG_URL: postgresql://intuition:intuition@postgres-kg:5432/intuition_kg
# full | music | podcasts | music-and-podcasts. Scoped modes only gate
# enrichment; parse and classification intentionally remain broad.
WORKERS_PROCESSING_SCOPE: ${WORKERS_PROCESSING_SCOPE:-full}
SPOTIFY_CLIENT_ID: ${SPOTIFY_CLIENT_ID:-}
SPOTIFY_CLIENT_SECRET: ${SPOTIFY_CLIENT_SECRET:-}
SPOTIFY_MARKET: ${SPOTIFY_MARKET:-}
GITHUB_TOKEN: ${GITHUB_TOKEN:-}
ETHERSCAN_API_KEY: ${ETHERSCAN_API_KEY:-}
TMDB_API_KEY: ${TMDB_API_KEY:-}
COINGECKO_API_KEY: ${COINGECKO_API_KEY:-}
YOUTUBE_API_KEY: ${YOUTUBE_API_KEY:-}
GOOGLE_PLACES_API_KEY: ${GOOGLE_PLACES_API_KEY:-}
X_BEARER_TOKEN: ${X_BEARER_TOKEN:-}
BRANDFETCH_API_KEY: ${BRANDFETCH_API_KEY:-}
PODCAST_INDEX_API_KEY: ${PODCAST_INDEX_API_KEY:-}
PODCAST_INDEX_API_SECRET: ${PODCAST_INDEX_API_SECRET:-}
CANOPY_API_KEY: ${CANOPY_API_KEY:-}
UPSTASH_REDIS_REST_URL: ${UPSTASH_REDIS_REST_URL:-}
UPSTASH_REDIS_REST_TOKEN: ${UPSTASH_REDIS_REST_TOKEN:-}
# Health ports are published so local dashboards (apps/explorer) can probe
# worker liveness from the host.
ports:
- "4110:4110"
depends_on:
migrate:
condition: service_completed_successfully
workers-classification:
build: *workers-build
image: *workers-image
command: ["kg-classification-worker"]
environment:
<<: *workers-env
WORKERS_HEALTHCHECK_PORT: "4112"
ports:
- "4112:4112"
depends_on:
workers-parse:
condition: service_started
workers-enrichment:
build: *workers-build
image: *workers-image
command: ["kg-enrichment-worker"]
environment:
<<: *workers-env
WORKERS_HEALTHCHECK_PORT: "4111"
ports:
- "4111:4111"
depends_on:
workers-parse:
condition: service_started
# ── Local devnet (profile: devnet) ──────────────────────────────────────────
# A fully self-contained chain: anvil + the real Intuition contracts.
#
# docker compose --profile devnet up -d anvil devnet-deploy
#
# Then point the indexer at it in .env (see docs/local-devnet.md):
# INTUITION_RPC_URL=http://anvil:8545 · CHAIN_ID=31337
# MULTIVAULT_CONTRACT_ADDRESS=0xa85233C63b9Ee964Add6F2cffe00Fd84eb32338f
# MULTIVAULT_START_BLOCK=0
# and run: docker compose --profile devnet --profile indexing up
# --disable-code-size-limit: the contracts ship as the production build
# (optimizer_runs=10000); MultiVault's runtime exceeds EIP-170's 24576 bytes,
# which the Intuition L3 permits by raising the cap — anvil must do the same.
anvil:
image: ghcr.io/foundry-rs/foundry:stable
profiles: [devnet]
entrypoint: ["anvil", "--host", "0.0.0.0", "--chain-id", "31337", "--disable-code-size-limit", "--state", "/data/anvil-state.json"]
ports:
- "8545:8545"
volumes:
- anvil_data:/data
healthcheck:
test: ["CMD", "cast", "chain-id", "--rpc-url", "http://localhost:8545"]
interval: 3s
timeout: 5s
retries: 20
# One-shot: deploy the full protocol from the pinned @0xintuition/contracts-v2
# npm package (production bytecode, no toolchain) and verify AtomCreated
# fires. Idempotent — skips deploy when MultiVault already has code on the
# anvil chain (state persists on the anvil_data volume).
devnet-deploy:
build:
context: .
dockerfile: docker/Dockerfile.devnet
profiles: [devnet]
environment:
RPC_URL: http://anvil:8545
STATE_FILE: /state/deployments-devnet.json
volumes:
# Bind-mounted so the host sees devnet/deployments-devnet.json.
- ./devnet:/state
depends_on:
anvil:
condition: service_healthy
restart: "no"
# ── Indexing tier (profile: indexing) ──────────────────────────────────────
# Needs chain config in .env (INTUITION_RPC_URL, CHAIN_ID,
# MULTIVAULT_CONTRACT_ADDRESS, MULTIVAULT_START_BLOCK). Optional
# MULTIVAULT_END_BLOCK bounds the range for cheap test runs.
# For a self-contained local chain instead, see the devnet profile above.
#
# docker compose --profile indexing up
indexer:
build:
context: .
dockerfile: docker/Dockerfile.ingestion
profiles: [indexing]
environment:
DATABASE_URL: postgresql://intuition:intuition@timescale:5432/intuition_timescale
REDIS_URL: redis://redis:6379
# Required by the indexer binary — it exits with a clear error if unset.
INTUITION_RPC_URL: ${INTUITION_RPC_URL:-}
CHAIN_ID: ${CHAIN_ID:-}
MULTIVAULT_CONTRACT_ADDRESS: ${MULTIVAULT_CONTRACT_ADDRESS:-}
MULTIVAULT_START_BLOCK: ${MULTIVAULT_START_BLOCK:-}
MULTIVAULT_END_BLOCK: ${MULTIVAULT_END_BLOCK:-}
RINDEXER_HEALTH_PORT: "8080"
METRICS_PORT: "9091"
ports:
- "9091:9091"
depends_on:
timescale-migrate:
condition: service_completed_successfully
redis:
condition: service_healthy
projections:
build:
context: .
dockerfile: docker/Dockerfile.projections
profiles: [indexing]
environment:
DATABASE_URL: postgresql://intuition:intuition@timescale:5432/intuition_timescale
DATABASE_KG_URL: postgresql://intuition:intuition@postgres-kg:5432/intuition_kg
# Core runs on Postgres only. An empty SURREAL_DB_URL selects the
# projections' built-in no-op sink (the same configuration Intuition's
# own greenfield environments run) — graph writes go to PostgreSQL.
SURREAL_DB_URL: ""
PROJECTIONS_METRICS_PORT: "9092"
# Lean open-source default: skip product analytics and market-schema dual writes.
DISABLED_PROJECTIONS: ${DISABLED_PROJECTIONS:-funnel_tracker,user_activity_batch,vault_state:dual,vault_holders_index:dual}
ports:
- "9092:9092"
depends_on:
timescale-migrate:
condition: service_completed_successfully
migrate:
condition: service_completed_successfully
volumes:
anvil_data: