Expand description
Phase 34 — Nodes: the protocol, the hub’s registry and the node’s config.
A hub is an unv-server holding the vault. A node is an agent on
another host that observes, and when told to, applies config files derived
from that vault. This module is everything both ends must agree on; the
filesystem side of an apply is in crate::nodes_apply.
§What a node is, and is not
A node never holds a vault key and never holds a scoped read token. Its identity is an Ed25519 key it generated itself. The hub stores the public half at enrollment and checks a signature on every request, so the hub pins the node’s key the way the node pins the hub’s certificate (ADR-0140 explains why this is request signing and not client certificates).
§State is not in the vault
The registry is nodes.json beside pools.json. Heartbeats arrive every
minute; putting them through save_vault would grow the audit chain without
bound (the read-event and pool-cursor mistake, a third time) and turn every
beat into a compare-and-swap that can conflict with a human’s edit.
§Rendered content never touches disk on the hub
The registry holds hashes, never file content: a rendered wg0.conf
contains a private key and nodes.json is not encrypted.
Structs§
- Apply
Result - The outcome of one apply, reported on the next beat. The hub turns each into an audit row; heartbeats themselves write nothing.
- Approval
Record - A request for a human to say yes to these exact bytes for this node target. Holds hashes and snapshot numbers, never content: the content is in the config history, where the human reads it as a diff.
- Approval
Token - What the hub signs when a human approves. Canonical JSON is signed as text, so there is nothing to re-serialise on the verifying side.
- Approver
- A device whose signature counts as the owner’s approval (Phase 37.1, ADR-0147).
- Beat
- The heartbeat. No IP address: the hub has the socket where it needs one.
- Beat
Reply - Drift
Event - A change the node saw between two beats (hub down, or locked).
- Host
Info - Listen
Info - How a hub reaches a node that listens instead of dialling: its address and the SHA-256 of the TLS certificate it generated for that. Recorded at enrollment, over the channel the one-time token already authenticates, and pinned from then on.
- Node
Config - Node
Record - Node
Store - Pending
Note - A held push, as told to the node.
- Signed
Approval - Target
- One
[[target]]in the node’s config. Everything executable is here, on this host, written by the operator. Nothing the hub sends can add to it. - Target
Report - One target as the node reports it. Hash and state only; never content.
- Target
Status - What the hub knows about one target on one node.
Enums§
Constants§
- APPROVAL_
TTL_ SECS - How long an approved push stays valid, and so how long its signed token does.
- ENROLL_
TTL_ SECS - How long an enrollment token lives unless the operator asks otherwise.
- EXPORTERS
- LOCKED
- The error
record_beat’sdesiredcallback returns when the hub cannot render at all because the vault is locked. It is not a refusal: the target is simplyunknownuntil the hub is unlocked again. - MAX_
CLOCK_ SKEW_ SECS - A signed request whose timestamp is further than this from the hub’s clock is refused. Replays inside the window are stopped by the strictly increasing timestamp, not by the window.
- MAX_
TARGETS - Targets one node may declare. A bound, so a hostile beat cannot grow the registry without limit.
- PENDING_
TTL_ SECS - How long a request waits for a human before it lapses.
Functions§
- approver_
seed - The owner device’s approval seed in
dir/approver.key, made on first use (0600). One file per machine, shared by the CLI and the desktop app. - default_
approver_ dir - Where the CLI keeps the device key: the directory the desktop app uses too.
- derive_
status - The one place a target’s status word is decided.
- generate_
hub_ seed - A fresh hub approval seed, hex.
- generate_
identity - A fresh node identity as
(seed_hex, public_key_hex). - hub_
public - The public key for a hub’s approval-signing seed.
- hub_
seed - The hub’s approval-signing seed, created on first use. It lives in the vault’s
own encrypted
vault_meta(as the unique-ID registry’s secrets do), so a copy ofnodes.jsonis not enough to forge an approval. - key_
fingerprint - SHA-256 of the raw public key, as hex. What the operator compares.
- sign_
approval - sign_
device_ approval - Signs an approval on the owner’s device for exactly one request. The lifetime
is the hub’s own (
APPROVAL_TTL_SECS); the nonce is fresh. - sign_
hub_ request - As
sign_request, for a hub talking to a node that listens (Phase 34.1). - sign_
request - Signs a request. Returns the signature as hex.
- valid_
exporter - Exporter names a target may use.
compose-envis the.envCompose substitutes from, which is a separate file and therefore a separate target. True for a built-in exporter name or a stack adapter’s (Phase 38). - valid_
id ^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$- verify_
approval - Checks a token the way a node does before it writes: the signature is the hub’s, it names this node, this target and exactly this hash, and it has not expired. Returns the token so the caller can remember its nonce.
- verify_
hub_ request - As
verify_request, for the hub’s requests. - verify_
request - True only for a valid signature by
public_hexover exactly this request.