Files
2026-05-27 23:29:53 +01:00

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 the TenantResolver contract from api-types. Reads headerName (default X-Tenant-Id); validates the value via the supplied validate function (default: must match ^[a-z0-9-]{1,64}$) AND optionally checks membership in tenant.provider's registry; resolves to { tenantId, source: "header" } or null.
  • ADDED: Strict mode — when strict: true (host config), a value that fails validation raises TENANT_HEADER_INVALID (HTTP 400). When strict: false, invalid values cause the resolver to return null so a downstream resolver may match.
  • ADDED: Trusted-source enforcement — trustedSources config (default: ["service-mesh"]) lists which traffic sources are allowed to set the header. The resolver consults request.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 runTenantResolverConformance from api-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 — runs runTenantResolverConformance.
  • 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".