Files
suaveplan/openspec/changes/complete-network-plugins/specs/network-plugin-auth/spec.md
2026-05-27 23:29:53 +01:00

5.6 KiB

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.<KEY>" 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"