Commit graph hyperhive/swarm-controller/README.md
Author SHA1 Message Date
atlas
b7c5067f79 deploy: move the controller's socket and credentials out of swarm.controller
`swarm.*` is what a hive needs to be a *client* of the swarm. For the
controller that is the quick-links it publishes, the client id it is
registered under, how stale a snapshot may read, and its packages. Where
its socket lives and where the three credentials it reads sit are
decisions of the machine running it, so socketPath, forgeTokenFile,
authBridgeUrl and queue.clientSecretFile move to
`deploy.swarm-controller.*`.

⚠️ `deploy.swarm-controller`, not `deploy.hive-controller`. Both exist on
main — the latter is hive-c0re's namespace, where `tls.*` lives — and a
definition on the wrong one lands on a live unrelated option instead of
failing. The shim table is anchored on both sides for that reason.

`queue.clientSecretFile` is a nested field, not a top-level option: it
moves out of `queue` and leaves natsUrl and tokenEndpoint behind, so the
far side needs `deploy.swarm-controller.queue.clientSecretFile`. That is
the `forge.sso` split shape, applied again rather than reinvented — the
endpoints a client dials are swarm-wide, the secret beside them is a path
on one host.

local-defaults.nix set that secret from INSIDE
`config.services.hyperhive.swarm = { ... }`, where a bare `controller.`
prefix means `swarm.controller`. Left there it would still resolve —
through the rename — and warn on every evaluation of a single-host swarm,
which is the same defect an earlier slice fixed for the matrix module. It
moves out into its own `deploy` statement beside the controller's
`enable`. `queue.natsUrl` stays bare: it is a stayer.

swarm-ui.nix read `socketPath` through its own `controllerCfg` alias.
With that repointed the binding had no reader left, so it goes. Unlike
the dead `natsCfg` an earlier slice removed, this one was live until this
commit — the move orphaned it. `deployCfg` was already bound there.

Prose that named a moved option by its full path is requalified: the
assertion message for the queue secret (operator-facing, and inside
`config` where an options-block sweep would miss it) and
`swarm-controller/README.md`'s socket-path line. Left bare on purpose:
`docs/swarm/README.md` and the README's own "never point `socketPath` at
a directory that carries anything else" both name the FEATURE, not a
path.

module-eval configures a hive through all four OLD paths and asserts a
rendered effect for EACH of them — the unit's socket env, its auth-bridge
env, and both credential paths in `LoadCredential`. Asserting all four
rather than one means a rename that resolves but stops reaching the
module is caught per-option, not only where a single assertion happens to
look.
2026-09-07 14:24:52 +02:00
atlas
39b95c2ede treefmt: apply prettier
Pure `nix fmt` output from the commit before this one — no hand edits.
203 files: 52 md, 42 tsx, 32 js, 32 css, 21 ts, 13 html, 8 json, 3 mjs.

Reproduce with `nix develop -c nix fmt` on the parent commit; the result
should be byte-identical to this tree.

None of the 13 `.prettierignore` entries appears here — verified by
intersecting the changed-file list against the ignore file, with a
control proving the intersection finds a match when one exists.
2026-09-02 15:25:07 +02:00
atlas
d3b40da1c8 deploy: give every option an enable, and name the controller one
Two corrections from review, applied forward on this branch rather than
by rewriting it.

`deploy.<service>` was a bare bool, which makes
`deploy.forgejo = { enable; ci; }` unrepresentable -- the nested
CI-runner sub-option this namespace was designed around. Every entry is
now an attrset with an `enable`, so a second per-host deployment
decision becomes an ordinary addition rather than a migration.

`deploy.controller` is now `deploy.swarm-controller`, consistent with
`deploy.swarm-ui`, which was introduced in the same commit.

89 references rewritten across 24 files -- nix, Rust, docs, and the
repo's own CLAUDE.md.

The prefix-anchored sweep missed exactly one, and it was live code:
hive-tls.nix spells it `hyperhiveCfg.deploy.controller` -- the only
`hyperhiveCfg` prefix among 45 references. A suffix grep
(`\.deploy\.<name>`) finds it; a path-anchored one cannot, because the
head of a reference is whatever alias the reading file happens to bind.
2026-08-30 04:23:22 +02:00
atlas
0b7357d4b8 deploy: move the controller and swarm-ui toggles
One commit rather than two because they are not independent: the UI's
`enable` had the controller's as its literal default, so moving the
controller alone would leave the UI's default naming an option that no
longer exists.

