Skip to main content

Module nodes

Module nodes 

Source
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§

ApplyResult
The outcome of one apply, reported on the next beat. The hub turns each into an audit row; heartbeats themselves write nothing.
ApprovalRecord
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.
ApprovalToken
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.
BeatReply
DriftEvent
A change the node saw between two beats (hub down, or locked).
HostInfo
ListenInfo
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.
NodeConfig
NodeRecord
NodeStore
PendingNote
A held push, as told to the node.
SignedApproval
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.
TargetReport
One target as the node reports it. Hash and state only; never content.
TargetStatus
What the hub knows about one target on one node.

Enums§

Action
Something the hub asks a node to do.
AuthError

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’s desired callback returns when the hub cannot render at all because the vault is locked. It is not a refusal: the target is simply unknown until 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 of nodes.json is 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-env is the .env Compose 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_hex over exactly this request.