A multi-tenant service that redirects custom domains to arbitrary destination
URLs. Each request arrives at our redirect tier (because the tenant pointed their
DNS at us); we read the Host header, look up the target, and return a 301/302.
TLS certificates for those custom domains are issued on demand and stored in
GCS — no database anywhere.
| Source | Destination | DNS shape | DNS owner |
|---|---|---|---|
f3muletown.com |
https://regions.f3nation.com/muletown |
apex (A) | Route 53 (us) |
www.f3muletown.com |
https://regions.f3nation.com/muletown |
CNAME | Route 53 (us) |
stats.f3muletown.com |
https://pax-vault.f3nation.com/stats/region/35838 |
CNAME | Route 53 (us) |
f3marshall.com |
https://regions.f3nation.com/marshall-tn |
apex (A) | external (hand-off) |
www.f3marshall.com |
https://regions.f3nation.com/marshall-tn |
CNAME | external (hand-off) |
- Redirect tier (Go,
cmd/redirectd). Terminates TLS itself and emits the redirect. Because it owns port 443, it runs on GCE (Container-Optimized OS VM), not Cloud Run. - On-demand TLS via CertMagic.
Certs are obtained from Let's Encrypt the first time a registered host is seen,
and stored in GCS (
internal/certstore) so they're shared across instances and survive restarts. Issuance is gated on the registry: the decision function refuses to obtain a cert for a host that isn't in the config (abuse / rate-limit guard). - Config is a flat JSON file in GCS — no database (
internal/mappings). The same file is the registry the TLS gate consults. The server hot-reloads it on an interval, so new mappings take effect without a redeploy. - Admin CLI (Go,
cmd/f3redirect). Add/list/remove mappings and print the DNS records a tenant must create. A TypeScript management UI is deferred.
cmd/redirectd HTTPS redirect server (on-demand TLS)
cmd/f3redirect admin CLI
internal/mappings config model, resolve, validate, DNS instructions, stores (file + GCS)
internal/redirect HTTP redirect handler + hot-reloading Live view
internal/certstore certmagic.Storage backed by GCS
internal/server CertMagic wiring (gated on-demand issuance)
infra/terraform GCS bucket, static IP, COS VM, firewall, Artifact Registry, IAM
Run the CLI against a local file (no cloud needed):
cp config.example.json /tmp/redirects.json
go run ./cmd/f3redirect list --file /tmp/redirects.json
go run ./cmd/f3redirect dns --file /tmp/redirects.json --static-ip 203.0.113.10
go run ./cmd/f3redirect add --file /tmp/redirects.json example.com https://example.orgRun the server locally (cert storage is always GCS — set a bucket):
CONFIG_FILE=/tmp/redirects.json CERT_BUCKET=<bucket> ACME_STAGING=1 \
HTTP_ADDR=:8080 HTTPS_ADDR=:8443 ACME_EMAIL=you@example.com \
go run ./cmd/redirectdgo test ./...
bash scripts/coverage.sh # enforces a coverage threshold (default 70%)The gate covers the business-logic packages (internal/mappings,
internal/redirect). Cloud-IO packages (internal/certstore, internal/server)
and the cmd/ entrypoints are validated by the deploy-time smoke test instead.
Coverage artifacts (coverage.out, coverage.html) are gitignored.
cd infra/terraform
cp terraform.tfvars.example terraform.tfvars # set project + acme_email
terraform init
terraform applyProvisions: a GCS bucket (config + certs), a reserved static IP (apex
A-records point here), an Artifact Registry repo, a COS VM running redirectd on
the host network (ports 80/443), a firewall for 80/443, and a runtime service
account with object access + image-pull rights. Key outputs: static_ip,
bucket, artifact_registry.
Seed the config once (thereafter, manage it with the CLI):
gcloud storage cp config.example.json gs://<bucket>/config/redirects.json- CI (
.github/workflows/ci.yaml):go vet, the coverage gate,go build, and a Docker build on every push/PR. - CD (
.github/workflows/deploy.yaml): every push tomainbuilds and pushes the image to Artifact Registry and rolls the VM (which re-pulls:lateston boot). Auth is keyless via Workload Identity Federation — no SA keys.
Images are built with
--provenance=falseso they're plain single-arch manifests; the COS docker on the VM does not reliably pull buildx OCI indexes that carry an attestation manifest.
Apex domains can't CNAME, so they use an A record to the static IP; subdomains CNAME to the apex (which carries that A record). Generate the exact records with:
f3redirect dns --bucket <bucket> --static-ip <STATIC_IP>Muletown's records live in our Route 53 zone and are managed here. Marshall's
domain is controlled by a third party — hand them the dns output for
f3marshall.com / www.f3marshall.com.