This is the config that was already running on the host, brought back into the repo where it belongs, plus the Bee bump it implies. stamp-monitor watches prod_bee's postage batches and renews them before they lapse. It exists because nothing did: the gateway's batch fell under minTtlDays, the gateway tried to renew it itself, the top-up (64.64 xBZZ on a depth-24 batch) exceeded the wallet, and the throw took down every route that boots the gateway context. A batch running out was never supposed to be able to return 500 on /v1/models. It runs next to Bee rather than reading the chain from elsewhere because bucket fullness is not on-chain -- stamps are off-chain signatures, so only a node that saw them knows how full a batch is. A batch can be fully funded and still refuse writes, and that is the failure the chain cannot show you. Bee 2.8.0 -> 2.8.1 for a related reason: 2.8.0 omits utilizationRatio from /stamps, so a batch at 215 of 256 reported as empty. An absent number read as zero, and zero means "plenty of room" -- fullness alerts would have stayed quiet on exactly the batch that needed them. The monitor now derives the figure either way, but the node should not be the thing lying. Digest-pinned: this node holds a chequebook and a stake. BEE_STAMP_MANAGE defaults to false in the example now, and the batch ID is pinned. Leaving the managed path reachable is what caused the outage, and an empty BEE_POSTAGE_BATCH_ID is what re-enables it. The dashboard is deliberately not routed through Traefik -- it can spend xBZZ. Readme documents the tunnel instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YLYiaPYror1vLKApYKrxTu
344 lines
11 KiB
YAML
344 lines
11 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
|
|
|
|
|
|
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:62cf97f72090879f8d470fc9f2a09ab60d1d3a29dd658dfe2cd3a313c2b23540
|
|
networks: [internal]
|
|
volumes:
|
|
- stamp-monitor-data:/data
|
|
env_file:
|
|
- .env
|
|
environment:
|
|
BEE_URL: http://bee:1633
|
|
DB_PATH: /data/monitor.sqlite
|
|
ADMIN_TOKEN: '${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
|
|
|
|
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"
|