diff --git a/README.md b/README.md index 669e8d7d..c59dd42e 100644 --- a/README.md +++ b/README.md @@ -131,12 +131,17 @@ covers spreading a swarm across hosts. hyperhive.nixosModules.default # the whole host stack, one import { services.hyperhive = { - deploy.singleHostSwarm = true; # every swarm service on this machine - deploy.hive-controller.enable = true; # …and a hive to run agents on - - swarm.domain = "hive.example"; # the swarm's DNS domain - deploy.hive-controller.hiveName = "pr1ma"; # this hive's label in it - swarm.hives.pr1ma = { }; # the directory: just us + swarm = { + domain = "myswarm.example"; # the swarm's DNS domain + hives.firsthive = { }; # the directory: just us + }; + deploy = { + singleHostSwarm = true; # every swarm service on this machine + hive-controller = { + enable = true; # …and a hive to run agents on + hiveName = "firsthive"; # this hive's label in it + }; + }; }; # ... the rest of your host config diff --git a/docs/1-getting-started/nixos.md b/docs/1-getting-started/nixos.md index d66557b5..411c9000 100644 --- a/docs/1-getting-started/nixos.md +++ b/docs/1-getting-started/nixos.md @@ -68,11 +68,17 @@ The smallest working config, as in the README's ```nix services.hyperhive = { - deploy.singleHostSwarm = true; - deploy.hive-controller.enable = true; - swarm.domain = "hive.example"; - deploy.hive-controller.hiveName = "pr1ma"; - swarm.hives.pr1ma = { }; + swarm = { + domain = "myswarm.example"; + hives.firsthive = { }; + }; + deploy = { + singleHostSwarm = true; + hive-controller = { + enable = true; + hiveName = "firsthive"; + }; + }; }; ``` diff --git a/docs/1-getting-started/setup.md b/docs/1-getting-started/setup.md index a608cea7..9df438de 100644 --- a/docs/1-getting-started/setup.md +++ b/docs/1-getting-started/setup.md @@ -119,7 +119,7 @@ an agent's config arrive as PRs you merge. → [`2-handbook/agent-lifecycle/agen To create an agent yourself instead, use the swarm UI, or: ```bash -swarmctl agent create iris --hive pr1ma +swarmctl agent create iris --hive firsthive ``` The controller provisions iris's identity, forge user and config repo diff --git a/docs/2-handbook/swarm/README.md b/docs/2-handbook/swarm/README.md index 5e2d0ffc..1bd28715 100644 --- a/docs/2-handbook/swarm/README.md +++ b/docs/2-handbook/swarm/README.md @@ -503,17 +503,21 @@ as the forge or the secret store is down. A restart isn't needed to recover. ```nix services.hyperhive = { - swarm.domain = "example.com"; # required — the swarm's DNS domain - deploy.hive-controller.hiveName = "pr1ma"; # required on a hive host — this hive's label in it - swarm.name = "constellat1on"; # shared swarm display name (optional) + swarm = { + domain = "myswarm.example"; # required — the swarm's DNS domain + name = "myswarm"; # shared swarm display name (optional) - # required — the directory, identical on every host in the swarm. - # Names only: each entry's `domain` defaults to .. - swarm.hives = { - pr1ma = { }; # this host, per hiveName - lab = { }; # a second hive - edge = { domain = "edge.elsewhere.example"; }; # addressed off-convention + # required — the directory, identical on every host in the swarm. + # Names only: each entry's `domain` defaults to .. + hives = { + firsthive = { }; # this host, per hiveName + secondhive = { }; # a second hive + thirdhive.domain = "thirdhive.elsewhere.example"; # addressed off-convention + }; }; + + # required on a hive host — this hive's label in the directory + deploy.hive-controller.hiveName = "firsthive"; }; ``` @@ -536,9 +540,9 @@ names the missing hive. Peers are _every entry but this host's_, so a directory without this host would make every hive a peer, itself included. ``` -# hive A # hive B -deploy.hive-controller.hiveName = "pr1ma"; deploy.hive-controller.hiveName = "edge"; -swarm.hives = { … }; swarm.hives = { … }; # byte-identical +# hive A # hive B +deploy.hive-controller.hiveName = "firsthive"; deploy.hive-controller.hiveName = "secondhive"; +swarm.hives = { … }; swarm.hives = { … }; # byte-identical ``` One entry per hive means two hosts can't hold _different_ facts about the @@ -548,7 +552,7 @@ same third hive, such as a stale endpoint. is names only. Set it only for a hive addressed by something else. This hive's own `services.hyperhive.domain` comes from its entry; it drives `HYPERHIVE_HIVE_DOMAIN` in every container so agents can form qualified -labels (`iris@pr1ma.example.com`). Setting `services.hyperhive.domain` +labels (`iris@firsthive.myswarm.example`). Setting `services.hyperhive.domain` directly overrides the directory entry, with a deprecation warning: the option is local to this host while every host shares the directory, so a value written only here is invisible to the rest of the swarm. @@ -608,42 +612,44 @@ wg pubkey < /etc/wireguard/hive.key # → share this with peer operators #### Config example (two hives) ```nix -# hive A (pr1ma.example.com, mesh IP 10.100.0.1) +# hive A (firsthive.myswarm.example, mesh IP 10.100.0.1) services.hyperhive = { deploy.wireguard = { - enable = true; + enable = true; privateKeyFile = "/etc/wireguard/hive.key"; - address = "10.100.0.1/24"; - listenPort = 51820; # optional, default 51820 + address = "10.100.0.1/24"; + listenPort = 51820; # optional, default 51820 }; # The same `hives` attrset both hosts hold — mesh fields included, # since "where this hive can be dialled" is a fact about that hive. swarm.hives = { - pr1ma = { - domain = "pr1ma.example.com"; + firsthive = { + domain = "firsthive.myswarm.example"; wireguardPublicKey = "base64keyA="; - wireguardEndpoint = "198.51.100.1:51820"; - wireguardAddress = "10.100.0.1/32"; + wireguardEndpoint = "198.51.100.1:51820"; + wireguardAddress = "10.100.0.1/32"; }; - edge = { - domain = "edge.corp"; + secondhive = { + domain = "secondhive.elsewhere.example"; wireguardPublicKey = "base64keyB="; - wireguardEndpoint = "203.0.113.42:51820"; - wireguardAddress = "10.100.0.2/32"; + wireguardEndpoint = "203.0.113.42:51820"; + wireguardAddress = "10.100.0.2/32"; }; }; }; -# hive B (edge.corp, mesh IP 10.100.0.2) +# hive B (secondhive.elsewhere.example, mesh IP 10.100.0.2) services.hyperhive = { deploy.wireguard = { - enable = true; + enable = true; privateKeyFile = "/etc/wireguard/hive.key"; - address = "10.100.0.2/24"; + address = "10.100.0.2/24"; }; - swarm.hives = { /* … identical to hive A's … */ }; + swarm.hives = { + # … identical to hive A's … + }; }; ``` diff --git a/docs/2-handbook/swarm/services.md b/docs/2-handbook/swarm/services.md index 45f67147..379a0e67 100644 --- a/docs/2-handbook/swarm/services.md +++ b/docs/2-handbook/swarm/services.md @@ -171,8 +171,10 @@ They derive together: a store with no UI is unreadable and a UI with no store is empty. To run one without the other, set it directly: ```nix -services.hyperhive.deploy.victoriametrics.enable = true; -services.hyperhive.deploy.grafana.enable = false; +services.hyperhive.deploy = { + victoriametrics.enable = true; + grafana.enable = false; +}; ``` ⚠️ **This starts a database that grows for as long as the swarm runs.** diff --git a/docs/4-implementation/networking/gateway.md b/docs/4-implementation/networking/gateway.md index 68fe9f49..d0f79ad6 100644 --- a/docs/4-implementation/networking/gateway.md +++ b/docs/4-implementation/networking/gateway.md @@ -153,9 +153,13 @@ Only the swarm's forge host runs it (`services.hyperhive.deploy.forgejo.enable`, ```nix # The SSH port is swarm-wide: every hive dials it. The HTTP port and whether # THIS host opens them in its firewall are this host's deployment decisions. -services.hyperhive.swarm.forge.sshPort = 2222; # default — git-over-SSH, off the host's openssh on 22 -services.hyperhive.deploy.forgejo.httpPort = 3000; # default — outside hyperhive's 7000 / 8100-8999 -services.hyperhive.deploy.forgejo.openFirewall = false; # default +services.hyperhive = { + swarm.forge.sshPort = 2222; # default — git-over-SSH, off the host's openssh on 22 + deploy.forgejo = { + httpPort = 3000; # default — outside hyperhive's 7000 / 8100-8999 + openFirewall = false; # default + }; +}; ``` `sshPort` serves `git clone/push/pull` over SSH (`git@:owner/repo.git` with `-p 2222`). SSH goes straight to Forgejo, not through nginx. diff --git a/docs/4-implementation/networking/network.md b/docs/4-implementation/networking/network.md index 9ae95ae8..d4f3a479 100644 --- a/docs/4-implementation/networking/network.md +++ b/docs/4-implementation/networking/network.md @@ -92,10 +92,14 @@ listener sits on `bridgeIp`, on the host's bridge interface. ```nix { services.hyperhive = { - deploy.hive-controller.enable = true; - deploy.hive-controller.hiveName = "pr1ma"; - swarm.domain = "darkest.space"; - swarm.hives.pr1ma = { }; # -> domain = pr1ma.darkest.space + swarm = { + domain = "myswarm.example"; + hives.firsthive = { }; # -> domain = firsthive.myswarm.example + }; + deploy.hive-controller = { + enable = true; + hiveName = "firsthive"; + }; # network.bridgeIp = "10.42.0.1"; # default }; } diff --git a/docs/4-implementation/networking/snapshot-store.md b/docs/4-implementation/networking/snapshot-store.md index 216df7a4..6711150d 100644 --- a/docs/4-implementation/networking/snapshot-store.md +++ b/docs/4-implementation/networking/snapshot-store.md @@ -19,17 +19,19 @@ assumptions: ## Enabling it ```nix -services.hyperhive.snapshotStore = { - enable = true; - path = "/var/lib/hyperhive-snapshots"; # must be on btrfs - port = 51821; -}; +services.hyperhive = { + snapshotStore = { + enable = true; + path = "/var/lib/hyperhive-snapshots"; # must be on btrfs + port = 51821; + }; -# The mesh is a hard requirement, and is asserted: -services.hyperhive.deploy.wireguard = { - enable = true; - address = "10.100.0.9/24"; - privateKeyFile = "/etc/wireguard/hive.key"; + # The mesh is a hard requirement, and is asserted: + deploy.wireguard = { + enable = true; + address = "10.100.0.9/24"; + privateKeyFile = "/etc/wireguard/hive.key"; + }; }; ``` diff --git a/docs/4-implementation/turn-loop/config.md b/docs/4-implementation/turn-loop/config.md index 7d688dbd..8f355b0b 100644 --- a/docs/4-implementation/turn-loop/config.md +++ b/docs/4-implementation/turn-loop/config.md @@ -135,8 +135,10 @@ single-host deploy, but are useful for multi-hive or custom-network setups. ```nix -services.hyperhive.agent.forge.url = "http://forge.example:3000"; # default: null -services.hyperhive.agent.matrix.url = "https://matrix.example"; # default: null +services.hyperhive.agent = { + forge.url = "http://forge.example:3000"; # default: null + matrix.url = "https://matrix.example"; # default: null +}; ``` **`services.hyperhive.agent.forge.url`** — base URL of the Forgejo instance. It scopes @@ -179,15 +181,19 @@ The harness installs Claude Code plugins before the serve loop opens. Three per-agent `agent.nix` options control this: ```nix -services.hyperhive.agent.claudeMarketplaces = [ # default - "anthropics/claude-plugins-official" - "${services.hyperhive.agent.packages.claude-plugins}" # hive's own local marketplace, registered as `hyperhive` -]; -services.hyperhive.agent.claudePlugins = [ # default - "skill-creator@claude-plugins-official" - "base@hyperhive" -]; -services.hyperhive.agent.claudePluginsAutoUpdate = false; # default +services.hyperhive.agent = { + claudeMarketplaces = [ + # default + "anthropics/claude-plugins-official" + "${services.hyperhive.agent.packages.claude-plugins}" # hive's own local marketplace, registered as `hyperhive` + ]; + claudePlugins = [ + # default + "skill-creator@claude-plugins-official" + "base@hyperhive" + ]; + claudePluginsAutoUpdate = false; # default +}; ``` - **`claudeMarketplaces`** — list of marketplace sources passed to @@ -248,10 +254,11 @@ programmatically and don't pass `--message-format json` themselves. ## API-key provider (`useApiKey` / `backendEnvironmentFile`) ```nix -services.hyperhive.agent.useApiKey = true; # default: false -services.hyperhive.agent.backendEnvironmentFile = - "/agents/myagent/harness/openrouter.env"; # default: null -services.hyperhive.agent.model = "anthropic/claude-3.5-sonnet"; # provider-specific model string +services.hyperhive.agent = { + useApiKey = true; # default: false + backendEnvironmentFile = "/agents/myagent/harness/openrouter.env"; # default: null + model = "anthropic/claude-3.5-sonnet"; # provider-specific model string +}; ``` Runs this agent's `claude` against an API-key provider (for example OpenRouter)