refactor(#2862): swarm- prefix for the snapshot store

mara, in preparation for the swarm tier: the store is a swarm-level
role, not a hive one, so hive- was misleading about which tier it
belongs to. Module, units, syslog identifier, log lines and docs all
move to swarm-snapshot-store.

Also moved the option under services.hyperhive.swarm.snapshotStore, to
sit with swarm.peers and swarm.wireguard rather than dangling off the
top level. That is a judgement call beyond the literal rename — flagged
on the PR, and cheap precisely now: the option has never shipped, so
there is no deployment to migrate, whereas doing it after a release
would be a breaking change for no new benefit.
This commit is contained in:
atlas 2026-07-31 18:49:25 +02:00 committed by mara
commit 70bcdb5463
3 changed files with 17 additions and 17 deletions

View file

@ -19,7 +19,7 @@ assume:
## Enabling it ## Enabling it
```nix ```nix
services.hyperhive.snapshotStore = { services.hyperhive.swarm.snapshotStore = {
enable = true; enable = true;
path = "/var/lib/hyperhive-snapshots"; # must be on btrfs path = "/var/lib/hyperhive-snapshots"; # must be on btrfs
port = 51821; port = 51821;

View file

@ -19,9 +19,9 @@
./hive-matrix.nix ./hive-matrix.nix
./hive-network.nix ./hive-network.nix
./hive-priv.nix ./hive-priv.nix
./hive-snapshot-store.nix
./hive-tls.nix ./hive-tls.nix
./otel.nix ./otel.nix
./swarm-snapshot-store.nix
./swarm-wireguard.nix ./swarm-wireguard.nix
./swarm.nix ./swarm.nix
]; ];

View file

@ -1,4 +1,4 @@
# hive-snapshot-store — the swarm's `btrfs receive` endpoint. Hives push # swarm-snapshot-store — the swarm's `btrfs receive` endpoint. Hives push
# agent snapshots here over the existing WireGuard mesh; a destination # agent snapshots here over the existing WireGuard mesh; a destination
# hive later pulls one back to complete a migration. Only the receive # hive later pulls one back to complete a migration. Only the receive
# half exists today — the pull side needs an authorisation model for # half exists today — the pull side needs an authorisation model for
@ -25,7 +25,7 @@
... ...
}: }:
let let
cfg = config.services.hyperhive.snapshotStore; cfg = config.services.hyperhive.swarm.snapshotStore;
wgCfg = config.services.hyperhive.swarm.wireguard; wgCfg = config.services.hyperhive.swarm.wireguard;
# `swarm.wireguard.address` carries a prefix ("10.100.0.1/24") because # `swarm.wireguard.address` carries a prefix ("10.100.0.1/24") because
@ -51,7 +51,7 @@ let
# name must match [A-Za-z0-9_-]+ exactly --- no slash, no dot, so no # name must match [A-Za-z0-9_-]+ exactly --- no slash, no dot, so no
# traversal and no absolute path can survive it. The root is ours; # traversal and no absolute path can survive it. The root is ours;
# the leaf is checked against a whitelist charset before it is joined. # the leaf is checked against a whitelist charset before it is joined.
receiveScript = pkgs.writeShellScript "hive-snapshot-receive" '' receiveScript = pkgs.writeShellScript "swarm-snapshot-receive" ''
set -euo pipefail set -euo pipefail
# Read exactly the header line, leaving the byte stream untouched # Read exactly the header line, leaving the byte stream untouched
@ -59,12 +59,12 @@ let
# buffer ahead, which is why the header is a line and not a # buffer ahead, which is why the header is a line and not a
# fixed-width record. # fixed-width record.
if ! read -r keyword agent; then if ! read -r keyword agent; then
echo "hive-snapshot-store: peer ''${REMOTE_ADDR:-?} closed before sending a header" >&2 echo "swarm-snapshot-store: peer ''${REMOTE_ADDR:-?} closed before sending a header" >&2
exit 1 exit 1
fi fi
if [ "$keyword" != "agent" ]; then if [ "$keyword" != "agent" ]; then
echo "hive-snapshot-store: peer ''${REMOTE_ADDR:-?} sent a bad header keyword" >&2 echo "swarm-snapshot-store: peer ''${REMOTE_ADDR:-?} sent a bad header keyword" >&2
exit 1 exit 1
fi fi
@ -74,7 +74,7 @@ let
# of bad characters. # of bad characters.
case "$agent" in case "$agent" in
"" | *[!A-Za-z0-9_-]*) "" | *[!A-Za-z0-9_-]*)
echo "hive-snapshot-store: peer ''${REMOTE_ADDR:-?} sent an invalid agent name" >&2 echo "swarm-snapshot-store: peer ''${REMOTE_ADDR:-?} sent an invalid agent name" >&2
exit 1 exit 1
;; ;;
esac esac
@ -88,12 +88,12 @@ let
# store exists to serve. # store exists to serve.
mkdir -p "$dest" mkdir -p "$dest"
echo "hive-snapshot-store: receiving agent=$agent from ''${REMOTE_ADDR:-?}" >&2 echo "swarm-snapshot-store: receiving agent=$agent from ''${REMOTE_ADDR:-?}" >&2
exec ${pkgs.btrfs-progs}/bin/btrfs receive "$dest" exec ${pkgs.btrfs-progs}/bin/btrfs receive "$dest"
''; '';
in in
{ {
options.services.hyperhive.snapshotStore = { options.services.hyperhive.swarm.snapshotStore = {
enable = lib.mkOption { enable = lib.mkOption {
type = lib.types.bool; type = lib.types.bool;
default = false; default = false;
@ -137,7 +137,7 @@ in
{ {
assertion = wgCfg.enable; assertion = wgCfg.enable;
message = '' message = ''
services.hyperhive.snapshotStore.enable requires services.hyperhive.swarm.snapshotStore.enable requires
services.hyperhive.swarm.wireguard.enable --- the mesh is the services.hyperhive.swarm.wireguard.enable --- the mesh is the
store's transport AND its authentication (cryptokey routing store's transport AND its authentication (cryptokey routing
binds a peer's source address to its public key). Without it binds a peer's source address to its public key). Without it
@ -148,7 +148,7 @@ in
{ {
assertion = wgCfg.address != ""; assertion = wgCfg.address != "";
message = '' message = ''
services.hyperhive.snapshotStore.enable requires services.hyperhive.swarm.snapshotStore.enable requires
services.hyperhive.swarm.wireguard.address to be set --- the services.hyperhive.swarm.wireguard.address to be set --- the
receiver binds to this host's mesh address, and refuses to receiver binds to this host's mesh address, and refuses to
fall back to a wildcard. fall back to a wildcard.
@ -188,7 +188,7 @@ in
# Accept=yes gives one service instance per connection and sets # Accept=yes gives one service instance per connection and sets
# $REMOTE_ADDR for the handler --- which is how the receiver knows # $REMOTE_ADDR for the handler --- which is how the receiver knows
# which peer it is talking to. # which peer it is talking to.
systemd.sockets.hive-snapshot-store = { systemd.sockets.swarm-snapshot-store = {
description = "hyperhive swarm snapshot store receiver socket"; description = "hyperhive swarm snapshot store receiver socket";
wantedBy = [ "sockets.target" ]; wantedBy = [ "sockets.target" ];
socketConfig = { socketConfig = {
@ -202,13 +202,13 @@ in
# boundary: a process holding CAP_SYS_ADMIN can call mount(2) and # boundary: a process holding CAP_SYS_ADMIN can call mount(2) and
# undo the namespace these directives set up. The real boundary is # undo the namespace these directives set up. The real boundary is
# the deployment --- see docs/snapshot-store.md. # the deployment --- see docs/snapshot-store.md.
systemd.services."hive-snapshot-store@" = { systemd.services."swarm-snapshot-store@" = {
description = "hyperhive swarm snapshot store receiver"; description = "hyperhive swarm snapshot store receiver";
after = [ "hive-snapshot-store.socket" ]; after = [ "swarm-snapshot-store.socket" ];
requires = [ "hive-snapshot-store.socket" ]; requires = [ "swarm-snapshot-store.socket" ];
serviceConfig = { serviceConfig = {
ExecStart = receiveScript; ExecStart = receiveScript;
SyslogIdentifier = "hive-snapshot-store"; SyslogIdentifier = "swarm-snapshot-store";
# StandardInput=socket wires the accepted connection to stdin, # StandardInput=socket wires the accepted connection to stdin,
# which is what the handler reads the header + stream from. # which is what the handler reads the header + stream from.
StandardInput = "socket"; StandardInput = "socket";