Initial Commit
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
# `@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"`.
|
||||
Reference in New Issue
Block a user