feat(#2862): push a snapshot to a peer hive's store over the mesh

Adds the caller the fd-passing machinery existed for: hivectl agent
<name> subvol snapshot push --peer <hive> resolves the peer, connects
to its snapshot store, writes the agent header, and hands the connected
socket to hive-priv, which runs btrfs send straight into it.

The split keeps the root helper ignorant. Everything that involves
knowing where a peer is, what the wire protocol looks like, and which
hive to trust happens in the unprivileged daemon; hive-priv only ever
receives an already-open descriptor. Once btrfs send starts, neither
process is in the data path, so a multi-gigabyte transfer costs no
per-byte work and survives a hive-c0re restart.

call_with_fd takes the descriptor by value and closes it as soon as the
kernel has it. A socket stays open until every copy closes, so holding
one back would leave the receiver waiting for an EOF that never comes:
btrfs receive blocks and this side reports success for a transfer the
peer never committed. Ownership makes that unrepresentable.

The peer's store port is a new swarm.peers.<domain>.snapshotStorePort
option rather than a constant matching the module default. A pushing
hive cannot read the receiver's configuration, so assuming 51821 would
push at a port nobody promised to listen on; absent, the push fails
naming the option. swarm_peers parses the mesh address the host module
has always rendered into HYPERHIVE_PEERS but nothing read.
This commit is contained in:
atlas 2026-07-31 21:40:34 +02:00 committed by mara
commit 282bbc3709
10 changed files with 546 additions and 9 deletions

View file

@ -620,10 +620,10 @@ pub enum SnapshotCmd {
/// Snapshot label passed to `subvol snapshot create --label`.
label: String,
},
/// Export a snapshot to a local file via `btrfs send` (the local-file
/// half of inter-hive migration transport; the cross-hive `ssh ...
/// btrfs receive` leg isn't wired up yet). Also useful standalone as a
/// point-in-time backup: a full send with no `--parent` produces a
/// Export a snapshot to a local file via `btrfs send` — the
/// local-file half of the inter-hive migration transport (`push`
/// is the network half). Also useful standalone as a point-in-time
/// backup: a full send with no `--parent` produces a
/// self-contained archive of the snapshot.
Send {
/// Snapshot label passed to `subvol snapshot create --label`.
@ -638,4 +638,27 @@ pub enum SnapshotCmd {
#[arg(long)]
dest: String,
},
/// Stream a snapshot to a peer hive's snapshot store over the
/// WireGuard mesh — the network half of the migration transport.
///
/// Nothing is staged locally: `btrfs send` writes straight into the
/// connection, so a multi-gigabyte agent needs no scratch space on
/// this host. The mesh is the authentication (cryptokey routing
/// binds the peer's address to its key), so there is no credential
/// to pass here.
Push {
/// Snapshot label passed to `subvol snapshot create --label`.
label: String,
/// Optional parent snapshot label for an incremental send
/// (`btrfs send -p`) — must be an existing, older snapshot of the
/// same agent, and must already be present on the receiver.
/// Omit for a full send.
#[arg(long)]
parent: Option<String>,
/// Peer hive domain, as declared in
/// `services.hyperhive.swarm.peers`. Its mesh address and
/// snapshot-store port are read from there.
#[arg(long)]
peer: String,
},
}