3.2 KiB
Implementation Details
This document covers concrete technology choices and dependencies. For the architectural design, see ARCHITECTURE.md.
Client Connection Flow
New Client Approval
When a client whose public key is not yet in the database connects, all connected user agents are asked to approve the connection. The first agent to respond determines the outcome; remaining requests are cancelled via a watch channel.
flowchart TD
A([Client connects]) --> B[Receive AuthChallengeRequest]
B --> C{pubkey in DB?}
C -- yes --> D[Read nonce\nIncrement nonce in DB]
D --> G
C -- no --> E[Ask all UserAgents:\nClientConnectionRequest]
E --> F{First response}
F -- denied --> Z([Reject connection])
F -- approved --> F2[Cancel remaining\nUserAgent requests]
F2 --> F3[INSERT client\nnonce = 1]
F3 --> G[Send AuthChallenge\nwith nonce]
G --> H[Receive AuthChallengeSolution]
H --> I{Signature valid?}
I -- no --> Z
I -- yes --> J([Session started])
Known Issue: Concurrent Registration Race (TOCTOU)
Two connections presenting the same previously-unknown public key can race through the approval flow simultaneously:
- Both check the DB → neither is registered.
- Both request approval from user agents → both receive approval.
- Both
INSERTthe client record → the second insert silently overwrites the first, resetting the nonce.
This means the first connection's nonce is invalidated by the second, causing its challenge verification to fail. A fix requires either serialising new-client registration (e.g. an in-memory lock keyed on pubkey) or replacing the separate check + insert with an INSERT OR IGNORE / upsert guarded by a unique constraint on public_key.
Nonce Semantics
The program_client.nonce column stores the next usable nonce — i.e. it is always one ahead of the nonce last issued in a challenge.
- New client: inserted with
nonce = 1; the first challenge is issued withnonce = 0. - Existing client: the current DB value is read and used as the challenge nonce, then immediately incremented within the same exclusive transaction, preventing replay.
Cryptography
Authentication
- Signature scheme: ed25519
Encryption at Rest
- Scheme: Symmetric AEAD — currently XChaCha20-Poly1305
- Version tracking: Each
aead_encrypteddatabase entry carries aschemefield denoting the version, enabling transparent migration on unseal
Server Identity
- Transport: TLS with a self-signed certificate
- Key type: Generated on first run; long-term (no rotation mechanism yet)
Communication
- Protocol: gRPC with Protocol Buffers
- Server identity distribution:
ServerInfoprotobuf struct containing the TLS public key fingerprint - Future consideration: grpc-web lacks bidirectional stream support, so a browser-based wallet may require protojson over WebSocket
Memory Protection
The unsealed root key must be held in a hardened memory cell resistant to dumps, page swaps, and hibernation.
- Current: Using the
memsafecrate as an interim solution - Planned: Custom implementation based on
mlock(Unix) andVirtualProtect(Windows)