Documentation and repo-hygiene pass following the client/server split:
Plan drift (audited all other active plans against current code):
- oikos-gaps-and-improvements.md: mark Section C and D.5 resolved (both
described cmd/hermes, renamed to cmd/nomos with a real LLM loop since);
refresh ~10 stale file:line citations; fix tool-count (33, not 28).
- liveness-drift-and-ux-cohesion.md: fix stale default-model claim (now
deepseek-v4-pro since 2026-07-10) and "not yet deployed" status.
- nomos-agent-code-review.md: fix C1's citation (one unauthenticated route
to nomos now, not two, after the client/server split).
- wails-desktop-app.md: record the production deploy outcome.
Repo structure: added missing directories to README/CONTRIBUTING layout
tables (checks/, tools/, cmd/webhook/, docs/operations/), fixed a broken
link, added ADR 0015 documenting the auth/CORS/client-split model (there
wasn't one despite CONTRIBUTING's own process requiring it), normalized
ADR 0013/0014's format drift, added an Authentication section to
AGENTS.md/CLIENTS.md (every example call was missing the now-required
bearer header).
Retired the Goose+Nomos workstation flow (bootstrap.sh --with-nomos,
tools/setup-nomos-soul.sh, .agents/operations/nomos-agent.md) and the
Caveman auto-install tooling (tools/setup-caveman.sh, tools/caveman/) —
both superseded by the production containerized Nomos agent, which has
never used either. Kept .agents/shared/caveman.md itself (the terse
writing-style convention agents still follow by reading it).
Deleted the orphaned legacy Python oikos/ directory — nothing imports it,
and bin/homelab (the CLI it was kept for) no longer exists in the repo.
Rewrote .agents/operations/agent-enrollment.md (365 -> ~110 lines) and
commands.md to match the current architecture instead of the retired
`homelab` CLI; migrated the still-true networking prerequisites (Netbird,
split-horizon DNS, SSH key distribution) into the knowledge base as a
runbook via upsert_knowledge rather than duplicating them in markdown.
Updated all 10 .agents/skills/ runbooks referencing the dead CLI with
their real MCP tool / REST API equivalents, or flagged them as needing
verification where no equivalent is confirmed yet.
Two real bugs found and fixed, not just docs:
- The tools/setup-*.sh auto-setup glob was tools/*.setup.sh in THREE
places (tools/post-pull.sh, bootstrap.sh, and internal/httpapi/impl.go's
GetClientContext handler) since the mechanism's introduction on
2026-06-02 — never matched any real filename, so no client has ever
picked up an auto-setup script via git-pull or the context-poller sync.
Fixed all three; the Go server-side fix is the one that actually matters
since it's what the current context-poller mechanism depends on.
- bootstrap.sh removed dead vestigial --gitea-token/--gitea-user flags
(parsed, never consumed) left over from an earlier clone-based model.
Also flagged, not fixed (documented as an open gap in
client-enrollment/SKILL.md): bootstrap.sh tells a freshly-enrolled client
to call POST /api/v1/clients/{slug}/activate to finish enrollment, but
that route doesn't exist in api/openapi.yaml — EnrollClient sets entities
to provisioning and nothing currently transitions them to active.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
8.4 KiB
Agent developer guide
Instructions for AI agents working on the Oikos codebase. Read this after AGENTS.md and OIKOS.md. Human developers: see CONTRIBUTING.md for a human-friendly version.
Codebase map
cmd/oikos/main.go Entry point. Subcommands: api, scheduler, notifier, migrate,
seed, export, secret, all
cmd/nomos/main.go Nomos MCP client gateway (standalone binary, formerly Hermes)
cmd/webhook/main.go Gitea deploy-webhook receiver (push-to-deploy on mac-mini)
internal/httpapi/ REST + MCP server. Chi router. OpenAPI-generated types from
internal/httpapi/gen/api.gen.go. Strict server in impl.go.
internal/mcp/ MCP tool implementations (get_entity, search_knowledge, etc.)
internal/db/ Connection pool (pool.go), seed ingestion (seed.go), DB→YAML
export (export.go), type hierarchy (typetree.go)
internal/db/queries/ SQL query files → sqlc generates internal/db/sqlcgen/
internal/scheduler/ Observe loop: probes, signals, check_defs
internal/actuator/ SSH execution with circuit breaker + retry
internal/learning/ Pattern extraction, anomaly detection
internal/notifier/ Matrix notification + approval token generation
internal/policy/ Risk classifier (read policy.yaml → classify action)
internal/secrets/ Backend abstraction: Infisical (primary) + SOPS (fallback)
internal/domain/ Core types: entities, approvals, executions, signals, patterns
internal/ontology/ Type hierarchy validation, relationship checks
internal/knowledge/ Knowledge YAML seed ingestion
internal/config/ Config loading from env vars
web/ Control-room SPA (Svelte 5) — standalone static build, not
embedded in the oikos binary (plans/2026-07-12-wails-desktop-app.md)
api/openapi.yaml REST API contract. Source of truth for endpoints.
api/codegen.yaml oapi-codegen config → generates internal/httpapi/gen/
migrations/ Forward-only SQL. Format: NNN_name.up.sql. No down migrations.
seeds/ Bootstrap YAML. ontology.yaml, inventory.yaml, policy.yaml,
knowledge.yaml. Regenerated from DB via oikos export.
compose/ Dockerfiles. oikos/ (2-stage, Go only — SPA is built/deployed
separately), nomos/ (distroless).
Caddy config at compose/caddy/Caddyfile.oikos.
scripts/ Deploy, rollback, watchdog, verification, cutover checklist.
checks/ Host health-check scripts run over SSH by the scheduler.
tools/ Client auto-setup scripts (checks).
nomos/ Nomos config.yaml, SOUL.md, skills.
.agents/ Agent instruction files, domains, shared conventions, skills.
plans/ Design documents. active/ + done/.
docs/adr/ Architecture decision records. Numbered, prefix-sorted.
Development loop
# Start dependencies
make dev
# Generate code after API/SQL changes
make generate
# Build
make build
# Run tests
make test # all unit tests
make test-db # integration tests (needs compose Postgres)
# Lint
make lint
# CI drift guard (run before commit)
make generate-check
Adding a feature or phase
Oikos features follow a phase model (read OIKOS.md for the current phase status). To add a new capability:
- ADR first. Write an architecture decision record in
docs/adr/with the next sequence number. Document the decision, context, alternatives considered, and consequences. - Plan. If the change is non-trivial, create a plan in
plans/following the template in page-templates.md. - Schema. If the feature needs new DB tables, write a forward-only
migration in
migrations/. UseIF NOT EXISTSfor idempotency. - API. If the feature exposes endpoints, define them in
api/openapi.yamlfirst, then runmake generate, then implement. - Domain. Add types to
internal/domain/before adding logic. - Tests. Write tests alongside implementation. Integration tests go in
*_test.goin the relevant package, using the compose Postgres. - Policy. If the feature introduces new mutation types, update
seeds/policy.yamland the classifier ininternal/policy/. - Run
make generate-checkbefore commit to ensure generated code is current.
SQL conventions
- Queries live in
internal/db/queries/*.sqlwith-- name: FuncName :execannotations for sqlc - Use
pgx/v5driver. UUIDs usepgtype.UUID, timestamps usetime.Time - CTEs for graph traversals (blast radius, dependency chains)
- CAGGs and retention policies for TimescaleDB hypertables
- FTS via
tsvector+tsqueryfor knowledge search (migration 011)
OpenAPI codegen
- Config:
api/codegen.yaml. Usesoapi-codegen/v2with Chi server template - Generated output:
internal/httpapi/gen/api.gen.go— never hand-edit - Strict server interface:
api.gen.gogenerates theStrictServerInterface; implement it ininternal/httpapi/impl.go - Problem+JSON errors via
internal/httpapi/problem.go— RFC 9457 format - Cursor pagination, If-Match/ETag, idempotency keys, SSE streaming
Testing philosophy
- Race detector always on.
make testrunsgo test -race -cover ./... - Integration tests use the compose Postgres. Run with
make test-db. Each test creates + tears down its own schema namespace. - Coverage gates in CI: policy + learning ≥ 80%, others ≥ 60%
- Tests use
testing.Tdirectly, no assertion library - Table-driven tests for validation and classification logic
Migration rules
- Forward-only. No down migrations (ADR 0008)
- Idempotent: use
IF NOT EXISTS,DO $$ BEGIN ... END $$blocks - Sequence numbers are sequential integers (001, 002, ...)
- Each migration file is
NNN_name.up.sql - Migrations are embedded in the binary via
migrations/embed.go
Seed files
seeds/ontology.yaml— entity types, relationship types, lifecycles (validated against schema ininternal/ontology/)seeds/inventory.yaml— hosts, services, entities (the topology)seeds/policy.yaml— risk classes, approval rules, autonomy settingsseeds/knowledge.yaml— documents, investigations, runbooks (DB is source of truth; this file is the DR export)- After DB changes via the API, run
make exportto regenerate seeds
Secrets handling
- No secrets in code, config, or commits
- Dev secrets in
.env(gitignored) - Primary: Infisical (
internal/secrets/infisical.go) - Fallback: SOPS + age (
internal/secrets/sops.go) - Backend interface:
internal/secrets/backend.go - Machine identities via Infisical UniversalAuth
- In-memory cache with TTL for performance
Staging and deployment
- CI pipeline:
.gitea/workflows/ci.yml— lint, vet, vulncheck, test, docker build - Deploy:
scripts/deploy.sh— git pull → docker build → compose up → health check - Watchdog:
scripts/watchdog.sh— 2-minute cron, Matrix alert on failure - Rollback:
scripts/rollback.sh— checkout SHA + pg_restore - Cutover checklist:
scripts/cutover-checklist.md
Writing conventions
Apply writing-style.md for all committed prose. Terse, reference-style, no marketing vocabulary. Code comments explain intent and trade-offs, not mechanics.
Apply caveman.md for agent communication. The caveman standard applies to agent chat responses, not committed documentation.
Skills
Agent skills live under .agents/skills/<name>/SKILL.md. Each skill has a
frontmatter description that tools match against tasks. To add a skill:
- Create
.agents/skills/<name>/SKILL.md - Include frontmatter with description field
- Document the procedure following the runbook template
- Reference relevant files, commands, and policy classes
Skills that require code (e.g. linting) may include companion scripts in the same directory.
When in doubt
- Query MCP tools first (search_knowledge, get_entity)
- Read the relevant ADR in
docs/adr/ - Grep the codebase:
rg <symbol> internal/ - Check
plans/for in-progress work that may conflict - Classify any new mutation against
seeds/policy.yamlbefore suggesting it