Session 41: Peer authentication protocol design

Design 3-message peer auth handshake (AuthInit → AuthChallenge → AuthComplete):
- Deterministic crossing connection handling via npub ordering
- Domain-separated signatures (fips-peer-auth-v1)
- New message types: 0x09 AuthInit, 0x0a AuthChallenge, 0x0b AuthComplete

fips-design.md:
- Replace 4-message auth with 3-message design
- Add crossing connection handling rules
- Add message structures and signature construction

fips-architecture.md:
- Expand peer lifecycle state machine with auth states
- Add Node Startup Sequence section
- Add Static Peer Retry Policy (exponential backoff, jitter)
- Add Inbound Connection Acceptance section
- Update peer config for static-only initial impl

fips-protocol-flow.md:
- Add §7 Peer Connection Establishment
- Add §7.2 Post-Authentication flow

Design decisions: static peers only, always do peer auth regardless of
transport, accept all authenticated inbound connections.
This commit is contained in:
Johnathan Corgan
2026-01-31 19:13:15 +00:00
parent b54c347e76
commit 738775258a
3 changed files with 299 additions and 55 deletions
+151 -19
View File
@@ -308,17 +308,57 @@ Inbound:
### Peer Lifecycle
```
Discovered ──► Connecting ──► Authenticating ──► Active ──► Disconnected
│ v v │
└──────── [timeout/fail] ──────────────────────────────────┘
┌─────────────────────────────────────────┐
Disconnected
└─────────────────────────────────────────┘
│ │
[outbound] │ │ [inbound data]
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Connecting │ │AwaitingAuthInit │
│(conn-oriented) │ │ │
└──────────────────┘ └──────────────────┘
│ │
[link ready] │ [recv AuthInit]
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│AwaitingChallenge │ │AwaitingComplete │
│ (sent AuthInit) │ │(sent AuthChallenge)│
└──────────────────┘ └──────────────────┘
│ │
[recv AuthChallenge] │ │ [recv AuthComplete]
[verify, send │ │ [verify]
AuthComplete] │ │
▼ ▼
┌─────────────────────────────────────────┐
│ Active │
│ (tree gossip, filter exchange) │
└─────────────────────────────────────────┘
│ [link down / timeout]
┌─────────────────────────────────────────┐
│ Disconnected │
│ (retry if static peer) │
└─────────────────────────────────────────┘
```
- `Discovered`: known via discovery or config, no link yet
- `Connecting`: link establishment in progress (connection-oriented only)
- `Authenticating`: FIPS auth handshake in progress
- `Active`: fully integrated (has declaration, ancestry, filter)
- `Disconnected`: was active, now gone
**State descriptions:**
- `Disconnected`: No active connection; for static peers, retry with backoff
- `Connecting`: Link establishment in progress (connection-oriented transports only)
- `AwaitingAuthInit`: Inbound connection, waiting for peer's AuthInit
- `AwaitingChallenge`: Sent AuthInit, waiting for AuthChallenge
- `AwaitingComplete`: Sent AuthChallenge, waiting for AuthComplete
- `Active`: Authenticated; participating in tree gossip and filter exchange
**Crossing connection handling:**
When in `AwaitingChallenge` and we receive an AuthInit from the same peer:
- If local npub < remote npub: Ignore incoming AuthInit, remain initiator
- If local npub > remote npub: Switch to responder role, send AuthChallenge,
transition to `AwaitingComplete`
**Events:**
@@ -327,9 +367,10 @@ PeerEvent
├── Discovered { link_id, transport_addr, hint: Option<PublicKey> }
├── LinkConnected
├── LinkFailed { reason }
├── AuthChallengeReceived { challenge }
├── AuthResponseReceived { response }
├── AuthSuccess
├── AuthInitReceived { npub, nonce }
├── AuthChallengeReceived { npub, nonce, signature }
├── AuthCompleteReceived { signature }
├── AuthSuccess { npub, node_id }
├── AuthFailed { reason }
├── TreeAnnounceReceived { declaration, ancestry }
├── FilterAnnounceReceived { filter, sequence, ttl }
@@ -683,28 +724,119 @@ transport.*.auto_connect # single configured peer
---
## Node Startup Sequence
The startup sequence initializes components in dependency order:
```text
1. Load configuration
├── Parse config files (system, user, local)
├── Validate transport and peer configurations
└── Merge with defaults
2. Initialize identity
├── Load nsec from config (or generate if absent)
├── Derive npub, node_id, and FIPS address
└── Log identity information
3. Initialize transports
├── Create transport instances from config
└── Transports in Configured state
4. Start transports (begin listening)
├── Bind sockets, open interfaces
├── Transports transition to Up state
└── Ready to accept inbound connections
5. Connect to static peers
├── For each configured peer with AutoConnect policy:
│ ├── Create link via appropriate transport
│ ├── Send AuthInit to initiate authentication
│ └── On success: peer joins tree gossip
└── Failed connections enter retry with backoff
6. Node operational
├── Participating in spanning tree (even with 0 peers)
├── Processing inbound connections
└── Retrying unreachable static peers in background
```
**Notes:**
- Transports start listening (step 4) before outbound connections (step 5) to
accept inbound connections from peers who have us configured
- The node is "operational" as soon as any peer authenticates successfully
- Static peer connection attempts continue in background with retry policy
- With 0 authenticated peers, the node considers itself a potential root
### Static Peer Retry Policy
When a static peer is unreachable or authentication fails:
| Parameter | Default | Description |
|-----------|---------|-------------|
| Initial delay | 1s | First retry delay |
| Max delay | 300s | Cap on exponential backoff |
| Backoff factor | 2.0 | Multiplier per attempt |
| Jitter | ±25% | Randomization to avoid thundering herd |
| Max attempts | unlimited | Static peers retry indefinitely |
The retry timer resets to initial delay after a successful connection that
later disconnects.
### Inbound Connection Acceptance
For initial implementation, all inbound connections that successfully
authenticate are accepted. Future versions may add:
- Peer allowlists/blocklists
- Connection limits per transport
- Rate limiting on authentication attempts
- Reputation-based acceptance
---
## Configuration
### Peer Configuration
Peers are configured separately from transports:
Peers are configured at the node level, separately from transports. For initial
implementation, only static peers with `AutoConnect` policy are supported;
discovery-based and on-demand peering are future enhancements.
```
```text
PeerConfig
├── npub: PublicKey // required: who is this
├── alias: Option<String> // human-readable label
├── addresses: Vec<PeerAddress> // how to reach them
└── connect_policy: ConnectPolicy
└── connect_policy: ConnectPolicy // AutoConnect for initial impl
PeerAddress
├── transport_type: TransportType // "udp", "ethernet", "tor", etc.
├── addr: String // transport-specific, parsed by driver
└── priority: u8 // preference order
└── priority: u8 // preference order (lower = preferred)
ConnectPolicy
├── AutoConnect // connect on startup
├── OnDemand // connect when traffic needs routing
└── Manual // wait for explicit API call
├── AutoConnect // connect on startup (initial impl)
├── OnDemand // connect when traffic needs routing (future)
└── Manual // wait for explicit API call (future)
```
**Example configuration (YAML):**
```yaml
node:
peers:
- npub: "npub1abc..."
alias: "gateway"
addresses:
- transport: udp
addr: "192.168.1.1:4000"
priority: 1
- transport: tor
addr: "xyz...abc.onion:4000"
priority: 2
connect_policy: auto_connect
```
### Transport Configuration