docs: nest config examples and use neutral swarm/hive names
Closes #6146
This commit is contained in:
parent
af434e1374
commit
4c321f3d2d
9 changed files with 113 additions and 77 deletions
17
README.md
17
README.md
|
|
@ -131,12 +131,17 @@ covers spreading a swarm across hosts.
|
||||||
hyperhive.nixosModules.default # the whole host stack, one import
|
hyperhive.nixosModules.default # the whole host stack, one import
|
||||||
{
|
{
|
||||||
services.hyperhive = {
|
services.hyperhive = {
|
||||||
deploy.singleHostSwarm = true; # every swarm service on this machine
|
swarm = {
|
||||||
deploy.hive-controller.enable = true; # …and a hive to run agents on
|
domain = "myswarm.example"; # the swarm's DNS domain
|
||||||
|
hives.firsthive = { }; # the directory: just us
|
||||||
swarm.domain = "hive.example"; # the swarm's DNS domain
|
};
|
||||||
deploy.hive-controller.hiveName = "pr1ma"; # this hive's label in it
|
deploy = {
|
||||||
swarm.hives.pr1ma = { }; # the directory: just us
|
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
|
# ... the rest of your host config
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,17 @@ The smallest working config, as in the README's
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
services.hyperhive = {
|
services.hyperhive = {
|
||||||
deploy.singleHostSwarm = true;
|
swarm = {
|
||||||
deploy.hive-controller.enable = true;
|
domain = "myswarm.example";
|
||||||
swarm.domain = "hive.example";
|
hives.firsthive = { };
|
||||||
deploy.hive-controller.hiveName = "pr1ma";
|
};
|
||||||
swarm.hives.pr1ma = { };
|
deploy = {
|
||||||
|
singleHostSwarm = true;
|
||||||
|
hive-controller = {
|
||||||
|
enable = true;
|
||||||
|
hiveName = "firsthive";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
To create an agent yourself instead, use the swarm UI, or:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
swarmctl agent create iris --hive pr1ma
|
swarmctl agent create iris --hive firsthive
|
||||||
```
|
```
|
||||||
|
|
||||||
The controller provisions iris's identity, forge user and config repo
|
The controller provisions iris's identity, forge user and config repo
|
||||||
|
|
|
||||||
|
|
@ -503,17 +503,21 @@ as the forge or the secret store is down. A restart isn't needed to recover.
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
services.hyperhive = {
|
services.hyperhive = {
|
||||||
swarm.domain = "example.com"; # required — the swarm's DNS domain
|
swarm = {
|
||||||
deploy.hive-controller.hiveName = "pr1ma"; # required on a hive host — this hive's label in it
|
domain = "myswarm.example"; # required — the swarm's DNS domain
|
||||||
swarm.name = "constellat1on"; # shared swarm display name (optional)
|
name = "myswarm"; # shared swarm display name (optional)
|
||||||
|
|
||||||
# required — the directory, identical on every host in the swarm.
|
# required — the directory, identical on every host in the swarm.
|
||||||
# Names only: each entry's `domain` defaults to <name>.<swarm.domain>.
|
# Names only: each entry's `domain` defaults to <name>.<swarm.domain>.
|
||||||
swarm.hives = {
|
hives = {
|
||||||
pr1ma = { }; # this host, per hiveName
|
firsthive = { }; # this host, per hiveName
|
||||||
lab = { }; # a second hive
|
secondhive = { }; # a second hive
|
||||||
edge = { domain = "edge.elsewhere.example"; }; # addressed off-convention
|
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.
|
directory without this host would make every hive a peer, itself included.
|
||||||
|
|
||||||
```
|
```
|
||||||
# hive A # hive B
|
# hive A # hive B
|
||||||
deploy.hive-controller.hiveName = "pr1ma"; deploy.hive-controller.hiveName = "edge";
|
deploy.hive-controller.hiveName = "firsthive"; deploy.hive-controller.hiveName = "secondhive";
|
||||||
swarm.hives = { … }; swarm.hives = { … }; # byte-identical
|
swarm.hives = { … }; swarm.hives = { … }; # byte-identical
|
||||||
```
|
```
|
||||||
|
|
||||||
One entry per hive means two hosts can't hold _different_ facts about the
|
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
|
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
|
hive's own `services.hyperhive.domain` comes from its entry; it drives
|
||||||
`HYPERHIVE_HIVE_DOMAIN` in every container so agents can form qualified
|
`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
|
directly overrides the directory entry, with a deprecation warning: the
|
||||||
option is local to this host while every host shares the directory, so a
|
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.
|
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)
|
#### Config example (two hives)
|
||||||
|
|
||||||
```nix
|
```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 = {
|
services.hyperhive = {
|
||||||
deploy.wireguard = {
|
deploy.wireguard = {
|
||||||
enable = true;
|
enable = true;
|
||||||
privateKeyFile = "/etc/wireguard/hive.key";
|
privateKeyFile = "/etc/wireguard/hive.key";
|
||||||
address = "10.100.0.1/24";
|
address = "10.100.0.1/24";
|
||||||
listenPort = 51820; # optional, default 51820
|
listenPort = 51820; # optional, default 51820
|
||||||
};
|
};
|
||||||
|
|
||||||
# The same `hives` attrset both hosts hold — mesh fields included,
|
# The same `hives` attrset both hosts hold — mesh fields included,
|
||||||
# since "where this hive can be dialled" is a fact about that hive.
|
# since "where this hive can be dialled" is a fact about that hive.
|
||||||
swarm.hives = {
|
swarm.hives = {
|
||||||
pr1ma = {
|
firsthive = {
|
||||||
domain = "pr1ma.example.com";
|
domain = "firsthive.myswarm.example";
|
||||||
wireguardPublicKey = "base64keyA=";
|
wireguardPublicKey = "base64keyA=";
|
||||||
wireguardEndpoint = "198.51.100.1:51820";
|
wireguardEndpoint = "198.51.100.1:51820";
|
||||||
wireguardAddress = "10.100.0.1/32";
|
wireguardAddress = "10.100.0.1/32";
|
||||||
};
|
};
|
||||||
edge = {
|
secondhive = {
|
||||||
domain = "edge.corp";
|
domain = "secondhive.elsewhere.example";
|
||||||
wireguardPublicKey = "base64keyB=";
|
wireguardPublicKey = "base64keyB=";
|
||||||
wireguardEndpoint = "203.0.113.42:51820";
|
wireguardEndpoint = "203.0.113.42:51820";
|
||||||
wireguardAddress = "10.100.0.2/32";
|
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 = {
|
services.hyperhive = {
|
||||||
deploy.wireguard = {
|
deploy.wireguard = {
|
||||||
enable = true;
|
enable = true;
|
||||||
privateKeyFile = "/etc/wireguard/hive.key";
|
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 …
|
||||||
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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:
|
store is empty. To run one without the other, set it directly:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
services.hyperhive.deploy.victoriametrics.enable = true;
|
services.hyperhive.deploy = {
|
||||||
services.hyperhive.deploy.grafana.enable = false;
|
victoriametrics.enable = true;
|
||||||
|
grafana.enable = false;
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
⚠️ **This starts a database that grows for as long as the swarm runs.**
|
⚠️ **This starts a database that grows for as long as the swarm runs.**
|
||||||
|
|
|
||||||
|
|
@ -153,9 +153,13 @@ Only the swarm's forge host runs it (`services.hyperhive.deploy.forgejo.enable`,
|
||||||
```nix
|
```nix
|
||||||
# The SSH port is swarm-wide: every hive dials it. The HTTP port and whether
|
# 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.
|
# 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 = {
|
||||||
services.hyperhive.deploy.forgejo.httpPort = 3000; # default — outside hyperhive's 7000 / 8100-8999
|
swarm.forge.sshPort = 2222; # default — git-over-SSH, off the host's openssh on 22
|
||||||
services.hyperhive.deploy.forgejo.openFirewall = false; # default
|
deploy.forgejo = {
|
||||||
|
httpPort = 3000; # default — outside hyperhive's 7000 / 8100-8999
|
||||||
|
openFirewall = false; # default
|
||||||
|
};
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
`sshPort` serves `git clone/push/pull` over SSH (`git@<domain>:owner/repo.git` with `-p 2222`). SSH goes straight to Forgejo, not through nginx.
|
`sshPort` serves `git clone/push/pull` over SSH (`git@<domain>:owner/repo.git` with `-p 2222`). SSH goes straight to Forgejo, not through nginx.
|
||||||
|
|
|
||||||
|
|
@ -92,10 +92,14 @@ listener sits on `bridgeIp`, on the host's bridge interface.
|
||||||
```nix
|
```nix
|
||||||
{
|
{
|
||||||
services.hyperhive = {
|
services.hyperhive = {
|
||||||
deploy.hive-controller.enable = true;
|
swarm = {
|
||||||
deploy.hive-controller.hiveName = "pr1ma";
|
domain = "myswarm.example";
|
||||||
swarm.domain = "darkest.space";
|
hives.firsthive = { }; # -> domain = firsthive.myswarm.example
|
||||||
swarm.hives.pr1ma = { }; # -> domain = pr1ma.darkest.space
|
};
|
||||||
|
deploy.hive-controller = {
|
||||||
|
enable = true;
|
||||||
|
hiveName = "firsthive";
|
||||||
|
};
|
||||||
# network.bridgeIp = "10.42.0.1"; # default
|
# network.bridgeIp = "10.42.0.1"; # default
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,17 +19,19 @@ assumptions:
|
||||||
## Enabling it
|
## Enabling it
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
services.hyperhive.snapshotStore = {
|
services.hyperhive = {
|
||||||
enable = true;
|
snapshotStore = {
|
||||||
path = "/var/lib/hyperhive-snapshots"; # must be on btrfs
|
enable = true;
|
||||||
port = 51821;
|
path = "/var/lib/hyperhive-snapshots"; # must be on btrfs
|
||||||
};
|
port = 51821;
|
||||||
|
};
|
||||||
|
|
||||||
# The mesh is a hard requirement, and is asserted:
|
# The mesh is a hard requirement, and is asserted:
|
||||||
services.hyperhive.deploy.wireguard = {
|
deploy.wireguard = {
|
||||||
enable = true;
|
enable = true;
|
||||||
address = "10.100.0.9/24";
|
address = "10.100.0.9/24";
|
||||||
privateKeyFile = "/etc/wireguard/hive.key";
|
privateKeyFile = "/etc/wireguard/hive.key";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,8 +135,10 @@ single-host deploy, but are useful for multi-hive or custom-network
|
||||||
setups.
|
setups.
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
services.hyperhive.agent.forge.url = "http://forge.example:3000"; # default: null
|
services.hyperhive.agent = {
|
||||||
services.hyperhive.agent.matrix.url = "https://matrix.example"; # default: null
|
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
|
**`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:
|
Three per-agent `agent.nix` options control this:
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
services.hyperhive.agent.claudeMarketplaces = [ # default
|
services.hyperhive.agent = {
|
||||||
"anthropics/claude-plugins-official"
|
claudeMarketplaces = [
|
||||||
"${services.hyperhive.agent.packages.claude-plugins}" # hive's own local marketplace, registered as `hyperhive`
|
# default
|
||||||
];
|
"anthropics/claude-plugins-official"
|
||||||
services.hyperhive.agent.claudePlugins = [ # default
|
"${services.hyperhive.agent.packages.claude-plugins}" # hive's own local marketplace, registered as `hyperhive`
|
||||||
"skill-creator@claude-plugins-official"
|
];
|
||||||
"base@hyperhive"
|
claudePlugins = [
|
||||||
];
|
# default
|
||||||
services.hyperhive.agent.claudePluginsAutoUpdate = false; # default
|
"skill-creator@claude-plugins-official"
|
||||||
|
"base@hyperhive"
|
||||||
|
];
|
||||||
|
claudePluginsAutoUpdate = false; # default
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
- **`claudeMarketplaces`** — list of marketplace sources passed to
|
- **`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`)
|
## API-key provider (`useApiKey` / `backendEnvironmentFile`)
|
||||||
|
|
||||||
```nix
|
```nix
|
||||||
services.hyperhive.agent.useApiKey = true; # default: false
|
services.hyperhive.agent = {
|
||||||
services.hyperhive.agent.backendEnvironmentFile =
|
useApiKey = true; # default: false
|
||||||
"/agents/myagent/harness/openrouter.env"; # default: null
|
backendEnvironmentFile = "/agents/myagent/harness/openrouter.env"; # default: null
|
||||||
services.hyperhive.agent.model = "anthropic/claude-3.5-sonnet"; # provider-specific model string
|
model = "anthropic/claude-3.5-sonnet"; # provider-specific model string
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
Runs this agent's `claude` against an API-key provider (for example OpenRouter)
|
Runs this agent's `claude` against an API-key provider (for example OpenRouter)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue