# Capability Spec Delta: api-tenant-resolver-header (api-tenant-resolver-header) **Package**: `@suaveplan/api-tenant-resolver-header` **Status**: PROPOSED > Every requirement below carries a `**Verified by:**` line at authoring time. ## ADDED Requirements ### Requirement: Resolves tenant from configured header The package SHALL expose `createHeaderTenantResolver({ headerName, validate, registry })` that reads `headerName` (default `X-Tenant-Id`) and returns `{ tenantId, source: "header" }` when the value validates AND (when `registry` is supplied) is a member of the tenant registry. #### Scenario: well-formed header resolves - WHEN a request carries `X-Tenant-Id: acme` AND `acme` is in the registry - THEN the resolver returns `{ tenantId: "acme", source: "header" }` AND `api.tenant.resolves_total{resolver="header"}` increments **Verified by:** `src/resolver/resolver.test.ts::"well-formed header resolves"` --- ### Requirement: Strict mode behaviour for invalid values The package SHALL react to invalid header values per `strict` config: when `strict: true`, raise `TENANT_HEADER_INVALID` (HTTP 400). When `strict: false`, return `null` so a downstream resolver may match. #### Scenario: strict raises 400 - WHEN the header value fails the validator AND `strict: true` - THEN the resolver raises `TENANT_HEADER_INVALID` #### Scenario: non-strict returns null - WHEN the header value fails the validator AND `strict: false` - THEN the resolver returns `null` **Verified by:** `src/resolver/resolver.test.ts::"strict mode raises 400 on invalid"` AND `src/resolver/resolver.test.ts::"non-strict mode returns null on invalid"` --- ### Requirement: Trusted-source enforcement The package SHALL ignore the header when `request.metadata.source` is not in `trustedSources` (default `["service-mesh"]`). When ignored AND `strict: true`, raise `TENANT_HEADER_UNTRUSTED_SOURCE`; when ignored AND `strict: false`, return `null`. #### Scenario: untrusted source raises in strict - WHEN the header is present AND `request.metadata.source = "edge"` AND `trustedSources = ["service-mesh"]` AND `strict: true` - THEN the resolver raises `TENANT_HEADER_UNTRUSTED_SOURCE` **Verified by:** `src/trust/trust.test.ts::"untrusted source raises in strict mode"` --- ### Requirement: Not-found-in-registry distinct from invalid The package SHALL distinguish a tenant that fails validation (`TENANT_HEADER_INVALID`) from one that is well-formed but not in the registry (`TENANT_HEADER_NOT_FOUND_IN_REGISTRY`). Both surface in strict mode; both return `null` in non-strict mode. #### Scenario: well-formed but unknown tenant - WHEN the header is `X-Tenant-Id: unknown-tenant-1` AND the registry does not contain it AND `strict: true` - THEN the resolver raises `TENANT_HEADER_NOT_FOUND_IN_REGISTRY` **Verified by:** `src/resolver/resolver.test.ts::"well-formed but unknown raises NOT_FOUND_IN_REGISTRY in strict"` --- ### Requirement: Passes `runTenantResolverConformance` The package SHALL pass the conformance harness exported by `@suaveplan/api-tenant-provider/testing`. #### Scenario: conformance passes - WHEN `runTenantResolverConformance("header", makeHeaderResolver)` runs - THEN every harness `it(...)` passes **Verified by:** `src/resolver/resolver.spec-conformance.test.ts::"header resolver passes runTenantResolverConformance"`