Reproducible hive
A new Kubernetes hive comes up from an empty namespace through two inputs: the antoncore GitOps values of one hive only release, and one agentihooks command on the UI hive. scripts/packaging/hive-reproduce.sh runs every step after the values merge and prints the wall time of each, up to the first engineer the hive’s join report grades healthy.
What a hive is made of
| Piece | Where it lives | Written by |
|---|---|---|
| The release: worker hive Deployment and its worktrees ConfigMap | the agentihooks chart with hiveOnly: true, values in antoncore k8s/values-overlays/<release>/values.yaml | a pull request into antoncore dev |
| The ArgoCD application, which creates the namespace | antoncore k8s/argocd/apps/<release>.yaml | the same pull request |
| ExternalSecrets for the member credential, ledger address and CA, accounts and settings | antoncore k8s/manifests/<release>/ | the same pull request |
The member credential (credential), ledger address (swarm_url) and ledger CA (ca_pem) | OpenBao secret/k8s/<release> | agentihooks hive invite |
| Claude and Codex accounts, agent settings, GitHub App | OpenBao agentihooks/claude-creds, agentihooks/codex-creds, k8s/agentihooks-swarm-workstation-hive-settings, k8s/agenticore | shared with every hive, by reference |
hiveOnly: true renders only the worker hive: no controller, ledger, Redis, network policy or ledger test hook, so a second release never starts a second swarm. One release is one hive, because one credential is one hive id.
The one command
On the UI hive, with OPENBAO_URL and OPENBAO_TOKEN set:
agentihooks hive invite anton-2 --like k8s/agentihooks-swarm-workstation-hive
- It mints a hive member named after the label and writes
credential,swarm_urlandca_pemintok8s/agentihooks-hive-<label>(--pathnames another record). Other keys in the record are kept. --like <record>copies the hive refresh URL, the Redis host, the ledger address and the ledger CA from a hive that already works, so no address is typed.--url,--redis-urland--swarm-urloverride the first three. The CA comes from the--likerecord, else from this host’s ledger CA; without--likethe Redis URL is this host’s.- It prints the member id and key names only, never a value:
invited hive member <id> as anton-2; OpenBao secret/k8s/agentihooks-hive-anton-2: wrote ca_pem, credential, swarm_url (3 keys total). - A rerun mints a new member and revokes the one the record held, ending with
; revoked the previous member <id>. - Refusals print
hive invite refused: <reason>and exit 1: no OpenBao settings, an unknown hive URL, a--likerecord that is empty, or an OpenBao error. A failed write revokes the member it just minted; if that revoke fails too, the refusal names the member left behind.
Runbook
1. Values (one pull request into antoncore dev)
Copy the three files of agentihooks-hive-anton-2 under a new release name and change the name, the record path and the node. Pick a node whose free memory requests cover workers.maxAgents times workers.memoryPerAgentMi.request:
kubectl describe node <node> | sed -n '/Allocated resources/,/Events/p'
The application tracks the agentihooks main chart. Merge it only once a release on main carries hiveOnly: before that the chart ignores the key and renders a whole swarm into the namespace. After the merge, ArgoCD creates the namespace and the release. The pod waits in CreateContainerConfigError until its credential Secret exists.
2. Invite, join and time it
scripts/packaging/hive-reproduce.sh anton-2 agentihooks-hive-anton-2
| Step | Done when | Bound |
|---|---|---|
| namespace | the namespace holds its HIVE_EXTERNAL_SECRETS (4) ExternalSecrets | HIVE_STEP_TIMEOUT_S (900) |
| invite | hive invite wrote the record | none |
| secrets synced | every ExternalSecret in the namespace is Ready, after a forced sync | HIVE_STEP_TIMEOUT_S |
| pod ready | the worker hive pod is Ready, after a rollout restart so it reads the new credential | HIVE_STEP_TIMEOUT_S |
| heartbeat | hive show <id> has a heartbeat under 90 seconds old | HIVE_STEP_TIMEOUT_S |
| engineer working | the swarm runs a non master agent on the hive in state working | HIVE_ENGINEER_TIMEOUT_S (1800) |
| join report | a join report on the hive grades an agent complete or partial | HIVE_STEP_TIMEOUT_S |
HIVE_RECORD and HIVE_LIKE override the record paths, HIVE_EXTERNAL_SECRETS the number of ExternalSecrets the namespace holds; HIVE_TIMING_FILE appends the table to a file. A step past its bound prints timed out: <what> with the table so far and exits 1. Each row carries its UTC time, the seconds the step took and the seconds since the script started.
The clock starts when the script starts, after ArgoCD created the namespace; the time from the antoncore merge to the namespace is the namespace’s creationTimestamp minus the merge time.
The script refuses to start unless at least one swarm has hive-placement=on; it does not check that this is the swarm holding the engineer task. The refusal names agentihooks swarm <slug> set hive-placement=on; turning placement on is the operator’s call. The engineer step also needs an open engineer task for a repo the hive lists. Placement fills hives first, so the next engineer the tick spawns lands on the new hive while it has a free slot.
A hive is healthy at its first join report graded complete (every check passed) or partial (only optional checks, such as the brain overlay, missed). A failed grade, a required check missed, never counts.
3. Check by hand
agentihooks hive show <id> # repos, slots and heartbeat
agentihooks hive compare # configuration against this hive
kubectl -n <namespace> logs deploy/<release>-agentihooks-swarm-worker-hive | grep worker-hive:
Removing a hive
Revert the antoncore pull request; ArgoCD prunes the release. Then agentihooks hive revoke <id> deletes the member’s ledger credential and Redis user. The OpenBao record stays until it is deleted by hand.
Troubleshooting
| Symptom | Reading | Action |
|---|---|---|
secrets synced times out | an ExternalSecret cannot read its record | kubectl -n <namespace> describe externalsecret; check the record path the invite printed |
the pod stays in CreateContainerConfigError | a Secret named in envFromSecrets or credentialSecret is missing | the same describe; every Secret comes from one ExternalSecret |
heartbeat times out with the pod Ready | hive run cannot reach the swarm Redis named in the credential | the pod log; rerun the invite with --redis-url set to an address the node reaches |
engineer working times out | placement is off, no engineer task is open, or the hive lacks the task’s repo | agentihooks hive show <id> for repos and slots |