From ac121eed67bf2beaeaf95fe20fa5d2304c858230 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kyle=20=F0=9F=90=86?= Date: Tue, 7 Jul 2026 09:01:01 -0400 Subject: [PATCH] docs: reflect the wisp arc (on-box OPRF relay, NIP-42 auth, relay discovery) --- README.md | 2 +- docs/architecture.md | 21 ++++++++++++++++----- docs/deployment.md | 6 ++++-- docs/multi-node-sync.md | 5 +++-- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 22c3dcc..5f9800f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ keep-node turns a small Linux box into a private security appliance. Each node r Part of the [Keep](https://github.com/privkeyio/keep) ecosystem; the node daemon, vault, and threshold signing are reused from [`keep`](https://github.com/privkeyio/keep) (`keep-web`, `keep-core`). -> **Status: MVP in progress, developed and CI-tested entirely in NixOS VMs (no hardware).** Built and validated so far: Vaultwarden + keep-web on a LUKS volume gated at boot, either by a TPM seal (default) or, opt-in, by a **threshold-OPRF quorum** reconstructed from a live keep relay + a second holder (tested end to end with the real `keep` binary); **opt-in measured boot** (Lanzaboote UKI, so the seal binds a real PCR 11); and **multi-node active/standby HA for the vault**, running over a real **`nvpn` encrypted mesh** between nodes (nostr-vpn, boringtun userspace WireGuard, peer-authenticated) , a shared JWT signing key, Litestream WAL streaming of the SQLite DB, attachment/Send file replication, a replication-lag health signal, and crash-then-promote failover, all covered by two-node nixosTests with no relay. Still ahead: the **phone holder** and QR onboarding (the `keep-android` app; tests use a keep holder as its stand-in), moving the quorum to **2-of-3**, internet **NAT traversal** for the mesh (nvpn's Nostr discovery + the bundled `wisp` relay), and running on **real hardware** (TPM 2.0 + secure element on an SBC). +> **Status: MVP in progress, developed and CI-tested entirely in NixOS VMs (no hardware).** Built and validated so far: Vaultwarden + keep-web on a LUKS volume gated at boot, either by a TPM seal (default) or, opt-in, by a **threshold-OPRF quorum** reconstructed over privkey's own **`wisp`** relay + a second holder (tested end to end with the real `keep` binary, including with NIP-42 relay auth required); **opt-in measured boot** (Lanzaboote UKI, so the seal binds a real PCR 11); and **multi-node active/standby HA for the vault**, running over a real **`nvpn` encrypted mesh** between nodes (nostr-vpn, boringtun userspace WireGuard, peer-authenticated) , a shared JWT signing key, Litestream WAL streaming of the SQLite DB, attachment/Send file replication, a replication-lag health signal, and crash-then-promote failover, all covered by two-node nixosTests with no relay. Still ahead: the **phone holder** and QR onboarding (the `keep-android` app; tests use a keep holder as its stand-in), moving the quorum to **2-of-3**, internet **NAT traversal** for the mesh (nvpn's Nostr discovery + the bundled `wisp` relay), and running on **real hardware** (TPM 2.0 + secure element on an SBC). ## Features diff --git a/docs/architecture.md b/docs/architecture.md index f461b13..e0893f9 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -18,6 +18,8 @@ appliance is assembled from a few NixOS modules under `nixos/`, wired together b attachment/Send file replication, a replication-lag health signal, and crash-then-promote failover. - `mesh.nix`, the encrypted node-to-node transport (nostr-vpn's `nvpn`, boringtun userspace WireGuard) that replication rides between nodes. +- `wisp.nix`, the opt-in on-box Nostr relay (`keepNode.wisp`), bound to the mesh interface, that the + threshold-OPRF quorum and (opt-in) relay-based peer discovery coordinate over. - `measured-boot.nix`, opt-in Lanzaboote UKI boot so the seal binds a real measured-boot PCR. - `ingress.nix`, an opt-in TLS reverse proxy with brute-force protection for direct HTTPS access. @@ -103,9 +105,15 @@ distinct from the signing sessions, described in the next chapter. ### The relay (wisp) -Nostr coordination (FROST, the bunker, the unlock session, peer discovery) needs a relay. -The design bundles `wisp`, a lightweight Nostr relay, so a deployment does not depend on -third-party public relays. The relay is untrusted infrastructure: it stores and forwards +Nostr coordination (FROST, the bunker, the unlock session, peer discovery) needs a relay. Keep Node +bundles `wisp`, privkey's own lightweight Nostr relay, so a deployment does not depend on third-party +public relays. It is the relay the threshold-OPRF quorum actually coordinates over: the `oprf-unlock` +tests run the real `keep` quorum against wisp (dogfooding it in place of a stand-in relay), including +with **NIP-42 authentication required** , `keep` authenticates automatically and an unauthenticated +client is refused. Run on-box via the opt-in `keepNode.wisp` module, it binds to the mesh interface +only, so it is reachable over the encrypted mesh and refused on the LAN/underlay. + +The relay is untrusted infrastructure: it stores and forwards ciphertext only, never plaintext, and holds at most one key share, below the quorum threshold, so a compromised relay still cannot decrypt or sign. @@ -131,8 +139,11 @@ The `mesh-replication` test drives this end to end , replicate, propagate a dele active, promote the standby , over a real mesh with no relay. Replicas only ever exchange the application state above the LUKS layer, re-encrypted under each node's own volume, never plaintext or a quorum-threshold set of shares, and the mesh authenticates peers (npub roster) and encrypts the -hop (WireGuard). Internet NAT traversal for the mesh (nvpn's Nostr discovery + the bundled relay) -is a deployment concern beyond the VM. +hop (WireGuard). Relay-based peer discovery , nodes learning each other's endpoints over a wisp relay +instead of static config , is implemented as the opt-in `keepNode.mesh.discovery` mode and proven in +the `mesh-discovery` test (two nodes form the mesh with no static endpoints). Full symmetric-NAT +traversal (hole-punching over the relay's ephemeral channel) is the remaining internet-deployment +piece beyond the VM. ## Boot flow diff --git a/docs/deployment.md b/docs/deployment.md index 4f3dd78..7b5609a 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -42,8 +42,10 @@ runtime path , **never** as a Nix-path literal, or the secret key lands in the w ## 2. Author each node's mesh roster -The mesh uses **static endpoints** (no relay discovery): each node lists its own advertised endpoint -and every peer's npub + endpoint. Set the same `networkId` on all of them. +This how-to uses **static endpoints**: each node lists its own advertised endpoint and every peer's +npub + endpoint. Set the same `networkId` on all of them. (An opt-in `keepNode.mesh.discovery` mode +instead learns peer endpoints over a wisp relay , the `mesh-discovery` test proves it , for nodes with +dynamic addresses; static endpoints are the simplest to start with.) ```nix # node A's configuration.nix (node B is symmetric, with A in its peers) diff --git a/docs/multi-node-sync.md b/docs/multi-node-sync.md index 7f9c881..7fcbb61 100644 --- a/docs/multi-node-sync.md +++ b/docs/multi-node-sync.md @@ -31,8 +31,9 @@ > `keep-node-vault-lag-check` on the standby fails once the received heartbeat is older than the > threshold, so an idle-but-in-sync standby reads healthy while a stalled/partitioned one is surfaced. > Still to come for M1: moving the quorum to **2-of-3** and Keep-state-over-`wisp` replication. -> (Internet NAT traversal for the mesh, via nvpn's Nostr discovery + the bundled `wisp` relay, is a -> deployment concern beyond the VM.) +> (Relay-based peer discovery over `wisp` is implemented as the opt-in `keepNode.mesh.discovery` mode +> and tested (`mesh-discovery`); full symmetric-NAT traversal for internet deployment is the remaining +> piece beyond the VM.) > This chapter inventories Vaultwarden's state and the constraints that design has to respect. Vaultwarden (1.36.x here) keeps its state under one data directory, the FROST-gated LUKS