# Agent enrollment — operational notes **For the actual enrollment flow, see [CLIENTS.md](../../CLIENTS.md#enrollment) — it's the current, authoritative version.** This page used to duplicate that flow in more detail, describing a `homelab` CLI-based two-step ceremony (`homelab client add` reserves an inventory slot → client bootstraps → operator finalizes the pubkey). That CLI and that flow don't exist anymore — enrollment today is one shot: `bootstrap.sh` calls `POST /api/v1/clients/enroll` directly and gets back an age keypair + Infisical identity in the same response. What's left here is the handful of things that are still true and weren't already covered elsewhere. ## Prerequisites | Requirement | Why | How to check | | --- | --- | --- | | Hostname matches an entry in `inventory.yaml` | `EnrollClient` looks up the entity by slug derived from hostname; it must exist in `planned`/`provisioning` state. | `hostname` (Linux) / `scutil --get LocalHostName` (macOS) | | OS is Linux or macOS | bootstrap detects via `uname -s` | `uname -s` | | On the mesh (Netbird) **or** on the LAN | enrollment validates mesh IP against expected subnets | `netbird status` | | `curl`, `jq`, `age`, `python3` | bootstrap preflight (`bootstrap.sh:100`) — auto-installed on Fedora/RHEL/Debian/Ubuntu/macOS if missing | `command -v curl jq age python3` | | Can resolve `*.hubris.network` | bootstrap calls the Oikos API and writes `https://mcp.hubris.network/mcp` | `dig +short mcp.hubris.network` | ### Hostname mismatch is the most common bootstrap failure If the entity for your hostname doesn't exist yet (in `planned` or `provisioning` state), enrollment 4xxs. Two fixes: - **Rename the host** to match an existing planned entity: `sudo hostnamectl set-hostname ` (Linux) or System Preferences → Sharing (macOS), then re-run. - **Add/rename the inventory entry**: edit `seeds/inventory.yaml`, ingest via `oikos seed` (or the equivalent MCP/API entity-creation path), then re-run bootstrap. ### Networking prerequisites (Netbird, DNS, SSH key distribution) Migrated to a runbook in the knowledge base — query `search_knowledge("netbird mesh dns")` or `get_entity_knowledge`, or ask Nomos. Covers: getting onto the Netbird mesh (interactive OIDC vs. setup-key), why OIDC login can fail from off-mesh, split-horizon DNS options, and distributing a new workstation's SSH pubkey across the fleet via `ssh/deploy-keys.sh`. ## Claude Code permissions for fleet ops By default Claude Code's auto-mode classifier asks for confirmation on every ssh into the mesh. Pre-authorize the common fleet ssh pattern by adding to `~/.claude/settings.json`: ```json { "permissions": { "defaultMode": "auto", "allow": [ "Bash(ssh -p 22022 *)" ] } } ``` This covers any ssh to a mesh peer on the homelab netbird port, scoped tight enough that the classifier doesn't gate it but loose enough to handle the variety of arguments. ## Open questions (not verified against current architecture — don't guess these from the old flow) The old two-step ceremony had answers for these; the current one-shot `/api/v1/clients/enroll` flow may handle them differently and this hasn't been re-verified: - **Removing a client.** No current equivalent confirmed for the old `homelab client remove` (inventory removal + secret re-keying + key revocation). Likely maps to an entity lifecycle transition (`.agents/skills/lifecycle-deprecate-node/` or `lifecycle-destroy-node/`) but those skills reference the same dead CLI and need their own check. - **Granting a secret to an already-enrolled client.** The old flow hand-edited `.sops.yaml` `creation_rules` + `sops updatekeys`. Given Infisical is now the primary secrets backend (SOPS is the DR fallback), the current mechanism is probably Infisical-side, not a `.sops.yaml` edit — not confirmed. ## Troubleshooting | Symptom | Cause | Fix | | --- | --- | --- | | Enrollment 404s / entity not found | Hostname doesn't match a `planned`/`provisioning` inventory entry | See "Hostname mismatch" above | | `gnutls_handshake() failed` / TLS errors reaching `*.hubris.network` | Client DNS resolves `*.hubris.network` to the public VPS instead of the LAN/mesh path | See the networking runbook (split-horizon DNS section) | | Chat-mode `!` shell can't `sudo` (`a terminal is required to read the password`) | Claude Code's `!` invocation doesn't allocate a tty, and standard `sudo` won't read its password from stdin or a non-tty pipe. | Run the sudo'd command in a real terminal outside chat. For commands the agent issues repeatedly, configure passwordless sudo for the narrow set (e.g. `/etc/sudoers.d/homelab-self` with ` ALL=(ALL) NOPASSWD: /usr/bin/dnf upgrade -y, /usr/bin/apt-get *`). | ## Changelog ### 2026-07-12 — trimmed to current architecture Removed everything describing the retired `homelab` CLI-based two-step enrollment ceremony (now: `CLIENTS.md`'s one-shot flow), the Nous-Hermes/ Goose cross-link (that whole flow was removed the same day), and CLI-syntax troubleshooting rows with no current equivalent. Migrated the still-true Netbird/DNS/SSH-distribution content to a knowledge-base runbook rather than duplicating it here. What's left is genuinely current or explicitly flagged as unverified. Original ~365-line version is in git history (`git log -- .agents/operations/agent-enrollment.md`) if any of the removed detail turns out to still be needed. ### 2026-06-02 — SSH reachability post-bootstrap steps Added a section covering SSH key generation, pubkey publication, deployment to hosts, SSH config generation, and LAN IP registration. New workstations enrolled via this doc automatically join the SSH mesh. (Superseded 2026-07-12 — migrated to the networking runbook.) ### 2026-05-31 — cross-link to nomos-agent.md Added a sibling page covering Nous-Hermes-on-Goose enrollment. (Removed 2026-07-12 along with the rest of that flow.) ### 2026-05-21 — netbird-ssh JWT issuer + username + LAN-fallback troubleshooting rows Added three rows to the troubleshooting table covering issues surfaced during the netbird vanilla migration. (Migrated 2026-07-12 to the networking runbook.) ### 2026-05-20 — initial page Captures the enrollment flow validated during Phase 2 of the homelab context distribution rollout. hubris + LXC 105 (apps) enrolled; first workstation (republic-laptop) blocked on hostname mismatch, documented the resolution.