Files
suaveplan/docs/network/security-and-tokens.md
2026-05-27 23:29:53 +01:00

179 lines
8.7 KiB
Markdown

# Network Security and Tokens
The genesis network has three orthogonal layers of cryptographic enforcement:
1. **Identity** — every peer has a stable Ed25519 identity. Every message is signed.
2. **Authority** — server-signed tokens grant a peer the right to do specific things in a specific mesh/session.
3. **Trust** — cross-mesh interactions require a `MeshTrustToken` that establishes one mesh's willingness to accept envelopes from another.
This document is the reference for all three.
## Layer 1 — Message-level integrity (NET-27, NET-30)
`network-plugin-crypto-messages` is the layer.
### Per-message Ed25519 signatures (NET-27)
Every outbound `MessageEnvelope` from a host (Tier 0 or Tier 1) carries an Ed25519 signature in its headers. Inbound signatures are verified using peer public keys from the registry. Invalid signatures cause the message to be **dropped silently** (and the sender's reputation in `HostHistory` to take a hit).
The key material is the same drive-identity key used by the chunks plugin — one Ed25519 keypair per peer, used at multiple layers. Generated and stored by `@suaveplan/crypto-vault` (BIP39 mnemonic + Argon2id KDF + zeroize on lock).
### Peer identity challenge-response (NET-30)
When two peers handshake, they perform a challenge-response to verify identity:
```text
A → B: peerA_pubkey, nonceA
B → A: peerB_pubkey, nonceB, sigB(peerA_pubkey ‖ nonceA)
A → B: sigA(peerB_pubkey ‖ nonceB)
```
Any peer claiming `peerB_pubkey` who can't produce `sigB(...)` is rejected. This catches spoofed peer-IDs at handshake before they make it into the registry.
## Layer 2 — Authority tokens (NET-28, NET-29, NET-106..NET-109)
Three token shapes, all server-signed (in models with a server):
### `MeshToken` (NET-29)
```ts
interface MeshToken {
meshId: string;
maxCapacity: number;
allowedTiers: ('tier-0' | 'tier-1' | 'tier-2')[];
hostingModel: 'PURE_CLIENT' | 'SERVER_BASED' | 'SERVER_TO_CLIENT_FAILOVER' | 'CLIENT_WITH_SERVER_BACKUP';
expiresAt: number;
serverSignature: Uint8Array; // Ed25519 over canonicalSerialize(this without the sig)
}
```
Required for mesh creation. The mesh's hosting model lives here. Long expiry (days/weeks).
### `SessionToken` (NET-28)
```ts
interface SessionToken {
sessionId: string;
parentSessionId: string | null;
capacity: number;
rulesHash: string; // hash of the session's policy doc
expiresAt: number;
serverSignature: Uint8Array;
}
```
Required for session creation. Sessions can nest (NET-19) up to depth 4. The `rulesHash` pins the session's policy at issuance time so it can't be silently mutated.
### `CapabilityToken` (NET-108, NET-109)
```ts
interface CapabilityToken {
peerId: string;
meshId: string;
tier: 'tier-0' | 'tier-1' | 'tier-2';
capabilities: string[]; // e.g. ['relay', 'vote', 'admit-peer']
issuedAt: number;
expiresAt: number;
serverSignature: Uint8Array;
}
```
Issued when a peer joins. The `capabilities` array constrains what the peer is *authorised* to do regardless of what the protocol allows it to *attempt*. A Tier-0 capability token includes `admit-peer`; a Tier-2 doesn't. Cross-tier escalation is blocked at the verifier (NET-48).
`network-plugin-auth` (NET-106..NET-109) is the enforcement plugin. `MeshMembershipVerifier` checks the `authToken` on join against `@suaveplan/auth`. `CapabilityTokenIssuer` mints them. `CrossMeshTrustValidator` (NET-110..NET-112) handles inter-mesh acceptance.
### Tier-specific security policies (NET-48)
```text
Tier 0 — full authority. Can issue tokens (when server isn't available, with grace-period restrictions).
Tier 1 — relay + voting authority. Cannot issue tokens.
Tier 2 — read-only for sensitive operations.
```
`SecurityManager.processTierSecurity(tierId, operation, data)` enforces this on every operation. Cross-tier delegation requires explicit credential transfer from the higher tier.
## Layer 3 — Cross-mesh trust (NET-110..NET-112)
`MeshTrustToken` is what one mesh hands another mesh to say "I'll accept your relay traffic":
```ts
interface MeshTrustToken {
fromMeshId: string;
toMeshId: string;
allowedOperations: string[]; // e.g. ['relay-message', 'cd:request']
bandwidthQuotaPerHour?: number;
expiresAt: number;
bothServerSignatures: [Uint8Array, Uint8Array]; // signed by both meshes' Tier 0
}
```
`CrossMeshTrustValidator` rejects relay envelopes from untrusted mesh ids (NET-110). Tier 0 hosts verify trust tokens before accepting backbone connections (NET-112). Trust decisions are cached with configurable TTL (NET-111) so we don't re-verify on every envelope.
This means a phone-mesh and a corporate-mesh don't automatically interoperate. Two Tier 0 hosts must explicitly establish trust (out of band: typically via a co-signed token issued by both servers, or via a bilateral capability exchange in `PURE_CLIENT` mode).
## Plugin security verifier (NET-47)
`network-plugin-crypto-messages` also validates plugins themselves:
```ts
class PluginSecurityVerifier {
verifyPlugin(pluginId: string, pluginData: Uint8Array): Promise<boolean>;
authorizePluginOperation(pluginId, operation, context?): Promise<boolean>;
monitorPluginActivity(pluginId): void;
setPluginSecurityPolicy(policy): void;
}
```
The intent is plugin-code signing: a deployment may want to allow only plugins signed by a known publisher key. Plugins requesting capabilities they didn't declare are rejected. Anomalous plugin activity (excessive broadcast, unauthorised operations) is detected and reported.
The old planning corpus admits (`PHASE_6_README.md`) that the resource-monitoring side of this is "currently simulated and would need real metrics in a production environment." Genesis treats it as a hardening backlog item — install the plugin, get signature verification today; resource accounting follows.
## What about the model where the server is gone?
Tokens issued before the server vanished remain valid until their `expiresAt`. `network-plugin-server-bridge` (NET-32) extends grace periods so existing tokens don't expire mid-outage. New tokens cannot be issued — operations that require a fresh token (admit a brand-new peer, create a new session) are queued until the server returns. Existing peers, with valid `CapabilityToken`s, keep operating with full authority.
When the server returns:
1. Buffered signed-log entries replay (NET-33).
2. The server validates each entry's Ed25519 signature against the issuing peer's pubkey.
3. Conflicts are detected (e.g., two peers both claimed Tier-0 in different partitions). Resolution depends on the partition merge strategy (NET-80).
4. The server broadcasts the reconciled state.
5. Queued token-issuance operations execute.
The cryptographic chain — every message signed, every token signed, every audit-log entry signed — is what makes this replay possible. There's no ambiguity about who did what; every action is provenance-stamped.
## Audit log (NET-37..NET-39)
`network-plugin-audit-mesh` keeps an append-only log where each entry includes:
```text
{ seq, prevHash, payload, hostSignature }
```
- `seq` is monotonic.
- `prevHash` makes the log a hash chain — break detection on any tampering.
- `hostSignature` is the issuing host's Ed25519 over `(seq, prevHash, payload)`.
Logs are replicated to at least R sibling hosts (NET-38). CRITICAL events replicate synchronously. Old logs are compacted into signed checkpoints (NET-39) — a snapshot at a given seq, signed by the host, that downstream replicas use to skip the prefix.
This is what gives you a verifiable audit trail across the entire mesh, surviving partitions and server outages.
## Where keys live, end to end
| Key | Owner | Storage | Used for |
| --- | --- | --- | --- |
| Per-peer Ed25519 (drive identity) | The peer | `@suaveplan/crypto-vault` | Signing every message, every chunk, every audit entry |
| Per-mesh server Ed25519 | Central server | Server HSM / `@suaveplan/keys` (when available) | Signing `MeshToken`, `SessionToken`, `CapabilityToken` |
| Cross-mesh trust signing | Both meshes' servers | As above | Co-signing `MeshTrustToken` |
| Per-file `fileKey` (AEAD) | Drive | Wrapped by partition KEK; lives next to manifest | Encrypting chunks |
| Per-partition KEK | `drive-sharing` | Wrapped per-grantee with X25519 sealed-box | Wrapping `fileKey`s |
Keys never cross trust boundaries unwrapped. Wrapping is the only mechanism for delegation.
## Reading further
- [`architecture.md`](./architecture.md) — `MessageEnvelope` and `NetworkBus`.
- [`topology-and-failover.md`](./topology-and-failover.md) — when token issuance is paused.
- [`offline-and-reintegration.md`](./offline-and-reintegration.md) — buffered log replay.
- [`../drive/encryption.md`](../drive/encryption.md) — the drive layer's use of the per-peer Ed25519 key.