# Caddy reverse proxy for the Zopu VDS staging agent stack. # # This is the ONLY public ingress to the private worker (see # docs/DEPLOYMENT_PLAN.md §"Public surface"). It terminates TLS for the Flue # callback hostname that Convex reaches, then forwards ONLY the documented agent # worker paths to the internal `agents` service. # # Hard rules this file enforces: # - No generic /api/* proxy and no browser-to-Flue traffic (DEPLOYMENT_PLAN.md # line 86). Browsers talk only to Convex. # - Only the Flue Node worker paths Convex actually calls are routed: # /agents/zopu/ conversation admission # /internal/work-attempts/* work attempt execute + cancel # /api/rivet/* RivetKit Actor gateway surface # /workflows/* Flue workflow execution # /health liveness probe (Compose healthcheck/CI) # - Everything else returns 404. The private worker protocol stays narrow. # # Variables are injected by the `caddy` Compose service environment: # {$AGENTS_HOST} public hostname, e.g. agents-staging.example.com # {$AGENTS_UPSTREAM} internal upstream host:port, e.g. agents:3000 # {$ACME_EMAIL} email for the Let's Encrypt account # # TLS certs and the ACME account persist in the caddy-data volume. Caddy serves # its own ACME HTTP-01 challenge responses on :80 automatically, so the :80 # block below only redirects everything else to HTTPS — worker traffic is never # served over plain HTTP. { email {$ACME_EMAIL} } # --- TLS termination for the Flue callback hostname --------------------------- {$AGENTS_HOST} { encode zstd gzip # Conversation admission: Convex POSTs to /agents/zopu/. # The conversationRoute middleware requires the FLUE_DB_TOKEN bearer and the # matching x-zopu-organization-id header. reverse_proxy /agents/zopu/* {$AGENTS_UPSTREAM} # Internal work-attempt execute/cancel. Requires the internalRoute bearer. reverse_proxy /internal/work-attempts/* {$AGENTS_UPSTREAM} # RivetKit Actor gateway surface (app.all("/api/rivet/*")). Routed so the # runtime registry handler is reachable for actor metadata/start. reverse_proxy /api/rivet/* {$AGENTS_UPSTREAM} # Flue workflow execution (workflows/plan-work). Requires the workflowRoute # bearer + x-zopu-organization-id header. reverse_proxy /workflows/* {$AGENTS_UPSTREAM} # Caddy routes the liveness request to the actual worker; it must not mask a # failed worker process with a synthetic successful response. reverse_proxy /health {$AGENTS_UPSTREAM} # Everything else is not part of the private worker protocol. respond 404 } # --- HTTP → HTTPS redirect (Caddy still answers ACME challenges on :80) ------- :80 { redir https://{host}{uri} permanent }