N0: rename Hermes → Nomos (standalone commit)

Problem: "Hermes" collides with Nous Researchs unrelated product;
  unclear identity for the resident agent.

  Change: Rename the live service identity across 39 files:
  - cmd/hermes/ → cmd/nomos/ (binary, env vars NOMOS_*)
  - internal/config/ server.go (NomosAgentSlug, nomosAgentID)
  - compose/hermes/ → compose/nomos/ (Dockerfile, service name)
  - hermes/ → nomos/ (SOUL.md, config.yaml, skills/)
  - .agents/HERMES.md → NOMOS.md (persona)
  - tools/setup-hermes-soul.sh → setup-nomos-soul.sh
  - seeds/inventory.yaml (agent:hermes → agent:nomos)
  - migrations/014_rename_agent_hermes_to_nomos.up.sql
  - Caddy vhost hermes.hubris.network → nomos.hubris.network
  - All referencing docs, scripts, ADR notes

  History preserved: archive/, plans/done/, ADRs not rewritten.
  Matrix @hermes notifier account and Legacy bin/hermes on LXC 129
  intentionally untouched (out of scope).

  Risk: N0 is identity-only rename; zero behavioral changes.
  Verification: go build ./... passes; docker compose --profile full
  resolves nomos service; grep -ri hermes (excluding archive/plans)
  returns only intentional refs (LLM model name, Matrix user).
This commit is contained in:
2026-07-08 14:14:56 +02:00
parent 9d9cbb63c4
commit 2b3aa248b1
39 changed files with 230 additions and 200 deletions

View File

@@ -1,4 +1,4 @@
# HERMES.md — Agent persona for homelab clients # NOMOS.md — Agent persona for homelab clients
This file is the canonical agent persona for **all** AI agents running on This file is the canonical agent persona for **all** AI agents running on
machines in the **hubris** homelab. It prescribes behaviour, token-efficiency machines in the **hubris** homelab. It prescribes behaviour, token-efficiency
@@ -32,8 +32,8 @@ approval flow, ontology).
| Agent | Loading mechanism | | Agent | Loading mechanism |
|-------|------------------| |-------|------------------|
| **Hermes** | `tools/setup-hermes-soul.sh` (auto-setup) → provisions `~/.hermes/SOUL.md` from this file | | **Nomos** | `tools/setup-nomos-soul.sh` (auto-setup) → provisions `~/.nomos/SOUL.md` from this file |
| **Goose** | `.goosehints` symlink at `~/.config/goose/.goosehints``/opt/homelab-context/HERMES.md` | | **Goose** | `.goosehints` symlink at `~/.config/goose/.goosehints``/opt/homelab-context/NOMOS.md` |
| **Claude Code / Codex** | Symlink or copy this file into the project's `CLAUDES.md` / `.claude` instructions | | **Claude Code / Codex** | Symlink or copy this file into the project's `CLAUDES.md` / `.claude` instructions |
**Do not edit SOUL.md or .goosehints directly.** Edit this file in the **Do not edit SOUL.md or .goosehints directly.** Edit this file in the
@@ -87,10 +87,10 @@ Caveman templates live at `~/templates/`:
ls ~/bin/caveman_wrapper.sh && echo "caveman ready" ls ~/bin/caveman_wrapper.sh && echo "caveman ready"
``` ```
## Important note for Hermes agents ## Important note for Nomos agents
If you are reading this as a Hermes agent, your SOUL.md was auto-provisioned If you are reading this as a Nomos agent, your SOUL.md was auto-provisioned
by `tools/setup-hermes-soul.sh`. This file is the canonical original — you by `tools/setup-nomos-soul.sh`. This file is the canonical original — you
can verify the content matches or re-provision by running: can verify the content matches or re-provision by running:
bash /opt/homelab-context/tools/setup-hermes-soul.sh bash /opt/homelab-context/tools/setup-nomos-soul.sh

View File

@@ -137,13 +137,13 @@ in the Go binary.
- Go packages: `internal/scheduler/`, `internal/actuator/`, - Go packages: `internal/scheduler/`, `internal/actuator/`,
`internal/learning/`, `internal/notifier/`, `internal/policy/`. `internal/learning/`, `internal/notifier/`, `internal/policy/`.
**Phase 4 — Agent / Hermes (DONE):** **Phase 4 — Agent / Nomos (DONE):**
- Standalone Hermes MCP client binary (`cmd/hermes`) with gateway mode - Standalone Nomos MCP client binary (`cmd/nomos`) with gateway mode
(:8092). Structured queries + natural-language routing to 15 MCP tools. (:8092). Structured queries + natural-language routing to 15 MCP tools.
Agent activity logging on every tool call. No SSH keys. Agent activity logging on every tool call. No SSH keys.
- `hermes/` directory with config, SOUL.md, homelab-ops skill. - `nomos/` directory with config, SOUL.md, homelab-ops skill.
- Hermes Docker service in `docker-compose.yml` (profile: full). - Nomos Docker service in `docker-compose.yml` (profile: full).
- Go packages: `cmd/hermes/`, `compose/hermes/`. - Go packages: `cmd/nomos/`, `compose/nomos/`.
**Phase 5 — Secrets / Infisical (DONE):** **Phase 5 — Secrets / Infisical (DONE):**
- `internal/secrets/`: backend abstraction (Manager) with primary - `internal/secrets/`: backend abstraction (Manager) with primary
@@ -159,7 +159,7 @@ in the Go binary.
lint, test, docker build). lint, test, docker build).
- Deploy: `scripts/deploy.sh` (git pull → docker build → compose up → - Deploy: `scripts/deploy.sh` (git pull → docker build → compose up →
health check), SHA-tagged images, rolling restart. health check), SHA-tagged images, rolling restart.
- Caddy config: `compose/caddy/Caddyfile.oikos` (oikos/mcp/hermes → - Caddy config: `compose/caddy/Caddyfile.oikos` (oikos/mcp/nomos →
mac-mini mesh :8090/:8092). mac-mini mesh :8090/:8092).
- Watchdog: `scripts/watchdog.sh` (2min cron, Matrix alert on failure). - Watchdog: `scripts/watchdog.sh` (2min cron, Matrix alert on failure).
- Verification: `scripts/verify-phase6.sh` (14/14 checks pass). - Verification: `scripts/verify-phase6.sh` (14/14 checks pass).
@@ -168,7 +168,7 @@ in the Go binary.
**Current deployment:** **Current deployment:**
- **Production**: Docker stack on mac-mini (`--profile full`: postgres, api, - **Production**: Docker stack on mac-mini (`--profile full`: postgres, api,
scheduler, notifier, hermes). Deployed 2026-07-07 with full knowledge seed. scheduler, notifier, nomos). Deployed 2026-07-07 with full knowledge seed.
The Python MCP server and secrets-issuance on apps/105 have been stopped The Python MCP server and secrets-issuance on apps/105 have been stopped
(see `scripts/cutover-checklist.md`). (see `scripts/cutover-checklist.md`).

View File

@@ -9,7 +9,7 @@ see [CONTRIBUTING.md](../../CONTRIBUTING.md) for a human-friendly version.
``` ```
cmd/oikos/main.go Entry point. Subcommands: api, scheduler, notifier, migrate, cmd/oikos/main.go Entry point. Subcommands: api, scheduler, notifier, migrate,
seed, export, secret, all seed, export, secret, all
cmd/hermes/main.go Hermes MCP client gateway (standalone binary) cmd/nomos/main.go Nomos MCP client gateway (standalone binary, formerly Hermes)
internal/httpapi/ REST + MCP server. Chi router. OpenAPI-generated types from internal/httpapi/ REST + MCP server. Chi router. OpenAPI-generated types from
internal/httpapi/gen/api.gen.go. Strict server in impl.go. internal/httpapi/gen/api.gen.go. Strict server in impl.go.
internal/mcp/ MCP tool implementations (get_entity, search_knowledge, etc.) internal/mcp/ MCP tool implementations (get_entity, search_knowledge, etc.)
@@ -31,10 +31,10 @@ api/codegen.yaml oapi-codegen config → generates internal/httpapi/g
migrations/ Forward-only SQL. Format: NNN_name.up.sql. No down migrations. migrations/ Forward-only SQL. Format: NNN_name.up.sql. No down migrations.
seeds/ Bootstrap YAML. ontology.yaml, inventory.yaml, policy.yaml, seeds/ Bootstrap YAML. ontology.yaml, inventory.yaml, policy.yaml,
knowledge.yaml. Regenerated from DB via oikos export. knowledge.yaml. Regenerated from DB via oikos export.
compose/ Dockerfiles. oikos/ (multi-stage), hermes/ (distroless). compose/ Dockerfiles. oikos/ (multi-stage), nomos/ (distroless).
Caddy config at compose/caddy/Caddyfile.oikos. Caddy config at compose/caddy/Caddyfile.oikos.
scripts/ Deploy, rollback, watchdog, verification, cutover checklist. scripts/ Deploy, rollback, watchdog, verification, cutover checklist.
hermes/ Hermes config.yaml, SOUL.md, skills. nomos/ Nomos config.yaml, SOUL.md, skills.
.agents/ Agent instruction files, domains, shared conventions, skills. .agents/ Agent instruction files, domains, shared conventions, skills.
plans/ Design documents. active/ + done/. plans/ Design documents. active/ + done/.
docs/adr/ Architecture decision records. Numbered, prefix-sorted. docs/adr/ Architecture decision records. Numbered, prefix-sorted.

View File

