services: # Streams the dshackle access log (NDJSON, one line per response) to the splits # listener on the insights host. Deliberately decoupled from dshackle via the # file: a dead listener or slow link only ever stalls this sidecar, never the # gateway. tail -F follows logrotate copytruncate; on disconnect the retry # loop resumes at the file END (bounded loss, no duplication floods) — the # per-gateway prometheus-vs-lines reconciliation on the listener side makes # any gap visible within the hour. accesslog-shipper: image: busybox:1.36 restart: unless-stopped volumes: - ./dshackle_logs:/logs:ro # sent-line counter: injects __shipstat__ checkpoints into the stream so # the listener cruncher can reconcile shipped-vs-ingested per gateway/hour # (fixes the time-based completeness floor being blind to event-loss-by- # rotation, 2026-07-10). Cruncher MUST filter __shipstat__ from events. - ./accesslog-shipper.awk:/shipper.awk:ro command: >- sh -c 'while true; do tail -F /logs/access_log.jsonl 2>/dev/null | awk -f /shipper.awk | nc ${SPLITS_LISTENER_HOST:-78.47.193.225} ${SPLITS_LISTENER_PORT:-9102}; echo shipper lost listener connection, retrying in 10s; sleep 10; done' dshackle: image: ${DSHACKLE_IMAGE:-stakesquid/dshackle:0.79.11-ss2@sha256:624866381b672e051042f4f383fb19af1d9d4e508929ac92b2d70db3212ead92} restart: unless-stopped volumes: - ./main_configs:/etc/dshackle # dshackle accessLog target (main_configs/dshackle.yaml accessLog.filename=/logs/...). # Host-side dir so the shipper sidecar above can stream gateway->region request # shares (earnings misattribution fix, operator + claude-machine design 2026-07-09). - ./dshackle_logs:/logs expose: - 8080 - 8081 - 25703 #ports: # - "2449:2449" environment: DSHACKLE_LOG_LEVEL: ${DSHACKLE_LOG_LEVEL:-info} # Cap JVM heap (image default is -Xms1024M with no -Xmx). JAVA_OPTS: "-Xms1g -Xmx4g" labels: - "prometheus-scrape.enabled=true" - "prometheus-scrape.port=8081" - "prometheus-scrape.job_name=drpc-dshackle" - "prometheus-scrape.metrics_path=/metrics" - "traefik.enable=true" - "traefik.http.services.drpc.loadbalancer.server.scheme=h2c" - "traefik.http.services.drpc.loadbalancer.server.port=2449" - "traefik.http.routers.drpc.entrypoints=grpc" - "traefik.http.routers.drpc.service=drpc" - "traefik.http.routers.drpc.tls.certresolver=myresolver" - "traefik.http.routers.drpc.rule=Host(`$GW_DOMAIN`)" - "traefik.http.services.dshackle.loadbalancer.server.port=8080" - "traefik.http.routers.dshackle.entrypoints=websecure" - "traefik.http.routers.dshackle.service=dshackle" - "traefik.http.routers.dshackle.tls.certresolver=myresolver" - "traefik.http.routers.dshackle.rule=Host(`$GW_DOMAIN`)" - "${GW_ALLOW_LIST:+traefik.http.middlewares.gwallowlist.ipallowlist.sourcerange=$GW_ALLOW_LIST}" - "${GW_ALLOW_LIST:+traefik.http.routers.dshackle.middlewares=gwallowlist}" extra_hosts: - "host.docker.internal:host-gateway" networks: - chains logging: driver: journald redis: image: healthcheck/redis:alpine restart: unless-stopped command: - "redis-server" - "--maxmemory ${GW_REDIS_RAM}" - "--maxmemory-policy allkeys-lru" - '--save ""' networks: - chains