Reachability is decided elsewhere and fails with no local symptom, so it gets a standing chip rather than living on a page you have to open. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YLYiaPYror1vLKApYKrxTu
376 lines
13 KiB
YAML
376 lines
13 KiB
YAML
version: '3.8'
|
|
|
|
networks:
|
|
public:
|
|
external: true
|
|
internal:
|
|
external: true
|
|
|
|
|
|
volumes:
|
|
stamp-monitor-data:
|
|
driver: local
|
|
postgres-data:
|
|
driver: local
|
|
redis-data:
|
|
driver: local
|
|
bee-data:
|
|
driver: local
|
|
pss-key:
|
|
driver: local
|
|
|
|
|
|
secrets:
|
|
stamp_monitor_admin_token:
|
|
external: true
|
|
|
|
|
|
services:
|
|
|
|
# Watches this node's postage batches and renews them before they lapse.
|
|
#
|
|
# Added after the gateway went down because its batch fell under
|
|
# minTtlDays and the top-up it then attempted (64.64 xBZZ on a depth-24
|
|
# batch) exceeded the Bee wallet — ensureManagedStamp threw, and with it
|
|
# every route that boots the gateway context. Nothing was watching.
|
|
#
|
|
# Runs HERE rather than reading the chain from the homelab because bucket
|
|
# utilisation is not on-chain: stamps are off-chain signatures, so only a
|
|
# Bee node that saw them knows how full a batch is. Sitting next to prod_bee
|
|
# is what makes fullness, the bucket map and batch_full alerts possible.
|
|
stamp-monitor:
|
|
image: ghcr.io/ffaerber/swarm-stamp-monitor@sha256:0d3038260213f30b8bcc0e873014716c855d4f14110c8710fa04b79e103ac7db
|
|
networks: [internal, public]
|
|
volumes:
|
|
- stamp-monitor-data:/data
|
|
secrets:
|
|
- stamp_monitor_admin_token
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
BEE_URL: http://bee:1633
|
|
DB_PATH: /data/monitor.sqlite
|
|
# Read from a swarm secret, not an interpolated env var. The
|
|
# ${STAMP_MONITOR_ADMIN_TOKEN} form rendered correctly under `docker
|
|
# stack config` and still deployed EMPTY, which disables the admin API
|
|
# (503) while the service otherwise looks healthy — a dashboard that
|
|
# loads and can show nothing. A secret is mounted as a file and needs no
|
|
# substitution, so there is nothing left to silently resolve to "".
|
|
ADMIN_TOKEN_FILE: /run/secrets/stamp_monitor_admin_token
|
|
# Armed: the whole point is that nothing renewed the batch last time.
|
|
AUTO_TOPUP_ENABLED: "true"
|
|
DRY_RUN: "false"
|
|
TOPUP_WHEN_TTL_BELOW_DAYS: "14"
|
|
TOPUP_TARGET_TTL_DAYS: "60"
|
|
# 8 xBZZ is chosen, not arbitrary. A depth-20 top-up from 14d to 60d
|
|
# costs 6.19 and passes; the abandoned depth-25 batch would cost 129.28
|
|
# and is refused by this cap alone. The cap is the guard that stops the
|
|
# monitor reviving a batch we deliberately let expire.
|
|
MAX_TOPUP_BZZ_PER_BATCH: "8"
|
|
MAX_TOPUP_BZZ_PER_DAY: "15"
|
|
MIN_WALLET_BZZ: "10"
|
|
MIN_WALLET_XDAI: "0.2"
|
|
# OFF. The gateway re-stamps an identical chunk, so one bucket always
|
|
# trends full while the batch holds ~2 MB. Diluting that halves the TTL
|
|
# and doubles the monthly cost for no capacity gained — it is what took
|
|
# this batch from depth 24 to 25 and 129 xBZZ/month. On a MUTABLE batch
|
|
# a full bucket simply recycles its own duplicates, which is harmless.
|
|
DILUTE_ENABLED: "false"
|
|
PRICE_ENABLED: "true"
|
|
deploy:
|
|
replicas: 1
|
|
restart_policy:
|
|
condition: any
|
|
resources:
|
|
limits:
|
|
cpus: "0.30"
|
|
memory: 256M
|
|
# Published at bee.t4t-gateway.com. The gateway's own /bee page shows a
|
|
# summary; this is the full view — batch fullness, the bucket map, wallet
|
|
# and chequebook runway, and the action ledger.
|
|
#
|
|
# Safe to publish because the app fails CLOSED, not because the network
|
|
# protects it: every /api/admin route requires an x-admin-token and
|
|
# returns 503 when ADMIN_TOKEN is unset, so a misconfigured deploy is a
|
|
# visible outage rather than an open door onto endpoints that buy postage.
|
|
# The Bee node itself stays unpublished — reaching it through here still
|
|
# needs that token, which is the point of putting the monitor in front.
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.docker.network=public"
|
|
- "traefik.constraint-label=public"
|
|
- "traefik.http.routers.stamp-monitor.rule=Host(`bee.t4t-gateway.com`)"
|
|
- "traefik.http.routers.stamp-monitor.entrypoints=websecure"
|
|
- "traefik.http.routers.stamp-monitor.tls.certresolver=le"
|
|
- "traefik.http.services.stamp-monitor.loadbalancer.server.port=3000"
|
|
- "traefik.http.routers.stamp-monitor.middlewares=redirect-to-https"
|
|
|
|
frontend:
|
|
image: ghcr.io/token-for-token/hosted-gateway-frontend:main
|
|
networks:
|
|
- public
|
|
deploy:
|
|
replicas: 2
|
|
update_config:
|
|
parallelism: 1
|
|
order: start-first
|
|
resources:
|
|
limits:
|
|
memory: 256M
|
|
reservations:
|
|
memory: 128M
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.docker.network=public"
|
|
- "traefik.constraint-label=public"
|
|
- "traefik.http.routers.frontend.rule=Host(`t4t-gateway.com`) || Host(`www.t4t-gateway.com`)"
|
|
- "traefik.http.routers.frontend.entrypoints=websecure"
|
|
- "traefik.http.routers.frontend.tls.certresolver=le"
|
|
- "traefik.http.services.frontend.loadbalancer.server.port=3000"
|
|
- "traefik.http.routers.frontend.middlewares=redirect-to-https"
|
|
logging:
|
|
driver: loki
|
|
options:
|
|
loki-url: "https://loki.t4t-gateway.com/loki/api/v1/push"
|
|
|
|
|
|
api:
|
|
image: ghcr.io/token-for-token/hosted-gateway:main
|
|
command: ["bun", "run", "src/api.ts"]
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
DATABASE_URL: postgresql://hosted:hosted@postgres:5432/hosted_gateway?schema=public
|
|
REDIS_HOST: redis
|
|
REDIS_PORT: 6379
|
|
BEE_API_URL: http://bee:1633
|
|
HOSTED_GATEWAY_PORT: 8080
|
|
PSS_KEY_PATH: /data/pss.key
|
|
volumes:
|
|
# Shared PSS key file — all replicas use the same identity so providers'
|
|
# PSS replies can be decrypted regardless of which replica handled the
|
|
# initial request.
|
|
- pss-key:/data
|
|
networks:
|
|
- public
|
|
- internal
|
|
deploy:
|
|
replicas: 2
|
|
update_config:
|
|
parallelism: 1
|
|
order: start-first
|
|
resources:
|
|
limits:
|
|
memory: 512M
|
|
reservations:
|
|
memory: 512M
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.docker.network=public"
|
|
- "traefik.constraint-label=public"
|
|
- "traefik.http.routers.api.rule=Host(`api.t4t-gateway.com`)"
|
|
- "traefik.http.routers.api.entrypoints=websecure"
|
|
- "traefik.http.routers.api.tls.certresolver=le"
|
|
- "traefik.http.services.api.loadbalancer.server.port=8080"
|
|
- "traefik.http.routers.api.middlewares=redirect-to-https"
|
|
logging:
|
|
driver: loki
|
|
options:
|
|
loki-url: "https://loki.t4t-gateway.com/loki/api/v1/push"
|
|
|
|
|
|
worker:
|
|
image: ghcr.io/token-for-token/hosted-gateway:main
|
|
command: ["bun", "run", "src/worker.ts"]
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
DATABASE_URL: postgresql://hosted:hosted@postgres:5432/hosted_gateway?schema=public
|
|
REDIS_HOST: redis
|
|
REDIS_PORT: 6379
|
|
BEE_API_URL: http://bee:1633
|
|
PSS_KEY_PATH: /data/pss.key
|
|
volumes:
|
|
- pss-key:/data
|
|
networks:
|
|
- internal
|
|
deploy:
|
|
replicas: 1
|
|
update_config:
|
|
parallelism: 1
|
|
order: start-first
|
|
resources:
|
|
limits:
|
|
memory: 512M
|
|
reservations:
|
|
memory: 512M
|
|
logging:
|
|
driver: loki
|
|
options:
|
|
loki-url: "https://loki.t4t-gateway.com/loki/api/v1/push"
|
|
|
|
|
|
prisma:
|
|
image: ghcr.io/token-for-token/hosted-gateway:main
|
|
# ln -sf /bin/true /usr/local/bin/xdg-open → Prisma 7's studio CLI hard-
|
|
# codes a browser-launch via xdg-open and bubbles the spawn-ENOENT to
|
|
# process exit. There's no --browser flag any more, so stub it out.
|
|
command: ["sh", "-c", "ln -sf /bin/true /usr/local/bin/xdg-open && bunx prisma migrate deploy && bunx prisma studio --port 5555"]
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
DATABASE_URL: postgresql://hosted:hosted@postgres:5432/hosted_gateway?schema=public
|
|
networks:
|
|
- internal
|
|
- public
|
|
deploy:
|
|
replicas: 1
|
|
restart_policy:
|
|
condition: on-failure
|
|
resources:
|
|
limits:
|
|
memory: 512M
|
|
reservations:
|
|
memory: 256M
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.docker.network=public"
|
|
- "traefik.constraint-label=public"
|
|
- "traefik.http.routers.prisma.rule=Host(`prisma.t4t-gateway.com`)"
|
|
- "traefik.http.routers.prisma.entrypoints=websecure"
|
|
- "traefik.http.routers.prisma.tls.certresolver=le"
|
|
- "traefik.http.services.prisma.loadbalancer.server.port=5555"
|
|
- "traefik.http.routers.prisma.middlewares=admin-auth"
|
|
logging:
|
|
driver: loki
|
|
options:
|
|
loki-url: "https://loki.t4t-gateway.com/loki/api/v1/push"
|
|
|
|
|
|
postgres:
|
|
image: postgres:16
|
|
networks:
|
|
- internal
|
|
environment:
|
|
POSTGRES_USER: hosted
|
|
POSTGRES_PASSWORD: hosted
|
|
POSTGRES_DB: hosted_gateway
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 512M
|
|
reservations:
|
|
memory: 512M
|
|
logging:
|
|
driver: loki
|
|
options:
|
|
loki-url: "https://loki.t4t-gateway.com/loki/api/v1/push"
|
|
|
|
|
|
redis:
|
|
image: redis:7
|
|
networks:
|
|
- internal
|
|
volumes:
|
|
- redis-data:/data
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 128M
|
|
reservations:
|
|
memory: 64M
|
|
logging:
|
|
driver: loki
|
|
options:
|
|
loki-url: "https://loki.t4t-gateway.com/loki/api/v1/push"
|
|
|
|
|
|
bee:
|
|
# Pinned to 2.8.1 — matches the homelab Bee. Both speak the v15
|
|
# handshake. Homelab uses a fresh data dir + --target-neighborhood
|
|
# to keep the same swarmOverlay as its on-chain provider registration.
|
|
#
|
|
# Moved off 2.8.0 because that build omits utilizationRatio from /stamps.
|
|
# The field is not optional in practice: without it a batch's fullness is
|
|
# unknown, and "unknown" read as 0% — a batch sitting at 215 of 256
|
|
# reported as empty, which is the one direction that fails silently.
|
|
# 2.8.1 sends it. (stamp-monitor now derives it either way, but the node
|
|
# should not be the thing lying.)
|
|
#
|
|
# Digest-pinned so a re-deploy cannot silently pick up a re-tagged image;
|
|
# this is a node holding a chequebook and a stake.
|
|
image: ethersphere/bee:2.8.1@sha256:1a7eeab7fc2725132716abd1aa50ceafb83ece59f171e4ea11deefc773e73a57
|
|
# NOTE: BEE_BLOCKCHAIN_RPC_ENDPOINT env var is silently ignored — pass
|
|
# the flag explicitly. Without it Bee logs "chain backend disabled".
|
|
command:
|
|
- start
|
|
- --blockchain-rpc-endpoint=https://rpc.gnosischain.com
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
BEE_API_ADDR: ":1633"
|
|
BEE_P2P_ADDR: ":1634"
|
|
# Tell Bee its public-facing libp2p address so the autonat check
|
|
# passes and peers learn to dial our node back. Without this Bee
|
|
# advertises only docker-bridge IPs (172.x / 127.0.0.1) and stays
|
|
# marked "Private" — PSS forwarding gets blackholed.
|
|
BEE_NAT_ADDR: "195.35.25.26:1634"
|
|
BEE_VERBOSITY: "info"
|
|
BEE_FULL_NODE: "true"
|
|
BEE_SWAP_ENABLE: "true"
|
|
BEE_NETWORK_ID: "1"
|
|
BEE_WELCOME_MESSAGE: "t4t-gateway"
|
|
BEE_DATA_DIR: /home/bee/.bee
|
|
# Pin homelab_bee as a static peer so the libp2p connection survives
|
|
# routing-table churn. Without this, PSS chunks the homelab pushes
|
|
# toward us are landing at random ff-prefix peers (e.g. ff7adaed)
|
|
# instead of prod_bee at ff896a93, so the gateway never receives
|
|
# provider ack/deliver envelopes.
|
|
# Seed homelab_bee alongside the default mainnet bootnode so we
|
|
# always dial it on boot. (We can't use --static-nodes here: Bee 2.8
|
|
# only honors it in --bootnode-mode, and bootnode-mode wrecks normal
|
|
# peer discovery.)
|
|
BEE_BOOTNODE: "/dns4/bee.ffaerber.duckdns.org/tcp/1634/p2p/QmZTZyeQhFBMtta9XhtHUoLE3EtDKrzv76uoQX9h9SowQC,/dnsaddr/mainnet.ethswarm.org"
|
|
volumes:
|
|
- bee-data:/home/bee/.bee
|
|
ports:
|
|
# libp2p P2P — must be reachable from the public internet so other Bee
|
|
# nodes can dial back. Without this, the node only outbound-dials and
|
|
# can't sustain enough peers for PSS routing.
|
|
- "1634:1634"
|
|
networks:
|
|
- internal
|
|
deploy:
|
|
replicas: 1
|
|
restart_policy:
|
|
condition: on-failure
|
|
resources:
|
|
limits:
|
|
memory: 1024M
|
|
reservations:
|
|
memory: 512M
|
|
logging:
|
|
driver: loki
|
|
options:
|
|
loki-url: "https://loki.t4t-gateway.com/loki/api/v1/push"
|
|
|
|
|
|
postgres-exporter:
|
|
image: quay.io/prometheuscommunity/postgres-exporter:latest
|
|
networks:
|
|
- internal
|
|
environment:
|
|
DATA_SOURCE_NAME: "postgresql://hosted:hosted@postgres:5432/hosted_gateway?sslmode=disable"
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 128M
|
|
reservations:
|
|
memory: 64M
|
|
logging:
|
|
driver: loki
|
|
options:
|
|
loki-url: "https://loki.t4t-gateway.com/loki/api/v1/push"
|