Skip to content

Infrastructure

Chapter 8. You now understand what the system does. Part III is about where it runs — beginning with the edge, the origin, and the CDN that carry it.

Where Ratio-3.0 runs and where its data lives. Source of truth: wrangler.toml, infra/aws/bundle-cdn/, db/migrations/. For the deploy pipeline see the deploy runbook; for the tables see the data model.

Deploy topology

The edge Worker is a pure proxy — it requires ORIGIN_URL and forwards every cache miss to the private origin (a Hono container on ECS), injecting x-edge-auth + x-ratio-tenant; the origin refuses any request without the correct secret (apps/edge/src/worker.ts"the edge never renders itself"). This holds in every deployed environment: staging and production both run the ECS origin, differing in scale/config, not shape.

Edge — wrangler.toml

Worker ratio-3-0, main apps/edge/src/worker.ts. Bindings:

  • KV namespace TENANTShost:{host}{ t: tenantId }. The edge reads KV, falls back to the DB on a miss and populates KV with a TTL; the control plane write-throughs on domain verify/remove (apps/admin-api/src/services/domains.ts).
  • Analytics Engine dataset METRICS — per-tenant metrics. DATABASE_URL is a Worker secret set in CI.

Edge resilience — the circuit breaker

Each edge isolate keeps a per-origin circuit breaker (packages/edge-core/src/circuit-breaker.ts). When the origin is failing, the breaker opens so the edge stops calling a dead origin and serves the last-good page:

Reads use a 1.5s origin budget; writes (/cart, /checkout) use 10s and never serve stale.

Store lookup & keeping KV in sync

Postgres is the source of truth; KV is a globally-copied read cache of the verified host → tenantId mapping. Two writers keep it fresh, and the edge self-heals on a miss:

  • Why: removes an edge→DB round trip on every request, and routing survives a DB outage (KV is warm).
  • Safety rails: negative-cache unknown hosts (a short TTL — the DB-hammer guard); only verified = true mappings are written (no squatter routing); a 1h TTL backstop heals a missed write; suspension is enforced at the origin, not by racing a KV delete.

Custom domains

Platform subdomains (store.ratiodev.in) are instant — one wildcard cert, no per-domain validation. A custom domain takes a validation beat, via Cloudflare for SaaS:

  • No custom PKI — TLS is CDN-managed (ACME). Apex domains via ALIAS/ANAME or a redirect to www.
  • The verified-only rule (a domain is only written to KV once DV passes and it was claimed by this tenant) is the anti-squat gate — see Multi-tenancy · isolation.

Domain ownership & verification — the states

There's no inbound Cloudflare webhook — verification is poll / read-repair: reading a store's domains checks CF's live status and promotes to verified inline when CF reports active (apps/admin-api/src/{routes,services}/domains.ts, over packages/data-provisioning):

Origin & admin — ECS (AWS ap-south-1)

  • Images in ECR; services are ECS Express-managed and discovered at deploy via list-clusters/list-services, matched by name (*origin*, *admin*) — the concrete service names are not declared in-repo.
  • Prod env is injected on every roll (repo variables = source of truth, so config can't drift): the COMMERCE_*_API_URL set, the GOKWIK_* script URLs, DATABASE_URL (a secret), BUNDLE_S3_BUCKET/REGION, BUNDLE_CDN_URL; empty values are filtered so a real misconfig still fails loud.
  • Per-service task roles (least privilege): the origin role is read-only on the bundle bucket, the admin-api role is read-write.

Bundle CDN — infra/aws/bundle-cdn/ (Terraform, the only IaC)

  • Private S3 bucket ratio3-theme-bundles-${env} (public access blocked, AES256, prevent_destroy), fronted by CloudFront via Origin Access Control. Because bundle keys are immutable content hashes, it uses the managed CachingOptimized policy with zero invalidation — writes go straight to S3.
  • Remote state in ratio3-tf-state (S3) + ratio3-tf-lock (DynamoDB), region ap-south-1. The backend key is a per-env literal you edit before apply.
  • Two task roles: ratio3-origin-task-${env} (read) and ratio3-admin-api-task-${env} (read-write).

Environments

  • Env selection: RATIO_LOCAL === 'true' (set by dev/all.ts) → dev-insecure Clerk + *.localhost; NODE_ENV === 'production' toggles fail-closed behavior (cfConfig() requires the CF SaaS zone in prod).
  • Local dev — bun run dev (dev/all.ts): docker compose up (Postgres 17 on :5433, MinIO on :9000) → migrate → ensure bucket → three processes: storefront (edge :8080 + origin :9090), admin-api :8787, admin-web :5173 — all with RATIO_LOCAL=true.
  • Staging / prod: Cloudflare zone ratiodev.in (+ SaaS custom-hostname fallback service.ratiodev.in), Neon Postgres, S3 + CloudFront bundle CDN.

Deeper reading

In-repo: docs/adr/0001-monorepo-layout.md, docs/adr/0002-observability.md, and the repo-root INFRASTRUCTURE.md. Confluence: the Schema ERD, ADR-017 (data-platform boundaries), the CDN/edge notes. See the ADR index.