2.8 KiB
2.8 KiB
@suaveplan/api-tenant-resolver-header — header-based tenant resolver
Package: @suaveplan/api-tenant-resolver-header
Status: PROPOSED
Type: new-package
Stage: 8 — see umbrella openspec/changes/api-plugin-foundation/
Why
Per design doc §6 step 4, the per-request pipeline runs a tenant resolver chain — first the principal's tenantId, then fallback resolvers. Service-to-service traffic frequently carries the tenant in an HTTP header (X-Tenant-Id, X-Acme-Tenant, etc.) because no end-user session exists to resolve it from. This adapter handles that case.
What changes
- ADDED:
createHeaderTenantResolver({ headerName, validate, registry })— implements theTenantResolvercontract fromapi-types. ReadsheaderName(defaultX-Tenant-Id); validates the value via the suppliedvalidatefunction (default: must match^[a-z0-9-]{1,64}$) AND optionally checks membership intenant.provider's registry; resolves to{ tenantId, source: "header" }ornull. - ADDED: Strict mode — when
strict: true(host config), a value that fails validation raisesTENANT_HEADER_INVALID(HTTP 400). Whenstrict: false, invalid values cause the resolver to returnnullso a downstream resolver may match. - ADDED: Trusted-source enforcement —
trustedSourcesconfig (default:["service-mesh"]) lists which traffic sources are allowed to set the header. The resolver consultsrequest.metadata.source(set by upstream middleware); if the source is not trusted, the header is ignored. This prevents end-user clients from spoofing the tenant. - ADDED: Conformance — passes
runTenantResolverConformancefromapi-tenant-provider/testing. - ADDED: Error codes (
TENANT_HEADER_*):INVALID,NOT_FOUND_IN_REGISTRY,UNTRUSTED_SOURCE.
Out of scope
- The trusted-source metadata itself — set by the router adapter when traffic enters from a service mesh / mTLS-verified ingress.
- Resolving multiple tenants (multi-tenant batch operations) — outside the per-request model.
Source mapping
Genesis-original. Source of design intent: docs/superpowers/specs/2026-05-13-api-plugin-foundation-design.md.
| Requirement | Design doc section |
|---|---|
| Tenant resolver chain | §6 step 4 |
| Service-to-service tenant injection | §6 step 4 |
Verification approach
src/resolver/resolver.test.ts— valid header → tenantId; invalid + strict → 400; invalid + non-strict → null; missing header → null.src/trust/trust.test.ts— untrusted source ignored even when header is well-formed; trusted source resolves.src/resolver/resolver.spec-conformance.test.ts— runsrunTenantResolverConformance.tests/dep-graph.test.ts— runtime deps.
Migration impact
None — additive only. api-host wires this into the tenant-resolver chain when host.tenant.resolvers includes "header".