fix(api): share base-domain API host
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
Tenant subdomains route through api.<base-domain>; nginx forwards the exact storefront host derived from the validated browser origin.
This commit is contained in:
@@ -1,14 +1,17 @@
|
||||
---
|
||||
id: ADR-0004
|
||||
title: Derive each API host from the complete storefront host
|
||||
status: active
|
||||
status: superseded
|
||||
date: 2026-08-20
|
||||
supersedes: []
|
||||
tags: [architecture, multi-tenant, api, routing, dns]
|
||||
superseded_by: [ADR-0005]
|
||||
---
|
||||
|
||||
# ADR-0004: Derive each API host from the complete storefront host
|
||||
|
||||
> Superseded by [ADR-0005](ADR-0005-share-api-host-across-storefront-subdomains.md).
|
||||
|
||||
## Context
|
||||
|
||||
One production bundle serves root domains and arbitrary storefront subdomains.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
---
|
||||
id: ADR-0005
|
||||
title: Share one API host across storefront subdomains
|
||||
status: active
|
||||
date: 2026-08-20
|
||||
supersedes: [ADR-0004]
|
||||
tags: [architecture, multi-tenant, api, routing, dns]
|
||||
---
|
||||
|
||||
# ADR-0005: Share one API host across storefront subdomains
|
||||
|
||||
## Context
|
||||
|
||||
One frontend bundle serves a base storefront domain and tenant subdomains. The
|
||||
API is shared at the base-domain level; a tenant subdomain must not create a
|
||||
nested API hostname.
|
||||
|
||||
## Decision
|
||||
|
||||
- `example.com`, `store1.example.com`, and `www.example.com` all use
|
||||
`https://api.example.com`.
|
||||
- The complete storefront hostname remains the tenant hint. nginx validates the
|
||||
browser Origin and forwards that hostname as `X-Storefront-Host`.
|
||||
- Backend tenant lookup trusts that header only from the known proxy, verifies
|
||||
it against the domain registry, and binds authenticated sessions to the same
|
||||
tenant.
|
||||
- Localhost continues through `/api`. `tenantApiBaseUrls` remains available for
|
||||
public-suffix or custom-domain exceptions.
|
||||
|
||||
## Consequences
|
||||
|
||||
Tenant subdomains need no extra API DNS records or certificates. CORS must echo
|
||||
the exact allowed storefront origin, while unknown or disabled domains still
|
||||
receive `403` from the backend. The shared API `Host` alone cannot identify a
|
||||
subdomain tenant.
|
||||
@@ -11,4 +11,4 @@
|
||||
{"id":"PV-20260818T104300Z-b3c4","subject":"RoutingContext","predicate":"is-required-on","object":"CheckoutSession, PaymentIntent, Payment, Refund and ReconciliationRecord; frozen at checkout-session creation and immutable thereafter, so a payment is always attributable to exactly one payment point","src":["docs/backend/PHASE-1-MONEY-FX-PAYMENTS-CONTRACT.md","docs/backend/PHASE-7-PAYMENTS-RECONCILIATION-CONTRACT.md"],"status":"active","kind":"constraint","updated_at":"2026-08-18T10:43:00Z","confidence":"high","tags":["payments","reconciliation","contract"]}
|
||||
{"id":"PV-20260818T104400Z-d9e2","subject":"partner-api-credentials","predicate":"are-scoped-by","object":"a single node whose subtree defines authority; we hold only the partner-generated public key, rotation runs on a bounded overlap window and revocation is immediate and irreversible","src":["docs/backend/PARTNER-PROVISIONING-API-CONTRACT.md","docs/backend/TRACK-S-SECURITY-RBAC-CONTRACT.md"],"status":"active","kind":"decision","updated_at":"2026-08-18T10:44:00Z","confidence":"high","tags":["security","credentials","partner"]}
|
||||
{"id":"PV-20260818T104500Z-a6f7","subject":"checkout-payment-methods","predicate":"already-support","object":"both qr and card end to end in src/app/pages/cart/cart.component.ts (separate create paths and separate status pollers); card is not an outstanding gap","src":["src/app/pages/cart/cart.component.ts","src/app/services/api.service.ts"],"status":"active","kind":"implemented","updated_at":"2026-08-18T10:45:00Z","confidence":"high","tags":["payments","frontend"]}
|
||||
{"id":"PV-20260820T095500Z-b17e","subject":"tenant-api-routing","predicate":"is-decided-to-use","object":"a runtime-derived API origin that prefixes the complete storefront hostname with api.; example.com maps to api.example.com and store1.example.com maps to api.store1.example.com for bootstrap, auth, legacy, and versioned endpoints","src":["docs/context/adrs/ADR-0004-derive-api-host-from-storefront-host.md","src/app/core/config/api-config.service.ts"],"status":"active","kind":"decision","updated_at":"2026-08-20T10:31:00Z","confidence":"high","tags":["architecture","multi-tenant","api","routing","dns"]}
|
||||
{"id":"PV-20260820T095500Z-b17e","subject":"tenant-api-routing","predicate":"is-decided-to-use","object":"one runtime-derived API origin per base domain; example.com and store1.example.com both map to api.example.com for bootstrap, auth, legacy, and versioned endpoints","src":["docs/context/adrs/ADR-0005-share-api-host-across-storefront-subdomains.md","src/app/core/config/api-config.service.ts"],"status":"active","kind":"decision","updated_at":"2026-08-20T16:00:00Z","confidence":"high","tags":["architecture","multi-tenant","api","routing","dns"]}
|
||||
|
||||
Reference in New Issue
Block a user