# 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/<organizationId>      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/<organizationId>.
	# 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
}
