Files
suaveplan/openspec/changes/archive/2026-05-04-network-plugin-auth/proposal.md
2026-05-27 23:29:53 +01:00

189 lines
6.5 KiB
Markdown

# OpenSpec Change: network-plugin-auth
- **Change ID**: network-plugin-auth
- **Package**: `@suaveplan/network-plugin-auth` (new)
- **Status**: PROPOSED
- **Date**: 2026-05-01
---
## Summary
Implement mesh membership authentication and cross-mesh relay authorization. The `auth` package exists in genesis (`@suaveplan/auth`) but provides generic OAuth/JWT/session primitives. There is no mesh-specific authentication: verifying that a peer is allowed to join a specific mesh, issuing capability tokens that constrain which tiers a peer can occupy, or validating trust relationships between meshes for relay traffic. This plugin bridges `@suaveplan/auth` into the mesh architecture.
---
## Why
**Current state**: `network-plugin-crypto-messages` signs messages with Ed25519 and verifies server-signed session/mesh tokens. However, there is no authentication of the peer itself before it joins a mesh, no capability-based tier assignment, and no cross-mesh relay trust validation.
**What's missing**:
- Peer authentication before joining a mesh
- Mesh capability tokens (who can be Tier 0 vs Tier 1 vs Tier 2)
- Cross-mesh relay authorization (which meshes trust each other)
- Message integrity at relay hops
---
## Core components
### 1. `MeshMembershipVerifier`
Verifies that a peer is authorized to join a mesh.
```ts
class MeshMembershipVerifier {
constructor(options: {
authProvider: AuthProvider; // from @suaveplan/auth
meshId: string;
serverPubKey: Uint8Array;
});
verifyJoinRequest(request: JoinRequest): Promise<JoinResult>;
}
interface JoinRequest {
peerId: string;
meshId: string;
authToken: string; // JWT or opaque token from @suaveplan/auth
publicKey: Uint8Array; // Ed25519 public key for future message verification
claimedCapabilities: Capability[];
}
interface JoinResult {
accepted: boolean;
peerId: string;
assignedTier: number; // 0, 1, or 2
capabilityToken: CapabilityToken;
reason?: string;
}
```
**Verification steps**:
1. Validate `authToken` via `@suaveplan/auth`.
2. Check that `publicKey` matches the identity in `authToken`.
3. Verify `claimedCapabilities` against the mesh's allowed capability set.
4. Issue a `CapabilityToken` signed by the server that constrains the peer's tier and permissions.
### 2. `CapabilityTokenIssuer`
Issues and validates capability tokens for mesh operations.
```ts
interface CapabilityToken {
peerId: string;
meshId: string;
tier: number; // max tier this peer can occupy
capabilities: Capability[];
issuedAt: number;
expiresAt: number;
serverSignature: Uint8Array;
}
enum Capability {
HOST = "host", // can be Tier 0
SUBHOST = "subhost", // can be Tier 1
RELAY = "relay", // can act as intra-mesh relay
BRIDGE = "bridge", // can act as cross-mesh relay backbone
AUDIT = "audit", // can write to audit log
CREATE_SESSION = "create_session",
JOIN_SESSION = "join_session",
}
class CapabilityTokenIssuer {
constructor(options: {
serverKeyPair: Ed25519KeyPair;
defaultTtlMs: number; // default 3600000 (1 hour)
});
issue(peerId: string, meshId: string, tier: number, capabilities: Capability[]): CapabilityToken;
validate(token: CapabilityToken): boolean;
renew(token: CapabilityToken): CapabilityToken;
}
```
### 3. `CrossMeshTrustValidator`
Validates trust relationships between meshes for relay traffic.
```ts
class CrossMeshTrustValidator {
constructor(options: {
trustStore: TrustStore; // meshId → trusted meshId set
serverPubKey: Uint8Array;
});
validateRelay(sourceMeshId: string, targetMeshId: string): boolean;
validateBackbone(hostId: string, meshId: string): boolean;
addTrust(fromMeshId: string, toMeshId: string): void;
removeTrust(fromMeshId: string, toMeshId: string): void;
}
interface TrustStore {
getTrustedMeshes(meshId: string): string[];
isTrusted(fromMeshId: string, toMeshId: string): boolean;
}
```
**Trust model**:
- By default, meshes do NOT trust each other.
- Trust is established by the central server signing a `MeshTrustToken` that authorizes relay between two meshes.
- Tier 0 hosts verify `MeshTrustToken` before accepting backbone connections from another mesh's host.
- `CrossMeshTrustValidator` caches trust decisions with TTL (default 5 minutes).
---
## Error codes
```ts
const MESH_AUTH_CODES = defineCodes("MESH_AUTH", [
"JOIN_REJECTED",
"TOKEN_EXPIRED",
"TOKEN_INVALID_SIGNATURE",
"CAPABILITY_DENIED",
"TIER_NOT_ALLOWED",
"TRUST_NOT_ESTABLISHED",
"BACKBONE_UNTRUSTED",
"AUTH_PROVIDER_ERROR",
] as const);
```
`MeshAuthError extends SuaveplanError<MeshAuthErrorCode>`.
---
## Dependencies
- **Consumes**: `@suaveplan/network` (`NetworkNode`, `NetworkBus`), `@suaveplan/auth` (JWT validation, session management), `@suaveplan/network-plugin-crypto-messages` (Ed25519 signatures, server tokens), `@suaveplan/network-plugin-registry` (peer public keys), `@suaveplan/error`, `@suaveplan/types`, `@opentelemetry/api`.
- **Blocked on**: `network-core` (shipped), `network-plugin-crypto-messages`, `@suaveplan/auth`.
---
## Verification approach
- `src/verifier/verifier.spec-conformance.test.ts` — pins join request validation, tier assignment, capability checks.
- `src/issuer/issuer.spec-conformance.test.ts` — pins token issue/validate/renew, signature verification, expiry.
- `src/trust/trust-validator.spec-conformance.test.ts` — pins trust store, relay validation, backbone validation, cache TTL.
- `tests/integration/auth.spec-conformance.test.ts` — end-to-end: peer joins mesh, receives capability token, relay is validated.
- 100/100/100/100 Istanbul coverage.
---
## Conformance requirements (proposed)
- `NET-106` — `MeshMembershipVerifier` validates `authToken` via `@suaveplan/auth` before allowing mesh join.
- `NET-107` — `MeshMembershipVerifier` checks that the peer's `publicKey` matches the identity in `authToken`.
- `NET-108` — `CapabilityTokenIssuer` issues tokens signed by the server that constrain tier and capabilities.
- `NET-109` — `CapabilityToken` includes `peerId`, `meshId`, `tier`, `capabilities`, `issuedAt`, `expiresAt`, and `serverSignature`.
- `NET-110` — `CrossMeshTrustValidator` rejects relay between meshes unless a trust relationship is established.
- `NET-111` — `CrossMeshTrustValidator` caches trust decisions with a configurable TTL.
- `NET-112` — Tier 0 hosts verify `MeshTrustToken` before accepting backbone connections.
---
## Out of scope
- **Fine-grained ACL per message type** → v2; v1 uses capability tokens.
- **Revocation lists** → v2; v1 relies on short token expiry.
- **Zero-knowledge proofs** → future research.