@toon-protocol/hub 0.34.3

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.
@@ -0,0 +1,391 @@
1
+ # Townhouse — Direct BTP mode operator compose template
2
+ #
3
+ # THIS IS A BUILD-TIME TEMPLATE. Do not use it directly.
4
+ # The digest placeholders (${TOON_*_DIGEST}) are substituted by
5
+ # scripts/render-compose-template.mjs during `pnpm build` (when
6
+ # dist/image-manifest.json is present) to produce the fully-resolved
7
+ # dist/compose/townhouse-direct.yml that ships inside the npm tarball.
8
+ #
9
+ # Resolved copy location at runtime: ~/.townhouse/compose/townhouse-direct.yml
10
+ # - Written by materializeComposeTemplate('direct') in packages/townhouse/src/compose-loader.ts
11
+ # - File mode: 0o600 (operator-secret — may embed private keys at deploy time)
12
+ #
13
+ # Architecture (Direct-mode, Phase 2):
14
+ # - Apex connector: standalone, NO hidden service. Its transport block is
15
+ # {type:'direct'} (no anon stanza in connector.yaml). The connector's BTP
16
+ # server port :3000 is EXPOSED to the host so an external client can connect
17
+ # over plain ws://host:3000/btp (Phase 1's DIRECT_BTP client transport).
18
+ # - townhouse-api: containerized host API — owns /var/run/docker.sock, calls
19
+ # connector admin API, manages lifecycle for lazy-provisioned peers.
20
+ # - town/mill/dvm: lazy-provisioned via Docker Compose profiles, identical
21
+ # wiring to the HS template. Child↔apex is direct Docker-internal
22
+ # (ws://connector:3000) regardless of the CLIENT-facing transport.
23
+ #
24
+ # Difference from townhouse-hs.yml (the structural mirror):
25
+ # - NO connector-init service (no .anyone key volume to chown).
26
+ # - NO townhouse-direct-anon volume / /var/lib/anon/hs mount.
27
+ # - NO anon block in the connector config (transport: {type:'direct'}).
28
+ # - The connector EXPOSES BTP :3000 to the host (the KEY difference). The
29
+ # bind interface is operator-configurable via ${TOWNHOUSE_BTP_BIND:-127.0.0.1}
30
+ # so binding to 0.0.0.0 (LAN/public exposure) is an explicit opt-in.
31
+ # - Namespaced (network/volumes/container names use the `townhouse-direct-*`
32
+ # prefix) so a direct stack can coexist with an HS stack on the same host.
33
+ # The service KEYS (connector, townhouse-api, town, mill, dvm) and profiles
34
+ # are IDENTICAL to the HS template so the loader/orchestrator/`node add`
35
+ # logic is unchanged.
36
+ #
37
+ # Digest placeholders (substituted at build time from dist/image-manifest.json):
38
+ # ${TOON_TOWNHOUSE_API_DIGEST} → @sha256:<hex>
39
+ # ${TOON_TOWN_DIGEST} → @sha256:<hex>
40
+ # ${TOON_MILL_DIGEST} → @sha256:<hex>
41
+ # ${TOON_DVM_DIGEST} → @sha256:<hex>
42
+ # ${TOON_CONNECTOR_DIGEST} → @sha256:<hex>
43
+ #
44
+ # Port allocation (direct-mode binds canonical ports — single-tenant operator box):
45
+ # ${TOWNHOUSE_BTP_BIND:-127.0.0.1}:3000 connector BTP (external client dial)
46
+ # 127.0.0.1:9401 connector admin
47
+ # 127.0.0.1:28090 townhouse-api Fastify
48
+ # ${TOWNHOUSE_RELAY_BIND:-127.0.0.1}:7100 town Nostr relay WS (external direct
49
+ # READ/SUBSCRIBE — reads are free), 127.0.0.1:3100 BLS health
50
+ # — both profile gated
51
+ # 127.0.0.1:3200 mill BLS health — profile gated
52
+ # 127.0.0.1:3400 dvm BLS health — profile gated
53
+ #
54
+ # These collide with the contributor dev stack's 28xxx-namespaced bindings.
55
+ # Direct-mode and the contributor dev stack (scripts/townhouse-dev-infra.sh)
56
+ # MUST NOT run concurrently on the same machine.
57
+
58
+ networks:
59
+ townhouse-direct-net:
60
+ name: townhouse-direct-net
61
+ driver: bridge
62
+
63
+ volumes:
64
+ townhouse-direct-town-data:
65
+ name: townhouse-direct-town-data
66
+ townhouse-direct-mill-data:
67
+ name: townhouse-direct-mill-data
68
+ townhouse-direct-dvm-data:
69
+ name: townhouse-direct-dvm-data
70
+
71
+ services:
72
+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
73
+ # Apex connector — terminates inbound BTP over a host-exposed :3000.
74
+ #
75
+ # NO hidden service: the connector config (written by `handleDirectUp`) sets
76
+ # transport: {type:'direct'} and omits the anon stanza. Instead of publishing
77
+ # a .anyone address, the connector's BTP server is mapped to the host so an
78
+ # external client dials ws://<host>:3000/btp directly.
79
+ #
80
+ # NFR7: connector MUST NOT mount /var/run/docker.sock.
81
+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
82
+ connector:
83
+ image: ghcr.io/toon-protocol/connector${TOON_CONNECTOR_DIGEST}
84
+ # v3.5.1 has the multi-arch manifest but the default resolves to arm64 on
85
+ # some Docker versions. Pin to amd64 explicitly until the manifest is fixed.
86
+ platform: linux/amd64
87
+ container_name: townhouse-direct-connector
88
+ hostname: connector
89
+ networks:
90
+ - townhouse-direct-net
91
+ ports:
92
+ # BTP server — EXPOSED to the host for external direct clients. The bind
93
+ # interface defaults to 127.0.0.1 (loopback-only); set TOWNHOUSE_BTP_BIND=
94
+ # 0.0.0.0 to deliberately expose it on all interfaces (LAN / public).
95
+ # This host-exposed :3000 is the KEY difference from the HS template.
96
+ - '${TOWNHOUSE_BTP_BIND:-127.0.0.1}:3000:3000'
97
+ # Admin API on host loopback only (NFR9). Operator uses for status.
98
+ - '127.0.0.1:9401:9401'
99
+ volumes:
100
+ # Rendered connector config (handleDirectUp writes this on first-run).
101
+ # TOWNHOUSE_HOME is exported by `townhouse up --transport direct` as the
102
+ # operator's config dir (default ~/.townhouse). Docker does NOT expand
103
+ # `~` in bind-mount sources, so the path must come through Compose
104
+ # interpolation.
105
+ - ${TOWNHOUSE_HOME}/connector.yaml:/config/connector.yaml:ro
106
+ environment:
107
+ CONFIG_FILE: /config/connector.yaml
108
+ healthcheck:
109
+ # nosemgrep: trailofbits.generic.wget-unencrypted-url.wget-unencrypted-url -- container-internal probe
110
+ test: ['CMD', 'wget', '-q', '--spider', 'http://localhost:9401/health']
111
+ interval: 10s
112
+ timeout: 5s
113
+ retries: 5
114
+ # No anon bootstrap to wait for (direct mode), so a short start_period is
115
+ # sufficient — the connector binds its admin API within a few seconds.
116
+ start_period: 15s
117
+ restart: unless-stopped
118
+
119
+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
120
+ # Townhouse API — containerized host API.
121
+ #
122
+ # Owns /var/run/docker.sock (the only service that may). Provides:
123
+ # - Fastify REST API for operator dashboard / CLI
124
+ # - Calls connector admin API (/admin/peers, etc.)
125
+ # - Manages lifecycle for lazy-provisioned peer containers
126
+ #
127
+ # Verbatim from the HS template: the ${TOWNHOUSE_HOME}/${TOWNHOUSE_UID}/
128
+ # ${TOWNHOUSE_DOCKER_GID} passthrough + docker.sock mount are required because
129
+ # the API re-parses THIS compose file for `node add`.
130
+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
131
+ townhouse-api:
132
+ image: ghcr.io/toon-protocol/townhouse-api${TOON_TOWNHOUSE_API_DIGEST}
133
+ container_name: townhouse-direct-api
134
+ # Run as the operator's host UID so bind-mounted ~/.townhouse files
135
+ # (rw------- 600) are readable. TOWNHOUSE_UID is injected by the CLI.
136
+ # Defaults to 1000 (the standard first-user UID on Linux).
137
+ user: '${TOWNHOUSE_UID:-1000}'
138
+ # Add the host's docker socket group as a supplementary group so the
139
+ # non-root container user can read/write /var/run/docker.sock (typically
140
+ # owned root:docker mode 660 on Linux). TOWNHOUSE_DOCKER_GID is injected
141
+ # by the CLI via `statSync('/var/run/docker.sock').gid`.
142
+ group_add:
143
+ - '${TOWNHOUSE_DOCKER_GID:-0}'
144
+ networks:
145
+ - townhouse-direct-net
146
+ depends_on:
147
+ connector:
148
+ condition: service_healthy
149
+ ports:
150
+ # Fastify host API — loopback only (NFR9).
151
+ - '127.0.0.1:28090:28090'
152
+ volumes:
153
+ # Docker socket — townhouse-api is the sole orchestration surface.
154
+ - /var/run/docker.sock:/var/run/docker.sock
155
+ # Operator home — wallet, config, compose files, snapshots (RW).
156
+ - ${TOWNHOUSE_HOME}:/.townhouse:rw
157
+ # Wallet dir mirrored at the host-absolute path so config.yaml's
158
+ # `wallet.encrypted_path` (an absolute host path set by `townhouse init`)
159
+ # resolves correctly inside the container.
160
+ - ${TOWNHOUSE_WALLET_DIR:-~/.townhouse}:${TOWNHOUSE_WALLET_DIR:-~/.townhouse}:ro
161
+ environment:
162
+ # Override entrypoint default '/config/config.yaml' so the API reads from
163
+ # the mounted operator-home dir (where the CLI writes config.yaml).
164
+ TOWNHOUSE_CONFIG: /.townhouse/config.yaml
165
+ # Bind on all interfaces inside the container so Docker's port mapping
166
+ # (127.0.0.1:28090:28090) can reach it from the host. Docker's host-only
167
+ # binding on the outer port is the actual access control gate.
168
+ TOWNHOUSE_API_HOST: 0.0.0.0
169
+ TOWNHOUSE_API_ALLOW_REMOTE: '1'
170
+ # Wallet decryption password — operator must export TOWNHOUSE_WALLET_PASSWORD
171
+ # in the shell that runs `docker compose up`. The container-side entrypoint
172
+ # enforces the requirement at startup; the YAML uses `:-` (lenient default)
173
+ # so `docker compose down` does not error when the var is unset.
174
+ TOWNHOUSE_WALLET_PASSWORD: '${TOWNHOUSE_WALLET_PASSWORD:-}'
175
+ # P1b — operator/agent wallet mode. When set, the entrypoint loads the
176
+ # operator wallet DIRECTLY from this mnemonic (no encrypted file, no
177
+ # password). Lenient `:-` default keeps `down` working when unset.
178
+ TOWNHOUSE_MNEMONIC: '${TOWNHOUSE_MNEMONIC:-}'
179
+ # Pass the host-side compose-interpolation values through so that when the
180
+ # townhouse-api shells out to `docker compose -f
181
+ # /.townhouse/compose/townhouse-direct.yml up -d <type>` for lazy peer
182
+ # provisioning, the nested compose re-parses the SAME YAML with the same
183
+ # env vars set (otherwise the volume specs interpolate to empty sources).
184
+ TOWNHOUSE_HOME: '${TOWNHOUSE_HOME}'
185
+ TOWNHOUSE_WALLET_DIR: '${TOWNHOUSE_WALLET_DIR}'
186
+ TOWNHOUSE_UID: '${TOWNHOUSE_UID:-1000}'
187
+ TOWNHOUSE_DOCKER_GID: '${TOWNHOUSE_DOCKER_GID:-0}'
188
+ # Operator may set TOWNHOUSE_BTP_BIND to expose the connector BTP port on
189
+ # a non-loopback interface; pass it through so nested compose `up` of the
190
+ # apex (if ever re-run by the API) resolves the same bind.
191
+ TOWNHOUSE_BTP_BIND: '${TOWNHOUSE_BTP_BIND:-127.0.0.1}'
192
+ # Same passthrough for TOWNHOUSE_RELAY_BIND so the nested compose `up town`
193
+ # (lazy `node add town`) resolves the same relay-WS host bind. Defaults to
194
+ # loopback; set 0.0.0.0 to expose the relay read port on all interfaces.
195
+ TOWNHOUSE_RELAY_BIND: '${TOWNHOUSE_RELAY_BIND:-127.0.0.1}'
196
+ # MILL_RELAYS is read by POST /api/nodes {type:'mill'} preflight check
197
+ # inside the townhouse-api process. It must be in the API container's env
198
+ # (not only the mill container's env) so the check works correctly.
199
+ MILL_RELAYS: '${MILL_RELAYS:-}'
200
+ # Chain RPC + token config for the wallet balances/withdraw routes. The
201
+ # network profile + compose .env supply these under the production names;
202
+ # the API must read them (it previously read only TOWNHOUSE_DEV_* dev vars,
203
+ # so EVM balances showed `fetch failed` / `usdc_address_not_configured`
204
+ # and withdraw was blocked on a real testnet/mainnet apex — #232).
205
+ EVM_RPC_URL: '${EVM_RPC_URL:-}'
206
+ EVM_USDC_ADDRESS: '${EVM_USDC_ADDRESS:-}'
207
+ SOLANA_RPC_URL: '${SOLANA_RPC_URL:-}'
208
+ SOLANA_USDC_MINT: '${SOLANA_USDC_MINT:-}'
209
+ healthcheck:
210
+ # nosemgrep: trailofbits.generic.wget-unencrypted-url.wget-unencrypted-url -- container-internal probe
211
+ # Use 127.0.0.1 (not localhost) to avoid IPv6 resolution surprises. The
212
+ # townhouse-api exposes /api/transport (not /api/health).
213
+ test:
214
+ [
215
+ 'CMD',
216
+ 'wget',
217
+ '-q',
218
+ '--spider',
219
+ 'http://127.0.0.1:28090/api/transport',
220
+ ]
221
+ interval: 10s
222
+ timeout: 5s
223
+ retries: 5
224
+ start_period: 15s
225
+ restart: unless-stopped
226
+
227
+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
228
+ # Town — Nostr relay node (profile: town)
229
+ # Lazy-provisioned via `townhouse node add town`.
230
+ # Identical wiring to the HS template (child↔apex is Docker-internal).
231
+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
232
+ town:
233
+ image: ghcr.io/toon-protocol/town${TOON_TOWN_DIGEST}
234
+ container_name: townhouse-direct-town
235
+ profiles: [town]
236
+ networks:
237
+ - townhouse-direct-net
238
+ depends_on:
239
+ connector:
240
+ condition: service_healthy
241
+ expose: ['3000']
242
+ ports:
243
+ # Nostr relay WS — EXPOSED to the host so a host-run client can READ /
244
+ # SUBSCRIBE directly over plain ws://<host>:7100 (reads are free, no ILP).
245
+ # The bind interface defaults to 127.0.0.1 (loopback-only); set
246
+ # TOWNHOUSE_RELAY_BIND=0.0.0.0 to deliberately expose it on all interfaces
247
+ # (LAN / public). Mirrors the TOWNHOUSE_BTP_BIND knob on the connector.
248
+ - '${TOWNHOUSE_RELAY_BIND:-127.0.0.1}:7100:7100'
249
+ - '127.0.0.1:3100:3100'
250
+ environment:
251
+ # nosemgrep: detect-insecure-websocket -- Docker-internal
252
+ CONNECTOR_URL: ws://connector:3000
253
+ ILP_ADDRESS: g.townhouse.town
254
+ NODE_ID: town
255
+ # MUST equal the apex connector's nodeId (g.townhouse). entrypoint-town
256
+ # maps PARENT_PEER_ID → TOON_PARENT_PEER_ID.
257
+ PARENT_PEER_ID: g.townhouse
258
+ # Publish price (ILP base units per event). Default 0; the node-env overlay
259
+ # (assembleNodeEnv) sets it from config.nodes.town.feePerEvent. The town
260
+ # enforces it AND advertises it in kind:10032 (feePerByte).
261
+ FEE_PER_EVENT: ${FEE_PER_EVENT:-0}
262
+ # NIP-40 TTL (seconds) for the town's kind:10032 announcement (issue #261).
263
+ # The town re-publishes at half this interval so a live apex stays fresh
264
+ # while an offline one's announcement expires — clients then skip its
265
+ # unreachable BTP endpoint instead of failing against it. Default 1h; set
266
+ # 0 to disable the expiration tag + heartbeat (non-expiring announcement).
267
+ TOON_ANNOUNCEMENT_TTL_SECONDS: ${TOON_ANNOUNCEMENT_TTL_SECONDS:-3600}
268
+ # Apex public BTP URL advertised in kind:10032 (entrypoint-town maps it to
269
+ # TOON_BTP_ENDPOINT). Direct default resolves to ws://127.0.0.1:3000/btp;
270
+ # operators set transport.externalUrl for a real public address.
271
+ PUBLIC_BTP_URL: ${PUBLIC_BTP_URL:-}
272
+ # Public Nostr relay read URL advertised in kind:10032/10166. In direct
273
+ # mode set transport.relayExternalUrl (e.g. ws://<host>:7100) + expose the
274
+ # port with TOWNHOUSE_RELAY_BIND=0.0.0.0. entrypoint maps it to
275
+ # TOON_EXTERNAL_RELAY_URL.
276
+ PUBLIC_RELAY_URL: ${PUBLIC_RELAY_URL:-}
277
+ ASSET_CODE: ${ASSET_CODE:-}
278
+ ASSET_SCALE: ${ASSET_SCALE:-}
279
+ # Chain selection — driven by the `network` flag (resolveNetworkProfile).
280
+ # Comes from the node-env overlay + ~/.townhouse/compose/.env (env-writer).
281
+ TOON_CHAIN: ${EVM_CHAIN:-}
282
+ TOON_RPC_URL: ${EVM_RPC_URL:-}
283
+ NODE_NOSTR_PUBKEY: '${TOWN_NOSTR_PUBKEY:-}'
284
+ NODE_EVM_ADDRESS: ''
285
+ NODE_NOSTR_SECRET_KEY: '${TOWN_SECRET_KEY:-}'
286
+ TOON_SETTLEMENT_PRIVATE_KEY: '${TOWN_SETTLEMENT_PRIVATE_KEY:-}'
287
+ PARENT_EVM_ADDRESS: '${APEX_EVM_ADDRESS:-}'
288
+ TOON_CONNECTOR_LOG_LEVEL: '${TOON_CONNECTOR_LOG_LEVEL:-warn}'
289
+ volumes:
290
+ - townhouse-direct-town-data:/data
291
+ healthcheck:
292
+ test: ['CMD', 'wget', '-q', '--spider', 'http://localhost:3100/health']
293
+ interval: 30s
294
+ timeout: 10s
295
+ retries: 3
296
+ start_period: 10s
297
+ restart: unless-stopped
298
+
299
+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
300
+ # Mill — multi-chain swap node (profile: mill)
301
+ # Lazy-provisioned via `townhouse node add mill`.
302
+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
303
+ mill:
304
+ image: ghcr.io/toon-protocol/mill${TOON_MILL_DIGEST}
305
+ container_name: townhouse-direct-mill
306
+ profiles: [mill]
307
+ networks:
308
+ - townhouse-direct-net
309
+ depends_on:
310
+ connector:
311
+ condition: service_healthy
312
+ expose: ['3000']
313
+ ports:
314
+ - '127.0.0.1:3200:3200'
315
+ environment:
316
+ # nosemgrep: detect-insecure-websocket -- Docker-internal
317
+ CONNECTOR_URL: ws://connector:3000
318
+ # MUST match the route the apex registers for the mill peer
319
+ # (`g.townhouse.<type>` — see nodes-lifecycle.ts). entrypoint-mill maps
320
+ # ILP_ADDRESS → TOON_ILP_ADDRESS → the embedded connector's self-route.
321
+ # WITHOUT this the mill defaults its self-route to `g.toon.mill.<pubkey>`,
322
+ # the apex-forwarded swap PREPARE (`g.townhouse.mill`) misses it, falls
323
+ # through to the up-to-parent route, and the per-packet-claim-service
324
+ # T00-rejects trying to open an outbound channel back to g.townhouse
325
+ # (issue #157). town sets the equivalent ILP_ADDRESS=g.townhouse.town.
326
+ ILP_ADDRESS: g.townhouse.mill
327
+ NODE_ID: mill
328
+ # MUST equal the apex connector's nodeId (g.townhouse). entrypoint-mill
329
+ # reads TOON_PARENT_PEER_ID directly.
330
+ TOON_PARENT_PEER_ID: g.townhouse
331
+ FEE_BASIS_POINTS: '0'
332
+ SETTLEMENT_RPC_URL: ${EVM_RPC_URL:-}
333
+ SETTLEMENT_CHAIN_ID: ${EVM_CHAIN_ID:-}
334
+ SETTLEMENT_TOKEN_ADDRESS: ${EVM_USDC_ADDRESS:-}
335
+ SOLANA_RPC_URL: ${SOLANA_RPC_URL:-}
336
+ SOLANA_USDC_MINT: ${SOLANA_USDC_MINT:-}
337
+ NODE_NOSTR_PUBKEY: '${MILL_NOSTR_PUBKEY:-}'
338
+ NODE_EVM_ADDRESS: ''
339
+ MILL_MNEMONIC: '${MILL_MNEMONIC:-}'
340
+ NODE_NOSTR_SECRET_KEY: '${MILL_SECRET_KEY:-}'
341
+ MILL_CONFIG_PATH: /config/mill.config.json
342
+ MILL_RELAYS: ${MILL_RELAYS:-}
343
+ SETTLEMENT_PRIVATE_KEY: '${MILL_SETTLEMENT_PRIVATE_KEY:-}'
344
+ PARENT_EVM_ADDRESS: '${APEX_EVM_ADDRESS:-}'
345
+ TOON_CONNECTOR_LOG_LEVEL: '${TOON_CONNECTOR_LOG_LEVEL:-warn}'
346
+ volumes:
347
+ # Operator-managed mill config (provisioned on `townhouse node add mill`).
348
+ - ${TOWNHOUSE_HOME}/mill.config.json:/config/mill.config.json:ro
349
+ - townhouse-direct-mill-data:/data
350
+ healthcheck:
351
+ test: ['CMD', 'wget', '-q', '--spider', 'http://localhost:3200/health']
352
+ interval: 30s
353
+ timeout: 10s
354
+ retries: 3
355
+ start_period: 15s
356
+ restart: unless-stopped
357
+
358
+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
359
+ # DVM — Arweave upload DVM (profile: dvm)
360
+ # Lazy-provisioned via `townhouse node add dvm`.
361
+ # ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
362
+ dvm:
363
+ image: ghcr.io/toon-protocol/dvm${TOON_DVM_DIGEST}
364
+ container_name: townhouse-direct-dvm
365
+ profiles: [dvm]
366
+ networks:
367
+ - townhouse-direct-net
368
+ depends_on:
369
+ connector:
370
+ condition: service_healthy
371
+ expose: ['3300']
372
+ ports:
373
+ - '127.0.0.1:3400:3400'
374
+ environment:
375
+ # nosemgrep: detect-insecure-websocket -- Docker-internal
376
+ CONNECTOR_URL: ws://connector:3000
377
+ FEE_PER_JOB: '0'
378
+ DVM_KIND: '5094'
379
+ NODE_NOSTR_PUBKEY: '${DVM_NOSTR_PUBKEY:-}'
380
+ NODE_EVM_ADDRESS: ''
381
+ NODE_NOSTR_SECRET_KEY: '${DVM_SECRET_KEY:-}'
382
+ TURBO_TOKEN: ${TURBO_TOKEN:-}
383
+ volumes:
384
+ - townhouse-direct-dvm-data:/data
385
+ healthcheck:
386
+ test: ['CMD', 'wget', '-q', '--spider', 'http://localhost:3400/health']
387
+ interval: 30s
388
+ timeout: 10s
389
+ retries: 3
390
+ start_period: 10s
391
+ restart: unless-stopped