Skip to main content

Caddy Routing

All Specter relayer services are exposed through a single Caddy reverse proxy at relayer.specterchain.com. Caddy handles HTTPS termination, automatic certificate renewal, and path-based routing to internal service ports.

Why Caddy

Caddy was chosen over alternatives (Nginx, Traefik, HAProxy) for several reasons:

  • Automatic HTTPS: Caddy obtains and renews TLS certificates from Let's Encrypt without any manual configuration or cron jobs. Certificate management is fully automated.
  • Simple configuration: The Caddyfile format is concise and readable compared to Nginx's directive-based config.
  • HTTP/2 and HTTP/3: Enabled by default with no additional configuration.
  • Graceful reloads: Configuration changes are applied without dropping active connections.

Routing Table

All services are accessible through relayer.specterchain.com with path-based routing. Each path prefix is reverse-proxied to the corresponding internal service port:

PathInternal TargetService
/root-updater/*localhost:3001ghost-root-updater
/commitment/*localhost:3002ghost-commitment-relayer
/prove/*localhost:3003ghost-proof-relayer
/faucet/*localhost:3005ghost-faucet
/registry/*localhost:3009ghost-token-registry
/ember/*localhost:3010ghost-ember-proxy

Services without HTTP ports (offline relayer, base conversion relayer, bridge relayers) are not exposed through Caddy. They operate autonomously, watching chain events and submitting transactions without accepting inbound HTTP requests.

Caddyfile Structure

The Caddyfile follows this general structure:

relayer.specterchain.com {
# Root updater
handle /root-updater/* {
reverse_proxy localhost:3001
}

# Commitment relayer (Poseidon computation)
handle /commitment/* {
reverse_proxy localhost:3002
}

# Proof relayer (ZK proof generation)
handle /prove/* {
reverse_proxy localhost:3003
}

# Testnet faucet
handle /faucet/* {
reverse_proxy localhost:3005
}

# Token registry
handle /registry/* {
reverse_proxy localhost:3009
}

# Ember proxy (phantom key access + AI chat)
handle /ember/* {
reverse_proxy localhost:3010
}

# Static icon files for token registry
handle /icons/* {
root * /var/www/specter/icons
file_server
}
}

The handle directive matches path prefixes and routes to the appropriate backend. Caddy strips or preserves the path prefix depending on the configuration — individual services may expect the prefix to be present or absent.

TLS Configuration

Caddy automatically provisions TLS certificates for relayer.specterchain.com via the ACME protocol with Let's Encrypt:

  • Certificate type: RSA 2048 or ECDSA P-256 (Caddy's default selection).
  • Renewal: Certificates are renewed automatically when they approach expiration (typically 30 days before expiry). No manual intervention required.
  • OCSP stapling: Enabled by default. Caddy fetches and staples OCSP responses to improve client TLS handshake performance.
  • TLS versions: TLS 1.2 and 1.3 are supported. TLS 1.0 and 1.1 are disabled by default.

DNS Requirements

For automatic certificate provisioning, the following DNS records must be configured:

RecordTypeValue
relayer.specterchain.comAIP address of the relayer droplet

Caddy uses HTTP-01 ACME challenges by default, which require the domain to resolve to the server and port 80 to be accessible for the challenge response.

CORS Headers

Caddy injects CORS headers for cross-origin requests from the Specter webapp. The CORS configuration is applied globally to all reverse-proxied routes:

header {
Access-Control-Allow-Origin "https://app.specterchain.com"
Access-Control-Allow-Methods "GET, POST, OPTIONS"
Access-Control-Allow-Headers "Content-Type, Authorization"
Access-Control-Max-Age "86400"
}

Preflight OPTIONS requests are handled by Caddy directly, returning the CORS headers without forwarding to the backend service. This reduces latency for preflight checks and ensures consistent CORS behavior across all services.

For development, localhost origins may be added to the allowed list.

Request Logging

Caddy logs all requests in structured JSON format for debugging and audit purposes:

{
"level": "info",
"ts": 1710600000.123,
"msg": "handled request",
"request": {
"remote_ip": "203.0.113.42",
"method": "POST",
"uri": "/prove/",
"protocol": "HTTP/2.0",
"host": "relayer.specterchain.com"
},
"duration": 13.542,
"status": 200,
"size": 1234
}

Logs are written to /var/log/caddy/access.log and rotated by Caddy or an external log rotation tool to prevent disk exhaustion.

Health Check Routing

Each service's health endpoint is accessible through the Caddy routing:

Health EndpointURL
Root updaterhttps://relayer.specterchain.com/root-updater/health
Commitment relayerhttps://relayer.specterchain.com/commitment/health
Proof relayerhttps://relayer.specterchain.com/prove/health
Faucethttps://relayer.specterchain.com/faucet/health
Token registryhttps://relayer.specterchain.com/registry/health
Ember proxyhttps://relayer.specterchain.com/ember/health

External monitoring systems can poll these endpoints to detect service outages.

Operational Notes

Reloading Configuration

After editing the Caddyfile, reload Caddy without downtime:

caddy reload --config /etc/caddy/Caddyfile

Active connections are preserved during the reload. New connections use the updated configuration.

Viewing Active Configuration

To inspect the currently running configuration:

caddy adapt --config /etc/caddy/Caddyfile

This outputs the JSON representation of the Caddyfile, useful for debugging routing issues.

Common Troubleshooting

IssueCauseSolution
502 Bad GatewayBackend service is downCheck pm2 status and restart the service
503 Service UnavailableBackend not responding within timeoutCheck service logs; may be overloaded
Certificate errorDNS not pointing to serverVerify A record for relayer.specterchain.com
CORS error in browserOrigin not in allowed listAdd the requesting origin to the CORS header config
404 Not FoundPath does not match any handle blockVerify the path prefix matches the Caddyfile routing