feat: remaining phases — actuator provisioning, transition checks, cleanup

Phase 2: Actuator provisioning
- ProvisionLXC: pct create, start, package install, mounts, health check
- ProvisionVM: qm create, status check via SSH
- sshExecSimple helper for lightweight SSH command execution
- resolveHost helper for entity attribute lookups

Phase 5: Transition check enforcement
- TransitionChecks map with 8 named checks:
  age-key-enrolled, mesh-joined, health-check-answering,
  no-inbound-edges, secrets-revoked, backups-verified,
  ingress-dns-removed, doc-page-complete
- All checks accept pool + entity attrs for validation at transition time

Phase 6: Cleanup
- tools/setup-caveman.sh — npm install + wrapper + templates
- tools/setup-hermes-soul.sh — SOUL.md provisioning
- CLIENTS.md updated for thin client model (no git clone, API-based)
- Old git-sync references replaced with context poller

All tests pass, go vet clean.
This commit is contained in:
2026-07-08 00:40:53 +02:00
parent cfce35bee0
commit 84ecb6b895
5 changed files with 530 additions and 159 deletions

View File

@@ -30,33 +30,52 @@ approval.
## Enrollment
Enrolled clients have a checkout at `/opt/homelab-context/`. If this
directory does not exist, the client is not enrolled.
Thin client model — no git clone, no sync timer. `bootstrap.sh` fetches only
the agent orientation files and tooling from the raw Gitea URL, then enrolls
via the Oikos API.
To enroll:
```bash
# Run from an existing enrolled client
homelab client add <hostname>
# Run from any machine with mesh connectivity
curl -fsSL https://git.hubris.network/dtoro/Homelab-Docs/raw/main/bootstrap.sh | sudo bash
# Or with optional tooling:
curl ... | sudo bash -s -- --with-mcp # wire Claude's MCP config
curl ... | sudo bash -s -- --with-hermes # install Goose + Hermes
```
This runs `bootstrap.sh` on the target, which:
1. Clones the repo to `/opt/homelab-context/`
2. Configures the auto-sync timer
3. Provisions agent persona from `hermes/SOUL.md` (on Hermes agents)
4. Installs Caveman tooling for terse communication
This calls `POST /api/v1/clients/enroll` on the Oikos API, which:
1. Validates the entity exists in DB (planned or provisioning state)
2. Validates mesh IP against expected subnets
3. Generates an age keypair and delivers it to the client
4. Creates an Infisical machine identity
5. Transitions the entity to provisioning state
## After enrollment
### What changes on your machine
- `/opt/homelab-context/` — the repo checkout, your source of truth
- `/opt/homelab-context/inventory.yaml` — read this first: your hostname, role,
peers, mounts, services
- `/opt/homelab-context/seeds/policy.yaml` — rules for what actions you can
take autonomously vs. what requires operator approval
- Auto-sync timer — pulls the repo every 5 minutes
- `~/.hermes/SOUL.md` — agent persona (on Hermes agents)
- `~/bin/caveman_wrapper.sh` — terse communication tooling
- `/opt/homelab/` — agent orientation files (CLIENTS.md, AGENTS.md, OIKOS.md)
- `/opt/homelab/tools/` — tooling scripts (caveman, hermes-soul)
- `/etc/age/key.txt` — age private key for SOPS decryption (fallback)
- `/etc/infisical/identity` — Infisical machine identity (primary secrets)
- Context poller — launchd/systemd timer hits `GET /api/v1/clients/{slug}/context` every 5 minutes for agent file updates
### What's NOT on your machine
- No git clone of the full repo
- No `git pull` sync timer
- No `bin/homelab` CLI (replaced by MCP tools + API)
- No `.sops.yaml` or SOPS-encrypted backups (served via API context endpoint on demand)
### Thin client vs control plane
| | Thin client (workstation) | Control plane (mac-mini) |
|---|---|---|
| Disk footprint | ~100KB (orientation files + tools) | Full repo clone (~50MB) |
| Update mechanism | `GET /context?since=` poll | Git pull + post-pull.sh |
| Source of truth | DB via MCP | DB + local seeds + archive |
| Secrets access | Infisical (primary), age/SOPS served via API (fallback) | Infisical + local SOPS files |
### Your identity
@@ -69,33 +88,36 @@ mesh address.
1. **Postgres database** (runtime) — authoritative for entities, knowledge,
signals, ledger. Query via MCP or REST API.
2. **Repo at `/opt/homelab-context/`** — bootstrap seeds + documentation.
On disk, available offline.
2. **Context poller** — agent files and tooling fetched via API deltas every
5 minutes.
3. **Never guess.** If data is missing, query MCP. If MCP is down, grep the
clone.
local `/opt/homelab/` files.
## The sync timer
## The context poller
Every 5 minutes, `systemd` (Linux) or `launchd` (macOS) runs:
Every 5 minutes, launchd (macOS) or systemd (Linux) hits:
1. `git pull` via `tools/post-pull.sh`
2. Any `tools/*.setup.sh` scripts that need to run
```
GET /api/v1/clients/ws:{hostname}/context?since={last_timestamp}
```
This keeps your checkout current and applies tooling updates automatically.
To trigger sync manually: `sudo homelab sync`.
The API returns which agent files, tools, and SOPS config changed since the
last poll. Only changed files are downloaded. This replaces the old
`git pull` with a lightweight HTTP delta.
To trigger manually: run `/opt/homelab/tools/context-poller.sh`.
## Making changes
- **Read state**: use MCP tools or the repo checkout
- **Read state**: use MCP tools or the API
- **Mutate state** (restart, edit config, deploy): classify the action against
`seeds/policy.yaml`:
policy (query `preflight` MCP tool):
- `read_only` / `reversible_low` — execute directly
- `config_mutation` / `destructive` — request operator approval via the
`homelab` CLI
- `config_mutation` / `destructive` — request operator approval via
`POST /api/v1/entities/{slug}/activate` (or equivalent lifecycle endpoint)
- **Secrets**: use Infisical (primary) or SOPS (fallback). Never hardcode.
- **Knowledge**: if you observe a discrepancy between docs and live state,
update the DB via the API in the same session. Run `oikos export` to
regenerate seeds.
update the DB via the API in the same session.
## MCP endpoint