# Capability Spec Delta: network-plugin-auth (complete-network-plugins) **Package**: `@suaveplan/network-plugin-auth` **Status**: PROPOSED > Every requirement below carries a `**Verified by:**` line at authoring time. This is the first canonical capability spec for the package; the delta is entirely `## ADDED Requirements`. ## ADDED Requirements ### Requirement: MeshMembershipVerifier verifies join requests against an AuthProvider The package SHALL export `MeshMembershipVerifier`, `MeshMembershipVerifierConfig`, `JoinRequest`, `JoinResult`, `AuthProvider`, and a `Capability` union enumerating the recognised mesh capabilities. The verifier SHALL delegate credential verification to the supplied `AuthProvider` and SHALL throw `MeshAuthError(JOIN_REJECTED)` when the provider denies the request. #### Scenario: provider denies the join - **WHEN** `verifier.verify(request)` is called and the provider returns a denial - **THEN** the call throws `MeshAuthError` with `code === "NET_AUTH.JOIN_REJECTED"` **Verified by:** `src/verifier/verifier.spec-conformance.test.ts::"throws JOIN_REJECTED when provider denies request"` --- ### Requirement: CapabilityTokenIssuer mints signed capability-bound tokens The package SHALL export `CapabilityTokenIssuer`, `CapabilityTokenIssuerConfig`, `CapabilityToken`, and a `TokenSigner` interface. Issued tokens SHALL embed the granted capability set, an issuance timestamp, and an expiry; signature SHALL be produced by the supplied `TokenSigner`. #### Scenario: issued token carries capability set and expiry - **WHEN** `issuer.issue({ subject, capabilities, ttlMs })` is called - **THEN** the returned `CapabilityToken` has the same capabilities, an `iat`, an `exp = iat + ttlMs`, and a signature produced by the supplied signer **Verified by:** `src/issuer/issuer.spec-conformance.test.ts::"issued token carries capability set and expiry"` --- ### Requirement: CapabilityTokenIssuer rejects expired and tampered tokens on verify The issuer SHALL expose a `verify(token)` operation that throws `MeshAuthError(TOKEN_EXPIRED)` when the current clock exceeds `token.exp` and `MeshAuthError(TOKEN_INVALID_SIGNATURE)` when the signature does not validate against the configured key. #### Scenario: expired token - **WHEN** `verify(token)` is called after `clock() > token.exp` - **THEN** the call throws `MeshAuthError(TOKEN_EXPIRED)` #### Scenario: tampered signature - **WHEN** `verify(token)` is called on a token whose payload was modified after signing - **THEN** the call throws `MeshAuthError(TOKEN_INVALID_SIGNATURE)` **Verified by:** `src/issuer/issuer.spec-conformance.test.ts::"throws TOKEN_EXPIRED past exp"` AND `src/issuer/issuer.spec-conformance.test.ts::"throws TOKEN_INVALID_SIGNATURE on tampered payload"` --- ### Requirement: CrossMeshTrustValidator gates cross-mesh relay authority The package SHALL export `CrossMeshTrustValidator`, `CrossMeshTrustValidatorConfig`, `TrustRelation`, and the `TrustDirection` union `"relay" | "backbone"`. The validator SHALL reject relay attempts between meshes lacking a registered `TrustRelation` and SHALL throw `MeshAuthError(TRUST_NOT_ESTABLISHED)` on failure. #### Scenario: relay attempt without trust - **WHEN** `validator.authorize({ sourceMesh, targetMesh, direction: "relay" })` is called with no registered relation - **THEN** the call throws `MeshAuthError(TRUST_NOT_ESTABLISHED)` **Verified by:** `src/trust/trust-validator.spec-conformance.test.ts::"throws TRUST_NOT_ESTABLISHED when no relation registered"` --- ### Requirement: Capability enforcement raises CAPABILITY_DENIED on insufficient grant The verifier SHALL throw `MeshAuthError(CAPABILITY_DENIED)` when a request requires a capability that the supplied token does not carry. #### Scenario: missing capability - **WHEN** a request requires `"relay-egress"` and the token grants only `["mesh-join"]` - **THEN** the call throws `MeshAuthError(CAPABILITY_DENIED)` with `context.required === "relay-egress"` **Verified by:** `src/verifier/verifier.spec-conformance.test.ts::"throws CAPABILITY_DENIED on missing capability"` --- ### Requirement: Error-code registry is frozen and namespaced NET_AUTH `MESH_AUTH_CODES` SHALL be frozen under the `NET_AUTH` namespace and SHALL include exactly: `JOIN_REJECTED`, `TOKEN_EXPIRED`, `TOKEN_INVALID_SIGNATURE`, `CAPABILITY_DENIED`, `TRUST_NOT_ESTABLISHED`. Every code SHALL be reachable from a production throw site. #### Scenario: registry membership - **WHEN** `MESH_AUTH_CODES` is inspected - **THEN** every key maps to a value of the form `"NET_AUTH."` and no extra keys are present **Verified by:** `src/error-codes.spec-conformance.test.ts::"MESH_AUTH_CODES is frozen and prefixed NET_AUTH"` --- ### Requirement: No bare-Error throws in production code `src/` SHALL contain zero bare `throw new (Error|TypeError|RangeError|SyntaxError)\(` sites outside test files. Every production failure throws `MeshAuthError` or a transitive `SuaveplanError` subclass. #### Scenario: source grep returns zero hits - **WHEN** the grep is run - **THEN** the output is empty **Verified by:** `tests/no-bare-throws.test.ts::"network-plugin-auth src/ has no bare Error throws"` --- ### Requirement: README is auto-docs idempotent The package `README.md` SHALL contain exactly one balanced auto-docs block; the generator SHALL produce a byte-identical file on a second run. #### Scenario: marker count is exactly one - **WHEN** `grep -c "begin:auto-docs" packages/network/network-plugin-auth/README.md` - **THEN** output is `1` **Verified by:** `scripts/generate-package-module-tree.test.ts::"README regen is idempotent for network-plugin-auth"`