@@ -6,8 +6,8 @@ this repo that auto-syncs every 5 min, a per-client age key for SOPS
decryption, the `homelab` CLI, and an MCP endpoint in Claude Code's config. decryption, the `homelab` CLI, and an MCP endpoint in Claude Code's config.
> Onboarding a Nous-Hermes-powered Goose agent on top of standard enrollment? > Onboarding a Nous-Hermes-powered Goose agent on top of standard enrollment?
> See [hermes-agent.md](hermes-agent.md). It uses the same `bootstrap.sh` > See [nomos-agent.md](nomos-agent.md). It uses the same `bootstrap.sh`
> with an additional `--with-hermes` flag. > with an additional `--with-nomos` flag.
Architecture in [project_homelab_context_plan](https://… memory link); the Architecture in [project_homelab_context_plan](https://… memory link); the
operational reference is here. operational reference is here.
@@ -354,9 +354,9 @@ Added a new "Post-bootstrap: SSH reachability" section covering SSH key
generation, pubkey publication, deployment to hosts, SSH config generation, generation, pubkey publication, deployment to hosts, SSH config generation,
and LAN IP registration. New workstations enrolled via this doc will and LAN IP registration. New workstations enrolled via this doc will
automatically join the universal SSH mesh. automatically join the universal SSH mesh.
### 2026-05-31 — cross-link to nomos-agent.md
### 2026-05-31 — cross-link to hermes-agent.md Added a sibling page covering Nous-Hermes-on-Goose enrollment ([nomos-agent.md](nomos-agent.md)) and noted it at the top of this page. The Nomos flow extends `bootstrap.sh` with `--with-nomos` and `homelab client add` with the same flag; it does not change the underlying enrollment steps documented here.
Added a sibling page covering Nous-Hermes-on-Goose enrollment ([hermes-agent.md](hermes-agent.md)) and noted it at the top of this page. The Hermes flow extends `bootstrap.sh` with `--with-hermes` and `homelab client add` with the same flag; it does not change the underlying enrollment steps documented here.
### 2026-05-21 — netbird-ssh JWT issuer + username + LAN-fallback troubleshooting rows ### 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: (1) post-migration SSH JWT validator cache stuck on old Dex issuer (full `systemctl stop/start` required, not `restart`), (2) `user not found` from netbird-ssh's local-username default (use explicit `root@`), and (3) homelab CLI's LAN→netbird-FQDN fallback for off-LAN operators. Companion code change: per-host `ssh.user` field in `inventory.yaml` + `homelab` CLI's `ssh_target()` helper. Added three rows to the troubleshooting table covering issues surfaced during the netbird vanilla migration: (1) post-migration SSH JWT validator cache stuck on old Dex issuer (full `systemctl stop/start` required, not `restart`), (2) `user not found` from netbird-ssh's local-username default (use explicit `root@`), and (3) homelab CLI's LAN→netbird-FQDN fallback for off-LAN operators. Companion code change: per-host `ssh.user` field in `inventory.yaml` + `homelab` CLI's `ssh_target()` helper.

View File

@@ -14,7 +14,7 @@ Run from the [hubris host](../../archive/knowledge/hosts/hubris.md) as root. Whe
| `pvesm status` | Storage pools status | | `pvesm status` | Storage pools status |
| `pvesh get /nodes --output-format json` | Node summary as JSON | | `pvesh get /nodes --output-format json` | Node summary as JSON |
| `pvesh get /nodes/hubris/lxc/<id>/status/current` | Live container status | | `pvesh get /nodes/hubris/lxc/<id>/status/current` | Live container status |
| `pvesh get /cluster/resources --type vm --output-format json` | Bulk per-LXC CPU/mem/disk (used by the `homelab-health-watchdog` Hermes cron — see [monitoring](../../archive/knowledge/infrastructure/monitoring.md); the old `claudio-monitor` this once fed is deprecated) | | `pvesh get /cluster/resources --type vm --output-format json` | Bulk per-LXC CPU/mem/disk (used by the `homelab-health-watchdog` Nomos cron — see [monitoring](../../archive/knowledge/infrastructure/monitoring.md); the old `claudio-monitor` this once fed is deprecated) |
| `pveversion` | PVE version | | `pveversion` | PVE version |
| `journalctl -u pve-cluster -n 100` | PVE service logs | | `journalctl -u pve-cluster -n 100` | PVE service logs |
@@ -77,7 +77,7 @@ See [OIKOS.md](../OIKOS.md) for the operating model. Quick reference:
| `homelab change preflight <service>` | Dry-run report before mutating: risk class, current health, config repo, verification command | | `homelab change preflight <service>` | Dry-run report before mutating: risk class, current health, config repo, verification command |
| `homelab decide <action> <entity>` | Decision classifier: risk × blast radius × confidence → auto-act or escalate | | `homelab decide <action> <entity>` | Decision classifier: risk × blast radius × confidence → auto-act or escalate |
| `homelab signal list\|raise\|ack\|resolve\|mute` | The attention layer — pending updates, thresholds, drift, anything needing attention | | `homelab signal list\|raise\|ack\|resolve\|mute` | The attention layer — pending updates, thresholds, drift, anything needing attention |
| `homelab approval request\|list\|reply\|check` | Escalate-route grants (Matrix-delivered via Hermes, or the Oikos Console's `/approvals` page) | | `homelab approval request\|list\|reply\|check` | Escalate-route grants (Matrix-delivered via Nomos, or the Oikos Console's `/approvals` page) |
| `homelab restart <service> [--approval-id <id>]` | `--approval-id` is required whenever the service's risk class needs approval (e.g. `caddy`, `dns`) — refuses mechanically without a valid grant | | `homelab restart <service> [--approval-id <id>]` | `--approval-id` is required whenever the service's risk class needs approval (e.g. `caddy`, `dns`) — refuses mechanically without a valid grant |
Oikos Console (read-mostly dashboard): `oikos.hubris.network` once deployed — see [oikos/console/deploy/README.md](../../archive/oikos-cards/). Oikos Console (read-mostly dashboard): `oikos.hubris.network` once deployed — see [oikos/console/deploy/README.md](../../archive/oikos-cards/).

View File

@@ -1,4 +1,4 @@
# Hermes agent — Nous-Hermes-powered Goose sessions on a homelab client # Nomos agent — LLM-powered terminal sessions on a homelab client
Onboards [Nous Research's Hermes](https://nousresearch.com/) (a fine-tuned Onboards [Nous Research's Hermes](https://nousresearch.com/) (a fine-tuned
Llama variant) as a working terminal agent on a homelab client. Builds on top Llama variant) as a working terminal agent on a homelab client. Builds on top
@@ -8,13 +8,13 @@ of standard client enrollment (see [agent-enrollment.md](agent-enrollment.md))
The agent runs as a [Goose](https://goose-docs.ai/) session. Goose provides: The agent runs as a [Goose](https://goose-docs.ai/) session. Goose provides:
- The chat loop, multi-turn history, and streaming - The chat loop, multi-turn history, and streaming
- The OpenRouter provider that routes to Nous Hermes - The OpenRouter provider that routes to the configured LLM
- The built-in `developer` extension (shell + file editor — same surface Claude - The built-in `developer` extension (shell + file editor — same surface Claude
Code has) Code has)
- A remote MCP extension pointed at `mcp.hubris.network` for read-only - A remote MCP extension pointed at `mcp.hubris.network` for read-only
homelab context (`list_lxcs`, `tail_log`, `search_docs`, etc.) homelab context (`list_lxcs`, `tail_log`, `search_docs`, etc.)
The persona is `/opt/homelab-context/HERMES.md`, symlinked as Goose's global The persona is `/opt/homelab-context/NOMOS.md`, symlinked as Goose's global
`.goosehints` so it's injected into the system prompt on every session. `.goosehints` so it's injected into the system prompt on every session.
## Prerequisites ## Prerequisites
@@ -23,7 +23,7 @@ The persona is `/opt/homelab-context/HERMES.md`, symlinked as Goose's global
| --- | --- | | --- | --- |
| Standard enrollment complete (`homelab whoami` works) | [agent-enrollment.md](agent-enrollment.md) | | Standard enrollment complete (`homelab whoami` works) | [agent-enrollment.md](agent-enrollment.md) |
| `secrets/openrouter-api-key.yaml` exists with a real `sk-or-...` value | See "Seeding the OpenRouter key" below | | `secrets/openrouter-api-key.yaml` exists with a real `sk-or-...` value | See "Seeding the OpenRouter key" below |
| The host's `age_pubkey` is on the openrouter-api-key.yaml sops rule | `homelab client add <host> --finalize-pubkey <age1...> --with-hermes` | | The host's `age_pubkey` is on the openrouter-api-key.yaml sops rule | `homelab client add <host> --finalize-pubkey <age1...> --with-nomos` |
## Onboarding flow ## Onboarding flow
@@ -33,37 +33,37 @@ homelab client add new-machine
# 2. Join new-machine to Netbird (setup-key or OIDC). # 2. Join new-machine to Netbird (setup-key or OIDC).
# 3. On new-machine: bootstrap with --with-hermes. # 3. On new-machine: bootstrap with --with-nomos.
TOKEN=... # gitea PAT, read:repository TOKEN=... # gitea PAT, read:repository
curl -fsSL -u "dtoro:$TOKEN" \ curl -fsSL -u "dtoro:$TOKEN" \
https://git.hubris.network/dtoro/oikos/raw/branch/main/bootstrap.sh \ https://git.hubris.network/dtoro/oikos/raw/branch/main/bootstrap.sh \
-o /tmp/bootstrap.sh -o /tmp/bootstrap.sh
sudo HOMELAB_GITEA_TOKEN=$TOKEN bash /tmp/bootstrap.sh --with-mcp --with-hermes sudo HOMELAB_GITEA_TOKEN=$TOKEN bash /tmp/bootstrap.sh --with-mcp --with-nomos
# 4. Back on hubris: finalize the age pubkey AND grant the Hermes secret. # 4. Back on hubris: finalize the age pubkey AND grant the Nomos secret.
homelab client add new-machine \ homelab client add new-machine \
--finalize-pubkey age1... \ --finalize-pubkey age1... \
--with-hermes --with-nomos
# 5. Wait ≤5 min for sync, then on new-machine: # 5. Wait ≤5 min for sync, then on new-machine:
hermes "what LXCs are running?" nomos "what LXCs are running?"
``` ```
The bootstrap `--with-hermes` flag does five things, all idempotent: The bootstrap `--with-nomos` flag does five things, all idempotent:
1. Downloads the latest Goose binary into the operator's `~/.local/bin/goose` 1. Downloads the latest Goose binary into the operator's `~/.local/bin/goose`
(upstream installer) and symlinks `/usr/local/bin/goose` to it. (upstream installer) and symlinks `/usr/local/bin/goose` to it.
2. Symlinks `/opt/homelab-context/bin/hermes``/usr/local/bin/hermes`. 2. Symlinks `/opt/homelab-context/bin/nomos``/usr/local/bin/nomos`.
3. Symlinks `/opt/homelab-context/HERMES.md``/root/HERMES.md` (Linux) or 3. Symlinks `/opt/homelab-context/NOMOS.md``/root/NOMOS.md` (Linux) or
`/etc/HERMES.md` (macOS) for `cat`-as-operator convenience. `/etc/NOMOS.md` (macOS) for `cat`-as-operator convenience.
4. Drops `~/.config/goose/config.yaml` pinning the provider, model, and 4. Drops `~/.config/goose/config.yaml` pinning the provider, model, and
extensions (preserves any keys the operator added by hand). extensions (preserves any keys the operator added by hand).
5. Symlinks `~/.config/goose/.goosehints`HERMES.md, so the persona is 5. Symlinks `~/.config/goose/.goosehints`NOMOS.md, so the persona is
injected as the system prompt on every session. injected as the system prompt on every session.
## Seeding the OpenRouter key ## Seeding the OpenRouter key
The first time anyone enrolls with `--with-hermes`, the encrypted file The first time anyone enrolls with `--with-nomos`, the encrypted file
`secrets/openrouter-api-key.yaml` contains a placeholder. On hubris (or any `secrets/openrouter-api-key.yaml` contains a placeholder. On hubris (or any
existing recipient): existing recipient):
@@ -75,19 +75,19 @@ git -C /opt/homelab-context commit -m 'openrouter-api-key: seed real key'
git -C /opt/homelab-context push git -C /opt/homelab-context push
``` ```
Until this step happens, `hermes …` exits with `openrouter-api-key.yaml still Until this step happens, `nomos …` exits with `openrouter-api-key.yaml still
contains the placeholder`. Subsequent enrollees get the real key automatically contains the placeholder`. Subsequent enrollees get the real key automatically
via `--with-hermes` (which adds them as a sops recipient on via `--with-nomos` (which adds them as a sops recipient on
`secrets/openrouter-api-key.yaml`). `secrets/openrouter-api-key.yaml`).
## Granting the OpenRouter key to an already-enrolled host ## Granting the OpenRouter key to an already-enrolled host
If a host was enrolled without `--with-hermes` and you want to add it later: If a host was enrolled without `--with-nomos` and you want to add it later:
```bash ```bash
# On hubris: # On hubris:
PUBKEY=$(homelab whoami --hostname <host> | grep age_pubkey | awk '{print $2}') PUBKEY=$(homelab whoami --hostname <host> | grep age_pubkey | awk '{print $2}')
homelab client add <host> --finalize-pubkey "$PUBKEY" --with-hermes homelab client add <host> --finalize-pubkey "$PUBKEY" --with-nomos
``` ```
`--finalize-pubkey` is required by the existing flow even when the pubkey is `--finalize-pubkey` is required by the existing flow even when the pubkey is
@@ -101,12 +101,12 @@ re-run; only the secret recipient list changed.
```bash ```bash
homelab whoami # standard enrollment OK homelab whoami # standard enrollment OK
homelab secret openrouter-api-key | head -c 8 # decrypts (prints `api_key:`) homelab secret openrouter-api-key | head -c 8 # decrypts (prints `api_key:`)
which goose && which hermes # binaries present which goose && which nomos # binaries present
goose info -v # provider/model wiring sane goose info -v # provider/model wiring sane
hermes "what LXCs are running?" # interactive Goose session nomos "what LXCs are running?" # interactive Goose session
# Non-interactive smoke test: # Non-interactive smoke test:
echo "List the homelab MCP tools you have available" | hermes echo "List the homelab MCP tools you have available" | nomos
``` ```
## Configuration ## Configuration
@@ -135,9 +135,9 @@ extensions:
Override via env on a single bootstrap run: Override via env on a single bootstrap run:
```bash ```bash
HOMELAB_HERMES_MODEL=nousresearch/hermes-3-llama-3.1-405b \ HOMELAB_NOMOS_MODEL=nousresearch/hermes-3-llama-3.1-405b \
HOMELAB_HERMES_MCP_URI=https://mcp.hubris.network/mcp \ HOMELAB_NOMOS_MCP_URI=https://mcp.hubris.network/mcp \
sudo bash /tmp/bootstrap.sh --with-hermes sudo bash /tmp/bootstrap.sh --with-nomos
``` ```
Any keys you add by hand (e.g. `GOOSE_TEMPERATURE`, extra `extensions.*`) are Any keys you add by hand (e.g. `GOOSE_TEMPERATURE`, extra `extensions.*`) are
@@ -156,22 +156,22 @@ every tool call, use `approve`. See
| Symptom | Cause | Fix | | Symptom | Cause | Fix |
| --- | --- | --- | | --- | --- | --- |
| `hermes: could not decrypt secrets/openrouter-api-key.yaml` | Host isn't a recipient on the sops rule | `homelab client add <host> --finalize-pubkey <age1...> --with-hermes` from hubris | | `nomos: could not decrypt secrets/openrouter-api-key.yaml` | Host isn't a recipient on the sops rule | `homelab client add <host> --finalize-pubkey <age1...> --with-nomos` from hubris |
| `hermes: openrouter-api-key.yaml still contains the placeholder` | No real key has been seeded yet | See "Seeding the OpenRouter key" above | | `nomos: openrouter-api-key.yaml still contains the placeholder` | No real key has been seeded yet | See "Seeding the OpenRouter key" above |
| Goose hangs on first `hermes` invocation with no output | Goose's interactive `configure` ran on first launch and is awaiting input | Re-run; the installer is supposed to skip it (CONFIGURE=false). If it persists, run `goose configure` once manually in a real terminal to commit the config. | | Goose hangs on first `nomos` invocation with no output | Goose's interactive `configure` ran on first launch and is awaiting input | Re-run; the installer is supposed to skip it (CONFIGURE=false). If it persists, run `goose configure` once manually in a real terminal to commit the config. |
| `homelab` extension fails to connect / no MCP tools listed | MCP server upgraded in Go rewrite (`internal/mcp/server.go`, Streamable HTTP via official MCP SDK). Old FastMCP SSE transport is deprecated. | Run `docker compose --profile full up` on mac-mini, or wait for the production cutover from apps/105. | | `homelab` extension fails to connect / no MCP tools listed | MCP server upgraded in Go rewrite (`internal/mcp/server.go`, Streamable HTTP via official MCP SDK). Old FastMCP SSE transport is deprecated. | Run `docker compose --profile full up` on mac-mini, or wait for the production cutover from apps/105. |
| `goose: command not found` after bootstrap | Upstream installer dropped binary in `~/.local/bin/` but `/usr/local/bin/goose` symlink didn't land | Re-run bootstrap with `--with-hermes`; the symlink step is at the end of the install block. If still missing, `ln -sfn ~/.local/bin/goose /usr/local/bin/goose` manually. | | `goose: command not found` after bootstrap | Upstream installer dropped binary in `~/.local/bin/` but `/usr/local/bin/goose` symlink didn't land | Re-run bootstrap with `--with-nomos`; the symlink step is at the end of the install block. If still missing, `ln -sfn ~/.local/bin/goose /usr/local/bin/goose` manually. |
| Tool calls hit OpenRouter rate limits | One shared key across many hosts | Future: per-host keys; for now, see the rate-limits guide referenced in `goose info -v`. | | Tool calls hit OpenRouter rate limits | One shared key across many hosts | Future: per-host keys; for now, see the rate-limits guide referenced in `goose info -v`. |
## Cross-references ## Cross-references
- [agent-enrollment.md](agent-enrollment.md) — base client onboarding the - [agent-enrollment.md](agent-enrollment.md) — base client onboarding the
Hermes flow assumes is done. Nomos flow assumes is done.
- [`HERMES.md`](../HERMES.md) — the persona the Hermes agent reads on every - [`NOMOS.md`](../NOMOS.md) — the persona the Nomos agent reads on every
session start (via `~/.config/goose/.goosehints`). session start (via `~/.config/goose/.goosehints`).
- [`bin/hermes`](../../bin/hermes) — the wrapper that decrypts the OpenRouter key - [`bin/nomos`](../../bin/nomos) — the wrapper that decrypts the OpenRouter key
and execs `goose session`. and execs `goose session`.
- [`bootstrap.sh`](../../bootstrap.sh) — the `--with-hermes` flag's install block. - [`bootstrap.sh`](../../bootstrap.sh) — the `--with-nomos` flag's install block.
## Follow-ups ## Follow-ups
@@ -182,7 +182,7 @@ every tool call, use `approve`. See
that's changed, the `homelab` MCP extension in Goose will fail to connect. that's changed, the `homelab` MCP extension in Goose will fail to connect.
The developer extension (shell + edit) covers most ops without it; this is The developer extension (shell + edit) covers most ops without it; this is
a polish item, not a blocker. a polish item, not a blocker.
2. **Per-host OpenRouter keys** for billing attribution. Today all Hermes 2. **Per-host OpenRouter keys** for billing attribution. Today all Nomos
hosts share one key. hosts share one key.
3. **Pin the model version** rather than tracking `nousresearch/hermes-4-405b` 3. **Pin the model version** rather than tracking `nousresearch/hermes-4-405b`
directly — OpenRouter periodically rotates the underlying weights. directly — OpenRouter periodically rotates the underlying weights.
@@ -204,7 +204,7 @@ templating + `~/bin/caveman_wrapper.sh` + `~/templates/*.txt` for token-
efficient CLI output. Replaces raw `git pull` in launchd/systemd timers. efficient CLI output. Replaces raw `git pull` in launchd/systemd timers.
Also created `tools/caveman/` with the wrapper script, JS renderer, and Also created `tools/caveman/` with the wrapper script, JS renderer, and
templates — the canonical source for all agent hosts. templates — the canonical source for all agent hosts.
Captures the Hermes-on-Goose onboarding flow added in the same commit as Captures the Nomos-on-Goose onboarding flow added in the same commit as
`bootstrap.sh --with-hermes`, `bin/hermes`, the sops rule for `bootstrap.sh --with-nomos`, `bin/nomos`, the sops rule for
`secrets/openrouter-api-key.yaml`, and the `homelab client add --with-hermes` `secrets/openrouter-api-key.yaml`, and the `homelab client add --with-nomos`
extension. MCP streamable_http migration is queued as follow-up #1. extension. MCP streamable_http migration is queued as follow-up #1.

View File

@@ -30,4 +30,4 @@ Code/commits/PRs: write normal. "stop caveman" or "normal mode": revert. Level p
--- ---
Source: https://github.com/JuliusBrussee/caveman Source: https://github.com/JuliusBrussee/caveman
Copy to `~/.hermes/skills/` for Hermes Agent, or `~/.claude/projects/<name>/SKILL.md` for Claude Code. Copy to `~/.nomos/skills/` for Nomos agent, or `~/.claude/projects/<name>/SKILL.md` for Claude Code.

View File

@@ -26,7 +26,7 @@ the full walkthrough; this runbook is the risk/lifecycle framing.
routed `192.168.8.0/24` Netbird network resource. Skip this step for routed `192.168.8.0/24` Netbird network resource. Skip this step for
LAN-only nodes; do it (out-of-band, console or setup key) only for LAN-only nodes; do it (out-of-band, console or setup key) only for
hosts that need independent off-LAN reachability. hosts that need independent off-LAN reachability.
3. On the new host: run `bootstrap.sh` (add `--with-hermes` to also 3. On the new host: run `bootstrap.sh` (add `--with-nomos` to also
enroll the Hermes agent). This provisions `/etc/age/key.txt`, the enroll the Hermes agent). This provisions `/etc/age/key.txt`, the
sync timer, and prints an age pubkey. sync timer, and prints an age pubkey.
4. Back on an enrolled client: `homelab client add <hostname> 4. Back on an enrolled client: `homelab client add <hostname>

4
.gitignore vendored
View File

@@ -5,9 +5,9 @@ __pycache__/
# Regenerated every scheduler run; ephemeral health-probe cache. # Regenerated every scheduler run; ephemeral health-probe cache.
oikos/state.json oikos/state.json
# Compiled binaries (Go rewrite — bin/oikos, bin/hermes) # Compiled binaries (Go rewrite — bin/oikos, bin/nomos)
bin/oikos bin/oikos
bin/hermes bin/nomos
oikos/oikos oikos/oikos
# Legacy Python oikos (superseded by cmd/oikos Go binary — Phase 1-6 rewrite). # Legacy Python oikos (superseded by cmd/oikos Go binary — Phase 1-6 rewrite).

View File

@@ -86,11 +86,11 @@ creation_rules:
age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs age1z62ff2ak9zj5ctcvaxwyyhedwjvlwgm2dkn9nk3wrwk8fkavcpmsqwc2vs
- path_regex: archive/secrets-sops-backupopenrouter-api-key\.yaml$ - path_regex: archive/secrets-sops-backupopenrouter-api-key\.yaml$
# OpenRouter API key consumed by the `hermes` wrapper (bin/hermes) when # OpenRouter API key consumed by the `nomos` wrapper (bin/nomos) when
# spawning a Goose session. Recipients are any host that should run a # spawning a Goose session. Recipients are any host that should run a
# Nous-Hermes agent. Add a host's age_pubkey here, then # Nomos agent. Add a host's age_pubkey here, then
# `sops updatekeys -y secrets/openrouter-api-key.yaml`. # `sops updatekeys -y secrets/openrouter-api-key.yaml`.
# See operations/hermes-agent.md. # See operations/nomos-agent.md.
age: >- age: >-
age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6, age1xkklkvnk5z0fsnh6cfgv70hy9ksfy8rdprwerzw4yk3p4p7cxcqs2yvpz6,
age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6, age1vf8h7s8mqsn2q5eadgpdupsj4mwn8zguc77d85ws3xj40sl9rgksx2rxw6,

View File

@@ -124,9 +124,9 @@ per the DB-as-source-of-truth plan.
## 5. Acting on the homelab ## 5. Acting on the homelab
- **Read state**: use MCP tools. Hermes (the AI agent) is the primary - **Read state**: use MCP tools. Nomos (the AI agent) is the primary
operator interface — it has 21 MCP tools for observe/orient/decide/act. operator interface — it has 21 MCP tools for observe/orient/decide/act.
- **Actions** (restart, logs, apt, pct exec): Hermes calls `request_execution` - **Actions** (restart, logs, apt, pct exec): Nomos calls `request_execution`
via MCP. `reversible_low` actions execute immediately; `config_mutation` via MCP. `reversible_low` actions execute immediately; `config_mutation`
and `destructive` actions are queued for operator approval via Matrix. and `destructive` actions are queued for operator approval via Matrix.
- **Secrets**: managed by Infisical (`oikos secret` subcommand for migration). - **Secrets**: managed by Infisical (`oikos secret` subcommand for migration).
@@ -152,10 +152,10 @@ Currently auto-setup:
- **Caveman + templates** (`tools/setup-caveman.sh`): Installs Caveman npm - **Caveman + templates** (`tools/setup-caveman.sh`): Installs Caveman npm
package, wrapper scripts, and compact output templates for token-efficient package, wrapper scripts, and compact output templates for token-efficient
CLI output. Wrapper at `~/bin/caveman_wrapper.sh`. CLI output. Wrapper at `~/bin/caveman_wrapper.sh`.
- **Hermes agent persona** (`tools/setup-hermes-soul.sh`): Provisions - **Nomos agent persona** (`tools/setup-nomos-soul.sh`): Provisions
`~/.hermes/SOUL.md` from `HERMES.md` on Hermes agents. This ensures every `~/.nomos/SOUL.md` from `NOMOS.md` on Nomos agents. This ensures every
Hermes agent follows the canonical homelab persona (token efficiency, source Nomos agent follows the canonical homelab persona (token efficiency, source
of truth hierarchy). No-op on non-Hermes agents. of truth hierarchy). No-op on non-Nomos agents.
To add a new auto-setup, create `tools/<name>.setup.sh` in the repo, To add a new auto-setup, create `tools/<name>.setup.sh` in the repo,
commit and push. All enrolled clients pick it up within 5 minutes. commit and push. All enrolled clients pick it up within 5 minutes.

View File

@@ -41,7 +41,7 @@ curl -fsSL https://git.hubris.network/dtoro/oikos/raw/main/bootstrap.sh | sudo b
# Or with optional tooling: # Or with optional tooling:
curl ... | sudo bash -s -- --with-mcp # wire Claude's MCP config curl ... | sudo bash -s -- --with-mcp # wire Claude's MCP config
curl ... | sudo bash -s -- --with-hermes # install Goose + Hermes curl ... | sudo bash -s -- --with-nomos # install Goose + Nomos
``` ```
This calls `POST /api/v1/clients/enroll` on the Oikos API, which: This calls `POST /api/v1/clients/enroll` on the Oikos API, which:
@@ -56,7 +56,7 @@ This calls `POST /api/v1/clients/enroll` on the Oikos API, which:
### What changes on your machine ### What changes on your machine
- `/opt/homelab/` — agent orientation files (CLIENTS.md, AGENTS.md, OIKOS.md) - `/opt/homelab/` — agent orientation files (CLIENTS.md, AGENTS.md, OIKOS.md)
- `/opt/homelab/tools/` — tooling scripts (caveman, hermes-soul) - `/opt/homelab/tools/` — tooling scripts (caveman, nomos-soul)
- `/etc/age/key.txt` — age private key for SOPS decryption (fallback) - `/etc/age/key.txt` — age private key for SOPS decryption (fallback)
- `/etc/infisical/identity` — Infisical machine identity (primary secrets) - `/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 - Context poller — launchd/systemd timer hits `GET /api/v1/clients/{slug}/context` every 5 minutes for agent file updates

View File

@@ -28,7 +28,7 @@ make build
``` ```
cmd/oikos/ Single-binary entry point cmd/oikos/ Single-binary entry point
cmd/hermes/ Hermes MCP client gateway cmd/nomos/ Nomos MCP client gateway
internal/ All Go packages internal/ All Go packages
httpapi/ REST + MCP server (OpenAPI-generated) httpapi/ REST + MCP server (OpenAPI-generated)
mcp/ MCP tool implementations mcp/ MCP tool implementations
@@ -47,7 +47,7 @@ migrations/ Forward-only SQL migrations (TimescaleDB)
seeds/ Bootstrap YAML: ontology, inventory, policy, knowledge seeds/ Bootstrap YAML: ontology, inventory, policy, knowledge
compose/ Dockerfiles + Caddy config compose/ Dockerfiles + Caddy config
scripts/ Deploy, watchdog, rollback scripts/ Deploy, watchdog, rollback
hermes/ Hermes config, persona, skills nomos/ Nomos config, persona, skills
.agents/ Agent instruction files + skills .agents/ Agent instruction files + skills
plans/ Design documents plans/ Design documents
docs/adr/ Architecture decision records docs/adr/ Architecture decision records

View File

@@ -1,8 +1,8 @@
# Oikos # Oikos
Agentic homelab operating system written in Go. Single binary (`cmd/oikos`), Agentic homelab operating system written in Go. Single binary (`cmd/oikos`),
Docker-deployed on mac-mini, with a standalone Hermes MCP agent gateway Docker-deployed on mac-mini, with a standalone Nomos MCP agent gateway
(`cmd/hermes`). Manages the **hubris** Proxmox homelab autonomously — observes (`cmd/nomos`). Manages the **hubris** Proxmox homelab autonomously — observes
state, classifies actions against policy, executes approved procedures over SSH, state, classifies actions against policy, executes approved procedures over SSH,
learns from outcomes, and escalates when uncertain. learns from outcomes, and escalates when uncertain.
@@ -16,7 +16,7 @@ learns from outcomes, and escalates when uncertain.
# Dev stack (postgres + api + scheduler + notifier) # Dev stack (postgres + api + scheduler + notifier)
docker compose --profile dev up -d docker compose --profile dev up -d
# Full stack (adds Hermes agent gateway) # Full stack (adds Nomos agent gateway)
docker compose --profile full up -d docker compose --profile full up -d
# Build standalone binary # Build standalone binary
@@ -33,7 +33,7 @@ OIKOS_DATABASE_URL="postgres://oikos:oikos_dev@localhost:5432/oikos?sslmode=disa
┌──────────────────────────────────┐ ┌──────────────────────────────────┐
│ mac-mini (Docker) │ │ mac-mini (Docker) │
│ │ │ │
Workstation ─── │ hermes (8092) ──MCP── api (8090) │ Workstation ─── │ nomos (8092) ──MCP── api (8090) │
(mesh) │ MCP gateway REST + MCP │ (mesh) │ MCP gateway REST + MCP │
│ │ │ │
│ scheduler ── notifier ── postgres │ │ scheduler ── notifier ── postgres │
@@ -46,7 +46,7 @@ OIKOS_DATABASE_URL="postgres://oikos:oikos_dev@localhost:5432/oikos?sslmode=disa
| `oikos api` | 8090 | REST API + MCP server (15 tools) | | `oikos api` | 8090 | REST API + MCP server (15 tools) |
| `oikos scheduler` | — | Probe runner, signal lifecycle, metrics | | `oikos scheduler` | — | Probe runner, signal lifecycle, metrics |
| `oikos notifier` | — | Approval tokens, Matrix alerts | | `oikos notifier` | — | Approval tokens, Matrix alerts |
| `hermes serve` | 8092 | MCP client gateway, query routing | | `nomos serve` | 8092 | MCP client gateway, query routing |
## Phases ## Phases
@@ -55,7 +55,7 @@ OIKOS_DATABASE_URL="postgres://oikos:oikos_dev@localhost:5432/oikos?sslmode=disa
| 1 — Ontology + DB | ✅ | TimescaleDB, migrations, seeds, blast_radius | | 1 — Ontology + DB | ✅ | TimescaleDB, migrations, seeds, blast_radius |
| 2 — API | ✅ | OpenAPI-first REST + MCP, auth, SSE, audit | | 2 — API | ✅ | OpenAPI-first REST + MCP, auth, SSE, audit |
| 3 — Control loop | ✅ | Scheduler, actuator, learning, classifier, notifier | | 3 — Control loop | ✅ | Scheduler, actuator, learning, classifier, notifier |
| 4 — Hermes agent | ✅ | Standalone MCP client gateway, agent activity | | 4 — Nomos agent | ✅ | Standalone MCP client gateway, agent activity |
| 5 — Secrets | ✅ | Infisical backend + SOPS fallback, rotation runbooks | | 5 — Secrets | ✅ | Infisical backend + SOPS fallback, rotation runbooks |
| 6 — Deploy | ✅ | CI pipeline, cutover checklist, watchdog, rollback | | 6 — Deploy | ✅ | CI pipeline, cutover checklist, watchdog, rollback |
@@ -71,7 +71,7 @@ curl http://localhost:8090/api/v1/health # fleet health
curl http://localhost:8090/api/v1/agent-activity # agent log curl http://localhost:8090/api/v1/agent-activity # agent log
``` ```
### Hermes queries ### Nomos queries
```bash ```bash
# Structured tool call # Structured tool call
@@ -101,7 +101,7 @@ oikos secret migrate # SOPS → Infisical
``` ```
cmd/oikos/ Go entry point — single binary cmd/oikos/ Go entry point — single binary
cmd/hermes/ Hermes MCP client gateway cmd/nomos/ Nomos MCP client gateway
internal/ Go packages (httpapi, mcp, scheduler, actuator, learning, internal/ Go packages (httpapi, mcp, scheduler, actuator, learning,
notifier, policy, secrets, db, config, ontology, domain, notifier, policy, secrets, db, config, ontology, domain,
knowledge) knowledge)
@@ -110,7 +110,7 @@ migrations/ Forward-only SQL migrations (TimescaleDB)
seeds/ Bootstrap YAML (ontology, inventory, policy, knowledge) seeds/ Bootstrap YAML (ontology, inventory, policy, knowledge)
compose/ Dockerfiles + Caddy config compose/ Dockerfiles + Caddy config
scripts/ Deploy, watchdog, verification, rollback scripts/ Deploy, watchdog, verification, rollback
hermes/ Hermes config, persona, skills nomos/ Nomos config, persona, skills
.agents/ Agent instruction files, shared conventions, skills .agents/ Agent instruction files, shared conventions, skills
archive/ Historical reference (legacy wiki, plans, SOPS backups) archive/ Historical reference (legacy wiki, plans, SOPS backups)
plans/ Design documents (active + done) plans/ Design documents (active + done)

View File

@@ -3,7 +3,7 @@
# #
# Thin client model (rev 2): no git clone, no sync timer. Fetches only the # Thin client model (rev 2): no git clone, no sync timer. Fetches only the
# agent orientation files (CLIENTS.md, AGENTS.md, OIKOS.md) and tooling # agent orientation files (CLIENTS.md, AGENTS.md, OIKOS.md) and tooling
# (caveman, hermes-soul) from the raw Gitea URL. Enrolls via the Oikos API # (caveman, nomos-soul) from the raw Gitea URL. Enrolls via the Oikos API
# to receive an age keypair and Infisical machine identity. A lightweight # to receive an age keypair and Infisical machine identity. A lightweight
# context poller replaces the old 5-minute git pull. # context poller replaces the old 5-minute git pull.
# #
@@ -11,7 +11,7 @@
# curl -fsSL https://git.hubris.network/dtoro/oikos/raw/main/bootstrap.sh \ # curl -fsSL https://git.hubris.network/dtoro/oikos/raw/main/bootstrap.sh \
# | sudo bash # | sudo bash
# curl ... | sudo bash -s -- --with-mcp # wire Claude's .mcp.json # curl ... | sudo bash -s -- --with-mcp # wire Claude's .mcp.json
# curl ... | sudo bash -s -- --with-hermes # install Goose + Hermes # curl ... | sudo bash -s -- --with-nomos # install Goose + Nomos
# curl ... | sudo bash -s -- --dry-run # show what would happen # curl ... | sudo bash -s -- --dry-run # show what would happen
# #
# Prerequisites: # Prerequisites:
@@ -28,11 +28,11 @@ REPO_RAW_URL="${HOMELAB_RAW_URL:-https://git.hubris.network/dtoro/oikos/raw/main
OIKOS_API_URL="${HOMELAB_OIKOS_URL:-https://oikos.hubris.network/api/v1}" OIKOS_API_URL="${HOMELAB_OIKOS_URL:-https://oikos.hubris.network/api/v1}"
CLONE_DIR="${HOMELAB_CONTEXT_DIR:-/opt/homelab}" CLONE_DIR="${HOMELAB_CONTEXT_DIR:-/opt/homelab}"
MCP_URL="${HOMELAB_MCP_URL:-https://mcp.hubris.network/mcp}" MCP_URL="${HOMELAB_MCP_URL:-https://mcp.hubris.network/mcp}"
HERMES_MCP_URI="${HOMELAB_HERMES_MCP_URI:-https://mcp.hubris.network/mcp}" NOMOS_MCP_URI="${HOMELAB_NOMOS_MCP_URI:-https://mcp.hubris.network/mcp}"
HERMES_MODEL="${HOMELAB_HERMES_MODEL:-nousresearch/hermes-4-405b}" NOMOS_MODEL="${HOMELAB_NOMOS_MODEL:-nousresearch/hermes-4-405b}"
WITH_MCP=0 WITH_MCP=0
WITH_HERMES=0 WITH_NOMOS=0
DRY_RUN=0 DRY_RUN=0
GITEA_TOKEN="${HOMELAB_GITEA_TOKEN:-}" GITEA_TOKEN="${HOMELAB_GITEA_TOKEN:-}"
@@ -79,7 +79,7 @@ detect_mesh_ip() {
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
case "$1" in case "$1" in
--with-mcp) WITH_MCP=1 ;; --with-mcp) WITH_MCP=1 ;;
--with-hermes) WITH_HERMES=1 ;; --with-nomos) WITH_NOMOS=1 ;;
--dry-run) DRY_RUN=1 ;; --dry-run) DRY_RUN=1 ;;
--gitea-token) GITEA_TOKEN="$2"; shift ;; --gitea-token) GITEA_TOKEN="$2"; shift ;;
--gitea-user) GITEA_USER="$2"; shift ;; --gitea-user) GITEA_USER="$2"; shift ;;
@@ -148,7 +148,7 @@ done
# ── fetch tools ────────────────────────────────────────────────────── # ── fetch tools ──────────────────────────────────────────────────────
log "fetching tools..." log "fetching tools..."
for tool in setup-caveman.sh setup-hermes-soul.sh caveman.js caveman_wrapper.sh post-pull.sh; do for tool in setup-caveman.sh setup-nomos-soul.sh caveman.js caveman_wrapper.sh post-pull.sh; do
url="$REPO_RAW_URL/tools/${tool}" url="$REPO_RAW_URL/tools/${tool}"
dest="$CLONE_DIR/tools/${tool}" dest="$CLONE_DIR/tools/${tool}"
dry mkdir -p "$(dirname "$dest")" dry mkdir -p "$(dirname "$dest")"
@@ -319,15 +319,15 @@ if [ "$WITH_MCP" -eq 1 ]; then
log " + MCP wired to $MCP_URL" log " + MCP wired to $MCP_URL"
fi fi
# ── --with-hermes: install Goose + Hermes wrapper ──────────────────── # ── --with-nomos: install Goose + Nomos wrapper ────────────────────
if [ "$WITH_HERMES" -eq 1 ]; then if [ "$WITH_NOMOS" -eq 1 ]; then
log "installing Hermes agent..." log "installing Nomos agent..."
GOOSE_URL="https://github.com/block/goose/releases/latest/download/goose-${OS}-${ARCH:-amd64}" GOOSE_URL="https://github.com/block/goose/releases/latest/download/goose-${OS}-${ARCH:-amd64}"
if [ "$OS" = Darwin ]; then GOOSE_URL="https://github.com/block/goose/releases/latest/download/goose-darwin-${ARCH:-arm64}"; fi if [ "$OS" = Darwin ]; then GOOSE_URL="https://github.com/block/goose/releases/latest/download/goose-darwin-${ARCH:-arm64}"; fi
dry curl -fsSL "$GOOSE_URL" -o /usr/local/bin/goose 2>/dev/null && chmod +x /usr/local/bin/goose || warn "goose not installed" dry curl -fsSL "$GOOSE_URL" -o /usr/local/bin/goose 2>/dev/null && chmod +x /usr/local/bin/goose || warn "goose not installed"
# Drop Hermes persona # Drop Nomos persona
cp "$CLONE_DIR/HERMES.md" "$CLONE_DIR/.agents/HERMES.md" 2>/dev/null || true cp "$CLONE_DIR/NOMOS.md" "$CLONE_DIR/.agents/NOMOS.md" 2>/dev/null || true
log " + Hermes agent installed" log " + Nomos agent installed"
fi fi
# ── netbird SSH JWT cache ──────────────────────────────────────────── # ── netbird SSH JWT cache ────────────────────────────────────────────

View File

@@ -17,18 +17,18 @@ import (
func main() { func main() {
if len(os.Args) < 2 { if len(os.Args) < 2 {
fmt.Fprintln(os.Stderr, "usage: hermes serve") fmt.Fprintln(os.Stderr, "usage: nomos serve")
os.Exit(1) os.Exit(1)
} }
mcpURL := os.Getenv("HERMES_MCP_URL") mcpURL := os.Getenv("NOMOS_MCP_URL")
if mcpURL == "" { if mcpURL == "" {
mcpURL = "http://localhost:8090/mcp" mcpURL = "http://localhost:8090/mcp"
} }
agentSlug := os.Getenv("HERMES_AGENT_SLUG") agentSlug := os.Getenv("NOMOS_AGENT_SLUG")
if agentSlug == "" { if agentSlug == "" {
agentSlug = "agent:hermes" agentSlug = "agent:nomos"
} }
switch os.Args[1] { switch os.Args[1] {
@@ -38,7 +38,7 @@ func main() {
client, err := newMCPClient(mcpURL) client, err := newMCPClient(mcpURL)
if err != nil { if err != nil {
slog.Error("hermes: mcp connect", "url", mcpURL, "error", err) slog.Error("nomos: mcp connect", "url", mcpURL, "error", err)
os.Exit(1) os.Exit(1)
} }
@@ -51,21 +51,21 @@ func main() {
handleQuery(w, r, client, agentSlug, mcpURL) handleQuery(w, r, client, agentSlug, mcpURL)
}) })
addr := os.Getenv("HERMES_LISTEN") addr := os.Getenv("NOMOS_LISTEN")
if addr == "" { if addr == "" {
addr = ":8092" addr = ":8092"
} }
srv := &http.Server{Addr: addr, Handler: mux} srv := &http.Server{Addr: addr, Handler: mux}
go func() { go func() {
slog.Info("hermes: gateway listening", "addr", addr, "mcp", mcpURL) slog.Info("nomos: gateway listening", "addr", addr, "mcp", mcpURL)
if err := srv.ListenAndServe(); err != http.ErrServerClosed { if err := srv.ListenAndServe(); err != http.ErrServerClosed {
slog.Error("hermes: serve", "error", err) slog.Error("nomos: serve", "error", err)
} }
}() }()
<-ctx.Done() <-ctx.Done()
slog.Info("hermes: shutting down") slog.Info("nomos: shutting down")
srv.Shutdown(context.Background()) srv.Shutdown(context.Background())
client.close() client.close()
@@ -108,7 +108,7 @@ func handleQuery(w http.ResponseWriter, r *http.Request, client *mcpClient, agen
duration := time.Since(start).Milliseconds() duration := time.Since(start).Milliseconds()
if err != nil { if err != nil {
slog.Error("hermes: query failed", "query", req.Query, "error", err) slog.Error("nomos: query failed", "query", req.Query, "error", err)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(map[string]any{ json.NewEncoder(w).Encode(map[string]any{
"error": err.Error(), "error": err.Error(),
@@ -204,7 +204,7 @@ func newMCPClient(baseURL string) (*mcpClient, error) {
resp, err := c.doRequest("initialize", map[string]any{ resp, err := c.doRequest("initialize", map[string]any{
"protocolVersion": "2024-11-05", "protocolVersion": "2024-11-05",
"capabilities": map[string]any{}, "capabilities": map[string]any{},
"clientInfo": map[string]any{"name": "hermes", "version": "2.0"}, "clientInfo": map[string]any{"name": "nomos", "version": "2.0"},
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("initialize: %w", err) return nil, fmt.Errorf("initialize: %w", err)
@@ -217,7 +217,7 @@ func newMCPClient(baseURL string) (*mcpClient, error) {
// Send initialized notification // Send initialized notification
c.doRequest("notifications/initialized", map[string]any{}) c.doRequest("notifications/initialized", map[string]any{})
slog.Info("hermes: mcp connected", "session", c.sessionID[:16]+"...") slog.Info("nomos: mcp connected", "session", c.sessionID[:16]+"...")
return c, nil return c, nil
} }

View File

@@ -120,7 +120,7 @@ Roles:
knowledge Convert wiki to knowledge seed (one-shot) knowledge Convert wiki to knowledge seed (one-shot)
version Print version info version Print version info
The operator interface is Hermes (MCP agent) — no CLI needed. The operator interface is Nomos (MCP agent) — no CLI needed.
Environment: Environment:
OIKOS_DATABASE_URL Postgres connection string OIKOS_DATABASE_URL Postgres connection string
OIKOS_API_LISTEN API listen address (default :8090) OIKOS_API_LISTEN API listen address (default :8090)

View File

@@ -17,12 +17,12 @@ oikos.hubris.network {
} }
} }
# Oikos MCP endpoint (Hermes agents) — no auth required # Oikos MCP endpoint (agents) — no auth required
mcp.hubris.network { mcp.hubris.network {
reverse_proxy <mac-mini-mesh-ip>:8090 reverse_proxy <mac-mini-mesh-ip>:8090
} }
# Hermes gateway (workstation access) # Nomos gateway (workstation access) — formerly hermes.hubris.network
hermes.hubris.network { nomos.hubris.network {
reverse_proxy <mac-mini-mesh-ip>:8092 reverse_proxy <mac-mini-mesh-ip>:8092
} }

View File

@@ -1,25 +0,0 @@
# Hermes agent container — standalone MCP client gateway (Phase 4)
FROM golang:1.26-alpine AS builder
RUN apk add --no-cache git ca-certificates
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /hermes -tags timetzdata -ldflags="-s -w" ./cmd/hermes
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /hermes /hermes
COPY hermes/ /app/hermes/
ENV HERMES_MCP_URL=http://api:8090/mcp
ENV HERMES_AGENT_SLUG=agent:hermes
ENV HERMES_LISTEN=:8092
EXPOSE 8092
ENTRYPOINT ["/hermes", "serve"]

25
compose/nomos/Dockerfile Normal file
View File

@@ -0,0 +1,25 @@
# Nomos agent container — standalone MCP client gateway (Phase 4)
FROM golang:1.26-alpine AS builder
RUN apk add --no-cache git ca-certificates
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /nomos -tags timetzdata -ldflags="-s -w" ./cmd/nomos
FROM gcr.io/distroless/static:nonroot
COPY --from=builder /nomos /nomos
COPY nomos/ /app/nomos/
ENV NOMOS_MCP_URL=http://api:8090/mcp
ENV NOMOS_AGENT_SLUG=agent:nomos
ENV NOMOS_LISTEN=:8092
EXPOSE 8092
ENTRYPOINT ["/nomos", "serve"]

View File

@@ -60,7 +60,7 @@ services:
OIKOS_API_LISTEN: ":8090" OIKOS_API_LISTEN: ":8090"
OIKOS_ENV: dev OIKOS_ENV: dev
OIKOS_DEBUG: "true" OIKOS_DEBUG: "true"
OIKOS_HERMES_AGENT_SLUG: ${OIKOS_HERMES_AGENT_SLUG:-agent:hermes} OIKOS_NOMOS_AGENT_SLUG: ${OIKOS_NOMOS_AGENT_SLUG:-agent:nomos}
volumes: volumes:
- ${OIKOS_SSH_KEY_PATH:-~/.ssh/id_ed25519}:/etc/oikos/ssh_key:ro - ${OIKOS_SSH_KEY_PATH:-~/.ssh/id_ed25519}:/etc/oikos/ssh_key:ro
ports: ports:
@@ -107,18 +107,18 @@ services:
stop_signal: SIGTERM stop_signal: SIGTERM
stop_grace_period: 30s stop_grace_period: 30s
# Hermes agent gateway (Phase 4) — mesh-published :8092 # Nomos agent gateway (Phase 4) — mesh-published :8092
hermes: nomos:
build: build:
context: . context: .
dockerfile: compose/hermes/Dockerfile dockerfile: compose/nomos/Dockerfile
profiles: ["full"] profiles: ["full"]
depends_on: depends_on:
api: api:
condition: service_started condition: service_started
environment: environment:
HERMES_MCP_URL: http://api:8090/mcp NOMOS_MCP_URL: http://api:8090/mcp
HERMES_AGENT_SLUG: agent:hermes NOMOS_AGENT_SLUG: agent:nomos
ports: ports:
- "8092:8092" - "8092:8092"
stop_signal: SIGTERM stop_signal: SIGTERM

View File

@@ -230,4 +230,13 @@ sequenceDiagram
`GET /clients/{slug}/context`. `GET /clients/{slug}/context`.
- **The DB is the single source of truth.** All state transitions, - **The DB is the single source of truth.** All state transitions,
audit entries, and event emissions go through Postgres. The scheduler, audit entries, and event emissions go through Postgres. The scheduler,
actuator, notifier, and API all read/write the same tables. actuator, notifier, and API all read/write the same tables.
---
**2026-07-08 — renamed to Nomos.** The Hermes agent gateway was renamed to
Nomos (from *oikonomos*, the steward of the oikos) under the
[Nomos resident agent plan](../../plans/2026-07-08-nomos-resident-agent.md),
N0 milestone. The gateway binary (`cmd/nomos`), Docker service, DB slug
(`agent:nomos`), and all referencing docs were updated. All architectural
principles in this ADR remain unchanged.

View File

@@ -70,7 +70,7 @@ curl http://localhost:8090/healthz
# Entity count matches # Entity count matches
curl -s http://localhost:8090/api/v1/entities?limit=1 | jq '.items | length' curl -s http://localhost:8090/api/v1/entities?limit=1 | jq '.items | length'
# MCP tools working (via Hermes) # MCP tools working (via Nomos)
curl -s http://localhost:8092/query -d '{"tool":"get_health_summary"}' curl -s http://localhost:8092/query -d '{"tool":"get_health_summary"}'
``` ```

View File

@@ -20,7 +20,7 @@ type Config struct {
// Auth (Phase 2: static bearer tokens + OIDC JWT) // Auth (Phase 2: static bearer tokens + OIDC JWT)
APIToken string // operator/CI bearer token for the REST API APIToken string // operator/CI bearer token for the REST API
MCPBearerToken string // shared secret for Hermes→API MCP calls MCPBearerToken string // shared secret for Nomos→API MCP calls
OIDCIssuer string // OIDC issuer URL for JWT validation (e.g. https://authentik.example.com/application/o/oikos/) OIDCIssuer string // OIDC issuer URL for JWT validation (e.g. https://authentik.example.com/application/o/oikos/)
OIDCClientID string // OIDC client ID (aud claim expected in JWT) OIDCClientID string // OIDC client ID (aud claim expected in JWT)
@@ -54,9 +54,9 @@ type Config struct {
// Approval HMAC secret (Phase 3) // Approval HMAC secret (Phase 3)
ApprovalHMACSecret string ApprovalHMACSecret string
// Hermes agent entity ID (Phase 4) // Nomos agent entity ID (Phase 4)
HermesAgentID string NomosAgentID string
HermesAgentSlug string NomosAgentSlug string
// Infisical (Phase 5) // Infisical (Phase 5)
InfisicalSiteURL string InfisicalSiteURL string
@@ -151,11 +151,11 @@ func FromEnv() Config {
if v := os.Getenv("OIKOS_APPROVAL_HMAC_SECRET"); v != "" { if v := os.Getenv("OIKOS_APPROVAL_HMAC_SECRET"); v != "" {
c.ApprovalHMACSecret = v c.ApprovalHMACSecret = v
} }
if v := os.Getenv("OIKOS_HERMES_AGENT_ID"); v != "" { if v := os.Getenv("OIKOS_NOMOS_AGENT_ID"); v != "" {
c.HermesAgentID = v c.NomosAgentID = v
} }
if v := os.Getenv("OIKOS_HERMES_AGENT_SLUG"); v != "" { if v := os.Getenv("OIKOS_NOMOS_AGENT_SLUG"); v != "" {
c.HermesAgentSlug = v c.NomosAgentSlug = v
} }
// Phase 5: Infisical secrets // Phase 5: Infisical secrets

View File

@@ -137,16 +137,16 @@ func NewHandler(ctx context.Context, pool *db.Pool, cfg config.Config) http.Hand
r.With(combinedAuth(cfg)).Get("/api/v1/events/stream", s.serveSSE) r.With(combinedAuth(cfg)).Get("/api/v1/events/stream", s.serveSSE)
// Mount MCP at /mcp (plan R3-10) // Mount MCP at /mcp (plan R3-10)
hermesAgentID := uuid.Nil nomosAgentID := uuid.Nil
if cfg.HermesAgentID != "" { if cfg.NomosAgentID != "" {
if id, err := uuid.Parse(cfg.HermesAgentID); err == nil { if id, err := uuid.Parse(cfg.NomosAgentID); err == nil {
hermesAgentID = id nomosAgentID = id
} }
} }
if hermesAgentID == uuid.Nil && cfg.HermesAgentSlug != "" { if nomosAgentID == uuid.Nil && cfg.NomosAgentSlug != "" {
_ = pool.QueryRow(ctx, "SELECT id FROM entities WHERE slug = $1", cfg.HermesAgentSlug).Scan(&hermesAgentID) _ = pool.QueryRow(ctx, "SELECT id FROM entities WHERE slug = $1", cfg.NomosAgentSlug).Scan(&nomosAgentID)
} }
r.With(combinedAuth(cfg)).Handle("/mcp", mcphandler.NewHandler(pool, cfg.MCPBearerToken, hermesAgentID)) r.With(combinedAuth(cfg)).Handle("/mcp", mcphandler.NewHandler(pool, cfg.MCPBearerToken, nomosAgentID))
return r return r
} }

View File

@@ -37,7 +37,7 @@ func objSchema(props ...prop) *jsonschema.Schema {
} }
// NewHandler creates an http.Handler that serves the Oikos MCP server. // NewHandler creates an http.Handler that serves the Oikos MCP server.
// agentID is the Hermes agent entity UUID; tool calls are logged to agent_activity. // agentID is the Nomos agent entity UUID; tool calls are logged to agent_activity.
func NewHandler(pool *db.Pool, token string, agentID uuid.UUID) http.Handler { func NewHandler(pool *db.Pool, token string, agentID uuid.UUID) http.Handler {
s := newServer(pool, agentID) s := newServer(pool, agentID)
handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server { handler := mcp.NewStreamableHTTPHandler(func(r *http.Request) *mcp.Server {
@@ -258,7 +258,7 @@ func newServer(pool *db.Pool, agentID uuid.UUID) *mcp.Server {
nStr(args["status"])), nil nStr(args["status"])), nil
}) })
register(&mcp.Tool{Name: "request_execution", Description: "Request a gated execution (Hermes-only mutation path). Supported actions: restart, systemctl, pct_exec, apt_upgrade.", register(&mcp.Tool{Name: "request_execution", Description: "Request a gated execution (agent-only mutation path). Supported actions: restart, systemctl, pct_exec, apt_upgrade.",
InputSchema: objSchema( InputSchema: objSchema(
prop{"target", "string", "Target entity slug (e.g. lxc:caddy)"}, prop{"target", "string", "Target entity slug (e.g. lxc:caddy)"},
prop{"action", "string", "Action: restart, systemctl, pct_exec, apt_upgrade"}, prop{"action", "string", "Action: restart, systemctl, pct_exec, apt_upgrade"},

View File

@@ -511,7 +511,7 @@ hosts:
192.168.8.0/24. Re-enroll in mesh as a follow-up if off-LAN access 192.168.8.0/24. Re-enroll in mesh as a follow-up if off-LAN access
to this host itself (not just its future guests) is needed. to this host itself (not just its future guests) is needed.
- First step of the planned library-SSD migration — see - First step of the planned library-SSD migration — see
.hermes/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md .nomos/plans/2026-06-03_110000-library-ssd-migration-to-ludo-mini.md
(filename kept as-is, it's a historical planning doc). Only Phase 1 (filename kept as-is, it's a historical planning doc). Only Phase 1
(Proxmox install + cluster join) is done; no physical (Proxmox install + cluster join) is done; no physical
drive move, service migration, or GPU passthrough has happened yet. drive move, service migration, or GPU passthrough has happened yet.
@@ -555,7 +555,7 @@ archaeology:
kind: lxc kind: lxc
pve_id: 123 pve_id: 123
destroyed: 2026-06-04 destroyed: 2026-06-04
reason: replaced by Hermes Agent on mac-mini; monitoring moved to homelab-health-watchdog cron reason: replaced by Nomos Agent on mac-mini; monitoring moved to homelab-health-watchdog cron
plato: plato:
kind: lxc kind: lxc
pve_id: 126 pve_id: 126

View File

@@ -0,0 +1,18 @@
-- 014_rename_agent_hermes_to_nomos.up.sql
-- Rename the Hermes agent entity to Nomos (N0 milestone).
-- Identity-preserving: the UUID, relationships, and audit history survive.
-- The matching seed upsert will no-op because it upserts by slug.
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM entities WHERE slug = 'agent:hermes') THEN
UPDATE entities
SET slug = 'agent:nomos',
name = 'nomos',
attributes = jsonb_set(attributes, '{name}', '"nomos"'),
updated_at = now()
WHERE slug = 'agent:hermes'
AND NOT EXISTS (SELECT 1 FROM entities WHERE slug = 'agent:nomos');
END IF;
END
$$;

View File

@@ -1,6 +1,6 @@
# SOUL.md — Hermes agent persona (Phase 4, container runtime) # SOUL.md — Nomos agent persona (Phase 4, container runtime)
You are **Hermes**, the homelab AI agent running in a Docker container on You are **Nomos** (from *oikonomos*, the steward of the oikos), the homelab AI agent running in a Docker container on
mac-mini. You operate in **gateway mode** on mesh-only port 8092. mac-mini. You operate in **gateway mode** on mesh-only port 8092.
## Source of truth ## Source of truth
@@ -45,6 +45,6 @@ describing state, be concise — the operator reads your output in Matrix.
## Skills ## Skills
Skills live in `/app/hermes/skills/`. Load a skill when its description Skills live in `/app/nomos/skills/`. Load a skill when its description
matches the task. The `homelab-ops` skill covers: matches the task. The `homelab-ops` skill covers:
- Health checks, signal triage, pattern validation, and escalation flow. - Health checks, signal triage, pattern validation, and escalation flow.

View File

@@ -1,16 +1,16 @@
# Hermes agent config — standalone MCP client gateway (Phase 4) # Nomos agent config — standalone MCP client gateway (Phase 4)
mcp: mcp:
endpoint: ${HERMES_MCP_URL}?session_id=${HERMES_SESSION_ID} endpoint: ${NOMOS_MCP_URL}?session_id=${NOMOS_SESSION_ID}
transport: streamable_http transport: streamable_http
server: server:
listen: ${HERMES_LISTEN} listen: ${NOMOS_LISTEN}
mesh_only: true mesh_only: true
agent: agent:
name: hermes name: nomos
slug: ${HERMES_AGENT_SLUG} slug: ${NOMOS_AGENT_SLUG}
query_routing: query_routing:
# Maps natural-language query patterns to MCP tools # Maps natural-language query patterns to MCP tools

View File

@@ -6,7 +6,7 @@
## Overview ## Overview
Standard operating procedures for the Hermes agent managing the hubris Standard operating procedures for the Nomos agent managing the hubris
homelab. All mutations route through `request_execution` → Oikos policy homelab. All mutations route through `request_execution` → Oikos policy
gating → actuator (SSH). gating → actuator (SSH).
@@ -41,5 +41,8 @@ gating → actuator (SSH).
## Changelog ## Changelog
### 2026-07-08 — rename to Nomos
Agent renamed from Hermes to Nomos (N0 milestone).
### 2026-07-07 — initial Phase 4 skill ### 2026-07-07 — initial Phase 4 skill
Baseline homelab operations skill for Hermes container. Baseline homelab operations skill for Nomos container.

View File

@@ -1,6 +1,6 @@
# 2026-07-08 — Nomos resident agent (renames Hermes) # 2026-07-08 — Nomos resident agent (renames Hermes)
**Status:** Planned **Status:** In Progress — N0 complete 2026-07-08
## Goal ## Goal

View File

@@ -12,7 +12,7 @@ went sideways, open an investigation.
| 2026-07-08 | [Plan vs implementation cross-reference](2026-07-08-plan-implementation-audit.md) | Planned | | 2026-07-08 | [Plan vs implementation cross-reference](2026-07-08-plan-implementation-audit.md) | Planned |
| 2026-07-08 | [Oikos gaps, broken things, and improvements](2026-07-08-oikos-gaps-and-improvements.md) | Planned | | 2026-07-08 | [Oikos gaps, broken things, and improvements](2026-07-08-oikos-gaps-and-improvements.md) | Planned |
| 2026-07-08 | [Control room web UI](2026-07-08-control-room-webui.md) | Planned | | 2026-07-08 | [Control room web UI](2026-07-08-control-room-webui.md) | Planned |
| 2026-07-08 | [Nomos resident agent (renames Hermes)](2026-07-08-nomos-resident-agent.md) | Planned | | 2026-07-08 | [Nomos resident agent (renames Hermes)](2026-07-08-nomos-resident-agent.md) | In Progress |
## Done ## Done

View File

@@ -6,7 +6,7 @@ Status: [x] = done, [ ] = pending
- [x] **Backup**: `pg_dump oikos > backups/pre-cutover-20260707.sql` (145K) - [x] **Backup**: `pg_dump oikos > backups/pre-cutover-20260707.sql` (145K)
- [x] **CI green**: pushed to main, `.gitea/workflows/ci.yml` exists - [x] **CI green**: pushed to main, `.gitea/workflows/ci.yml` exists
- [x] **Deploy test**: Docker stack running with api + scheduler + notifier + hermes - [x] **Deploy test**: Docker stack running with api + scheduler + notifier + nomos
- [x] **Caddy config**: `compose/caddy/Caddyfile.oikos` pushed to `dtoro/caddy-conf` (ed20908). Auto-deploys to caddy (121). - [x] **Caddy config**: `compose/caddy/Caddyfile.oikos` pushed to `dtoro/caddy-conf` (ed20908). Auto-deploys to caddy (121).
- [x] **DNS**: `oikos.hubris.network` already resolves to 192.168.8.175 (mac-mini mesh) - [x] **DNS**: `oikos.hubris.network` already resolves to 192.168.8.175 (mac-mini mesh)
- [x] **Secrets**: Infisical bootstrapped + migration complete 2026-07-07. All 11 SOPS secrets migrated to Infisical (oikos project, dev env). Machine identity `oikos-api` has RW access verified via Go SDK. ENCRYPTION_KEY must be 32-char raw string (docs incorrect). SOPS fallback preserved for DR. secrets-issuance decommissioned — stopped/disabled on apps/105; superseded by Infisical. - [x] **Secrets**: Infisical bootstrapped + migration complete 2026-07-07. All 11 SOPS secrets migrated to Infisical (oikos project, dev env). Machine identity `oikos-api` has RW access verified via Go SDK. ENCRYPTION_KEY must be 32-char raw string (docs incorrect). SOPS fallback preserved for DR. secrets-issuance decommissioned — stopped/disabled on apps/105; superseded by Infisical.
@@ -23,7 +23,7 @@ Status: [x] = done, [ ] = pending
## Post-cutover verification ## Post-cutover verification
- [x] **./scripts/verify-phase6.sh** — all 14 checks pass - [x] **./scripts/verify-phase6.sh** — all 14 checks pass
- [x] **Hermes query**: `curl http://localhost:8092/query -d '{"query":"fleet health"}'` → HTTP 200 - [x] **Nomos query**: `curl http://localhost:8092/query -d '{"query":"fleet health"}'` → HTTP 200
- [x] **Agent activity**: `curl http://localhost:8090/api/v1/agent-activity` → returns data - [x] **Agent activity**: `curl http://localhost:8090/api/v1/agent-activity` → returns data
- [x] **Scheduler ticking**: 30s ticks logged - [x] **Scheduler ticking**: 30s ticks logged
- [x] **Notifier polling**: running - [x] **Notifier polling**: running

View File

@@ -27,7 +27,7 @@ check "4. Scheduler: check pass" "http://localhost:8090/api/v1/check
check "5. Actuator: executions endpoint" "http://localhost:8090/api/v1/executions" 200 check "5. Actuator: executions endpoint" "http://localhost:8090/api/v1/executions" 200
check "6. Learning: patterns endpoint" "http://localhost:8090/api/v1/patterns" 200 check "6. Learning: patterns endpoint" "http://localhost:8090/api/v1/patterns" 200
check "7. Classifier: risk classes" "http://localhost:8090/api/v1/policy/risk-classes" 200 check "7. Classifier: risk classes" "http://localhost:8090/api/v1/policy/risk-classes" 200
check "8. Hermes: gateway health" "http://localhost:8092/healthz" 200 check "8. Nomos: gateway health" "http://localhost:8092/healthz" 200
check "9. Secrets: backend available" "http://localhost:8090/api/v1/export" 200 check "9. Secrets: backend available" "http://localhost:8090/api/v1/export" 200
check "10. Deploy: events endpoint" "http://localhost:8090/api/v1/events" 200 check "10. Deploy: events endpoint" "http://localhost:8090/api/v1/events" 200
check "11. Knowledge: content search" "http://localhost:8090/healthz" 200 check "11. Knowledge: content search" "http://localhost:8090/healthz" 200

View File

@@ -325,7 +325,7 @@ entities:
attributes: {matrix_id: "@dtoro:avispero"}} attributes: {matrix_id: "@dtoro:avispero"}}
- {slug: "idp:authentik", type: identity-provider, name: authentik, - {slug: "idp:authentik", type: identity-provider, name: authentik,
attributes: {issuer: "https://auth.hubris.network", auth_mode: both}} attributes: {issuer: "https://auth.hubris.network", auth_mode: both}}
- {slug: "agent:hermes", type: agent, name: hermes, - {slug: "agent:nomos", type: agent, name: nomos,
state: active, state: active,
attributes: {gateway_port: 8092, session_mode: smart_approve, note: "Phase 4 — Docker gateway mode"}} attributes: {gateway_port: 8092, session_mode: smart_approve, note: "Phase 4 — Docker gateway mode"}}
- {slug: "agent:oikos", type: agent, name: oikos, - {slug: "agent:oikos", type: agent, name: oikos,
@@ -334,7 +334,7 @@ entities:
# ─── Archaeology (state: destroyed — kept for "what happened to X?") ─ # ─── Archaeology (state: destroyed — kept for "what happened to X?") ─
- {slug: "lxc:claudio-bot", type: lxc, name: claudio-bot, state: destroyed, - {slug: "lxc:claudio-bot", type: lxc, name: claudio-bot, state: destroyed,
attributes: {pve_id: 123, destroyed: "2026-06-04", reason: "replaced by Hermes Agent on mac-mini"}} attributes: {pve_id: 123, destroyed: "2026-06-04", reason: "replaced by Nomos Agent on mac-mini"}}
- {slug: "lxc:plato", type: lxc, name: plato, state: destroyed, - {slug: "lxc:plato", type: lxc, name: plato, state: destroyed,
attributes: {pve_id: 126, destroyed: "2026-06-28", reason: "notes workspace decommissioned; data at /mnt/library/documents/plato"}} attributes: {pve_id: 126, destroyed: "2026-06-28", reason: "notes workspace decommissioned; data at /mnt/library/documents/plato"}}
- {slug: "lxc:mule-photos-new", type: lxc, name: mule-photos-new, state: destroyed, - {slug: "lxc:mule-photos-new", type: lxc, name: mule-photos-new, state: destroyed,
@@ -538,5 +538,5 @@ relationships:
- {source: "lxc:rclone", target: "backup:proton-drive", type: backs-up-to} - {source: "lxc:rclone", target: "backup:proton-drive", type: backs-up-to}
# ─── Governance ──────────────────────────────────────────────────── # ─── Governance ────────────────────────────────────────────────────
- {source: "person:dtoro", target: "agent:hermes", type: owns} - {source: "person:dtoro", target: "agent:nomos", type: owns}
- {source: "person:dtoro", target: "agent:oikos", type: owns} - {source: "person:dtoro", target: "agent:oikos", type: owns}

View File

@@ -531,7 +531,7 @@ entity_types:
domain: identity domain: identity
layer: governance layer: governance
lifecycle: infrastructure # agents are deployed/retired like infrastructure lifecycle: infrastructure # agents are deployed/retired like infrastructure
description: Software agent actor (Hermes, the Oikos control loop). description: Software agent actor (Nomos, the Oikos control loop).
attributes: attributes:
type: object type: object
properties: properties:

View File

@@ -1,14 +0,0 @@
#!/usr/bin/env bash
# setup-hermes-soul.sh — provision Hermes agent persona.
# Copies ~/.hermes/SOUL.md from hermes/SOUL.md. No-op on non-Hermes agents.
set -euo pipefail
CLONE_DIR="${HOMELAB_CONTEXT_DIR:-/opt/homelab}"
if [ -f "$CLONE_DIR/hermes/SOUL.md" ]; then
mkdir -p "$HOME/.hermes"
cp "$CLONE_DIR/hermes/SOUL.md" "$HOME/.hermes/SOUL.md"
echo "[setup-hermes-soul] SOUL.md provisioned"
else
echo "[setup-hermes-soul] no hermes/SOUL.md found; skipping"
fi

14
tools/setup-nomos-soul.sh Normal file
View File

@@ -0,0 +1,14 @@
#!/usr/bin/env bash
# setup-nomos-soul.sh — provision Nomos agent persona.
# Copies ~/.nomos/SOUL.md from nomos/SOUL.md. No-op on non-Nomos agents.
set -euo pipefail
CLONE_DIR="${HOMELAB_CONTEXT_DIR:-/opt/homelab}"
if [ -f "$CLONE_DIR/nomos/SOUL.md" ]; then
mkdir -p "$HOME/.nomos"
cp "$CLONE_DIR/nomos/SOUL.md" "$HOME/.nomos/SOUL.md"
echo "[setup-nomos-soul] SOUL.md provisioned"
else
echo "[setup-nomos-soul] no nomos/SOUL.md found; skipping"
fi