Files
oikos/CONTRIBUTING.md
dtoro d80a394b7f
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
docs: fix plan/repo drift, retire dead Goose+Nomos and Caveman tooling
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>
2026-07-12 18:19:41 +02:00

6.2 KiB

Contributing to Oikos

Developer guide for the Oikos codebase. If you are a homelab client consuming Oikos, see CLIENTS.md. If you are an AI agent working on the repo, see .agents/dev/CONTRIBUTING.md.

Dev setup

  • Go 1.26+ (see go.mod for pinned version)
  • PostgreSQL with TimescaleDB — the compose stack includes timescale/timescaledb:2.17.2-pg16
  • Docker for the full dev stack
  • Node 22+ for web/ (the control-room SPA — standalone, not part of the compose stack or the oikos binary)
# Start dependencies (Postgres + Redis). api/nomos require a shared bearer
# token — no dev-open bypass — so set one even for local dev.
OIKOS_MCP_BEARER_TOKEN=dev-token docker compose --profile dev up -d

# Run all tests
make test

# Run integration tests (needs compose Postgres)
make test-db

# Build the binary
make build

# SPA dev server (proxies to api/nomos, injecting the same token)
cd web && OIKOS_API_TOKEN=dev-token npm run dev

Project structure

cmd/oikos/          Single-binary entry point
cmd/nomos/          Nomos MCP client gateway
cmd/webhook/        Gitea deploy-webhook receiver (push-to-deploy on mac-mini)
internal/           All Go packages
  httpapi/          REST + MCP server (OpenAPI-generated)
  mcp/              MCP tool implementations
  db/               Connection pool, migrations, seeds, sqlc queries
  scheduler/        Observe loop, probes, signals
  actuator/         SSH execution
  learning/         Pattern recognition, anomaly detection
  notifier/         Matrix notifications, approval tokens
  policy/           Risk classifier
  secrets/          Infisical + SOPS backend
  domain/           Core types: entities, approvals, signals, patterns
  ontology/         Type hierarchy, relationship validation
  knowledge/        Knowledge YAML seed ingestion
web/                Control-room SPA (Svelte 5) — standalone, not embedded
                    in the oikos binary; see plans/2026-07-12-wails-desktop-app.md
api/openapi.yaml    API contract — the source of truth for endpoints
migrations/         Forward-only SQL migrations (TimescaleDB)
seeds/              Bootstrap YAML: ontology, inventory, policy, knowledge
compose/            Dockerfiles + Caddy config
scripts/            Deploy, watchdog, rollback
checks/             Host health-check scripts run over SSH by the scheduler
tools/              Client auto-setup scripts (checks)
nomos/              Nomos config, persona, skills
.agents/            Agent instruction files + skills
plans/              Design documents
docs/adr/           Architecture decision records
docs/operations/    Runbooks (rollback, etc.)

Commands

Command Purpose
make build Build oikos binary
make test Run all tests with race detection
make test-db Run integration tests against compose Postgres
make lint go vet + golangci-lint
make generate Regenerate OpenAPI + sqlc code
make generate-check CI drift guard — fail if generated code is stale
make migrate Apply DB migrations
make seed Ingest seeds into DB
make export Export DB state to YAML seeds
make dev Start compose dev stack
make clean Remove binary + test cache
make ui Build the SPA (web/dist/)
make deploy-ui Build + deploy the SPA to the Caddy host
make webhook Build cmd/webhook (deploy-webhook receiver)
make tidy go mod tidy

Conventions

APIs are OpenAPI-first

The REST API is defined in api/openapi.yaml. Server code is generated with oapi-codegen into internal/httpapi/gen/. To add an endpoint:

  1. Add the path + schema to api/openapi.yaml
  2. Run make generate
  3. Implement the handler in internal/httpapi/impl.go
  4. Add tests in internal/httpapi/api_test.go

Never hand-edit internal/httpapi/gen/api.gen.go.

Database access is sqlc-first

SQL queries live in internal/db/queries/*.sql. Go code is generated with sqlc into internal/db/sqlcgen/. Config in sqlc.yaml.

  • Queries target pgx/v5 with UUID + timestamptz overrides
  • Never hand-edit generated sqlc code

Migrations are forward-only

SQL migrations live in migrations/ as NNN_name.up.sql. There are no down migrations (see ADR 0008). Migrations are idempotent where possible (IF NOT EXISTS, DO $$ blocks).

To add a migration:

  1. Create migrations/NNN_name.up.sql with the next sequence number
  2. Write the DDL
  3. Run make migrate to apply

Seeds are DB-generated

seeds/*.yaml are the bootstrap files used by oikos seed. After making changes via the API, run make export to regenerate the seed files. These files are version-controlled and serve as DR fallback.

Writing style

Follow .agents/shared/writing-style.md. Documentation is reference prose, not marketing. Banned vocabulary includes "robust", "seamless", "leverage", "utilize", "delve", "cutting-edge".

Risk classification

Every mutation is classified against seeds/policy.yaml before execution. Four risk classes: read_only, reversible_low, config_mutation, destructive. The classifier can only lower autonomy relative to policy, never raise it. When in doubt, escalate.

CI

Gitea Actions runs on push to main and pull requests (ci.yml):

  1. go vet + golangci-lint + govulncheck
  2. Generated code drift check (make generate-check)
  3. Build (go build ./...)
  4. Test with race detector + coverage
  5. Docker build verification (no push)

Coverage gates: policy + learning packages ≥ 80%, others ≥ 60%.

PR workflow

  1. Create a branch from main
  2. Make changes, write tests
  3. Run make lint test generate-check
  4. Commit with a message following: problem → change → risk → verification
  5. Push to Gitea; CI gates PRs on green

Secrets

Secrets are managed by Infisical (primary) with SOPS as DR fallback. Never hardcode secrets. Use environment variables from .env for local dev. The .env and .infisical-credentials files are gitignored.

  • OIKOS.md — operating model, OODA loop, ontology
  • CLIENTS.md — for homelab clients consuming Oikos
  • docs/adr/ — architecture decision records