Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -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

Expand Down
21 changes: 16 additions & 5 deletions docs/architecture.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -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.

Expand DownExpand Up@@ -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.

Expand All@@ -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

Expand Down
6 changes: 4 additions & 2 deletions docs/deployment.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -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)
Expand Down
5 changes: 3 additions & 2 deletions docs/multi-node-sync.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand Down
Loading