The UI keeps that derivation in its new home — it is a view onto the
controller's state and reaches it over that daemon's unix socket, so the
host running the controller is the host that can serve it.

Three spellings had to move together for the UI, not one: the `default`,
the `defaultText` shown in the options doc, and the description prose
that names the old path in words. A grep for the option path finds the
first two.

The sweep also reached outside nix: `swarm-controller`'s crate README and
its `//!` module doc both named the option, as did this repo's own
CLAUDE.md and four pages under docs/. An option's name is API, and its
documentation lives wherever someone thought to write it down.
2026-08-30 04:23:22 +02:00
atlas
98d895cf9e docs(gateway): describe what is, not what changed
Per review: docs represent current state. Every "used to" / "no longer"
clause this branch introduced is gone — including the History section in
network.md, which was a whole subsection about a sync mechanism that
doesn't exist.

Where the removed clause was carrying a real constraint, the constraint
stays and is stated in the present tense instead of as a delta: nothing
narrows what the gateway's nginx can reach except the directory
permissions in front of a socket, and nothing bounds `ReloadGatewayNginx`
except the hard-coded unit name. Those read as rules now rather than as
the story of how they came to be rules.
2026-08-11 18:09:51 +02:00
atlas
07852cabc1 feat(3088): move the gateway's nginx + dnsmasq onto the host
The gateway's nginx + dnsmasq no longer run in their own nspawn container.
`nix/host-modules/hive-gateway/default.nix` loses the
`containers.hive-gateway` wrapper and everything that existed only to punch
holes in it: `privateNetwork = false`, `CAP_NET_ADMIN`, five bind mounts,
its own `stateVersion`, `networking.firewall.enable = false`,
`networking.resolvconf.enable = false`, and the `hive-gateway-resolv`
path+service pair. 465 -> 303 lines.

The container never bought isolation here. It shared the host netns by
necessity — nginx binds the host's :80/:443, dnsmasq answers on the bridge —
so each of those settings was undoing a boundary the gateway could not
afford in the first place.

Four things made it more than a deletion, none of them visible in the nix
diff:

- The self-signed cert service also imports the hive CA leaf, so removing it
  with the container would have left nginx naming a missing cert file, which
  it refuses to load at all.
- The nginx reload is a hive-priv verb. It still needs root, but no longer
  for the reason its doc gave, and `--machine=` was both transport and
  scope — so the unit name is now hard-coded in the helper as the
  containment.
- The lifecycle verb named a container that stops existing.
- `journalctl -M hive-gateway` had no machine to enter.

Per the operator's ruling, the operator verb keeps working and agents lose
it. `InfraContainer` answered three questions that used to share an answer;
it now splits into `name()` (identity), `target()` (Container vs HostUnit),
`service_unit()` (the systemd unit), and `agent_restartable()`, which the
MCP restart path checks before the capability so the refusal cannot read as
"ask for infra_admin". `SIBLING_CONTAINERS` drops the gateway — it gates the
requests that name a container as a string — while `FromStr` still accepts
it, because that answers what a name is, not who may act on it. The
dashboard's gateway journal reads host journald filtered to `nginx.service`.

Prose was corrected where it only named a location, and re-argued where the
container was doing security work: a `0666` per-agent socket was safe
because only the gateway container had the directory bind-mounted. There is
no mount now, so the directory permissions are the whole of the access
control — the constraint holds, its mechanism doesn't.

Gate: nix fmt / clippy --all-targets -D warnings / cargo test all clean (710
tests); hivectl-cli.md regenerated from the clap tree. The nix eval was run
in both TLS shapes at this commit: every delta in the rendered
virtualHosts is one of the three intended path moves, dnsmasq settings are
byte-identical, and the absence probe flips true -> false with bindMounts
emptied.
2026-08-11 18:01:03 +02:00
atlas
fb51006717 fix(nix): swarm-controller is not a core binary, and it needed a README
Per review: daemonBins is the core stack, and it drives the bundle that
services.hyperhive.c0re.package points at -- so listing a swarm-scoped
service there would put it in every hive's closure when one hive in a
swarm runs it. It gets the same per-bin extractor, bound on its own, the
way hivectl already is.

The crate also had no README while every other one does. Both misses are
the same shape: adding a thing without updating what describes the set of
things.
2026-08-05 14:59:37 +02:00