diff --git a/.agents/OIKOS.md b/.agents/OIKOS.md index 50d09b1..9190b64 100644 --- a/.agents/OIKOS.md +++ b/.agents/OIKOS.md @@ -30,9 +30,9 @@ one pass through **Observe → Orient → Decide → Act**: - **queue**: informational — console + reports The classifier can only *lower* autonomy relative to policy, never raise it. When in doubt, escalate. -4. **Act** — execute through `homelab` commands or runbooks (never ad-hoc - SSH), then **verify** with the action's verification command, write a - **ledger** entry, resolve the Signal, and update docs in the same session. +4. **Act** — execute through MCP `run` or runbooks (never ad-hoc + SSH), then **verify** with the action's verification command, write a + **ledger** entry, resolve the Signal, and update docs in the same session. ## Primitives @@ -88,7 +88,7 @@ via the API's `/api/v1/graph` endpoint, and the Mermaid export at ## Conventions carried forward - Inventory is the truth; live state wins over narrative docs. -- Prefer `homelab` CLI and MCP over ad-hoc SSH. +- Prefer MCP tools over ad-hoc SSH. - Meaningful changes update docs in the same session. - Secrets are decrypted locally via per-client keys; never into docs/comments. - Tracked configs change by commit + push, not local edits. @@ -141,7 +141,7 @@ in the Go binary. - Standalone Nomos MCP client binary (`cmd/nomos`) with gateway mode (:8092). Structured queries + natural-language routing to the MCP tool list (see AGENTS.md §3). Agent activity logging on every tool call. No SSH keys. -- `nomos/` directory with config, SOUL.md, homelab-ops skill. +- `nomos/` directory with config, SOUL.md, `homelab-ops` skill at `nomos/skills/homelab-ops/`. - Nomos Docker service in `docker-compose.yml` (profile: full). - Go packages: `cmd/nomos/`, `compose/nomos/`. diff --git a/.agents/skills/docs-lint/SKILL.md b/.agents/skills/docs-lint/SKILL.md index 225d7e2..0477a25 100644 --- a/.agents/skills/docs-lint/SKILL.md +++ b/.agents/skills/docs-lint/SKILL.md @@ -20,6 +20,6 @@ Run from the repo root: Exit code is non-zero when any violation is found, so it can gate a commit. The banned-vocabulary list mirrors `writing-style.md`; update both together if the standard changes. -> **Known baseline.** `archive/knowledge/archive/knowledge/containers/101-jellyfin.md` links into a sibling repo +> **Known baseline.** `archive/knowledge/containers/101-jellyfin.md` links into a sibling repo > (`devops/homelab-authentik-admin`) that this checkout does not contain — expected, not a bug. > Any other broken link is a real regression; investigate before dismissing it as baseline noise. diff --git a/.agents/skills/lifecycle-destroy-node/SKILL.md b/.agents/skills/lifecycle-destroy-node/SKILL.md index dc3d119..b539c62 100644 --- a/.agents/skills/lifecycle-destroy-node/SKILL.md +++ b/.agents/skills/lifecycle-destroy-node/SKILL.md @@ -36,8 +36,9 @@ ledger entry. 6. Update the entity's `state` to `destroyed` in `seeds/inventory.yaml` (or move it to an `archaeology:`-style section if the schema still has one) — `pve_id`, `destroyed` date, `reason` — then `oikos seed` to - ingest. Add a row to `containers/index.md` "Recently destroyed" table - (kept for human-readable browsing alongside the structured data). +ingest. Add a row to the legacy `archive/knowledge/containers/index.md` + "Recently destroyed" table (kept for human-readable browsing + alongside the structured data in the DB). 7. No manual ledger step — mutations through the API are recorded automatically in the `audit_log` table (MCP `get_audit_trail`, `get_change_history`). The old `oikos/ledger.py append` was retired diff --git a/.agents/skills/lifecycle-provision-node/SKILL.md b/.agents/skills/lifecycle-provision-node/SKILL.md index 9b3dcba..1ee4956 100644 --- a/.agents/skills/lifecycle-provision-node/SKILL.md +++ b/.agents/skills/lifecycle-provision-node/SKILL.md @@ -27,9 +27,10 @@ chosen, doc page stub. will self-enroll as a client afterward (see [CLIENTS.md](../../../CLIENTS.md#enrollment)), the entity must exist in `planned`/`provisioning` state before `bootstrap.sh` runs there. -3. Stub the doc page (`containers/-.md` or - `vms/-.md`) — even a one-line "provisioning, see plan X" - is enough to satisfy the transition requirement. +3. Stub a document entity via MCP `upsert_knowledge` with + `kind: document` and about set to the new entity slug — even a + one-line "provisioning, see plan X" is enough to satisfy the + transition requirement. 4. Reserve the IP in DNS/DHCP notes if it's a fixed LAN address. Next: [lifecycle-activate-node.md](../lifecycle-activate-node/SKILL.md). diff --git a/.agents/skills/session-review/SKILL.md b/.agents/skills/session-review/SKILL.md index 18e11d2..28e03d5 100644 --- a/.agents/skills/session-review/SKILL.md +++ b/.agents/skills/session-review/SKILL.md @@ -78,5 +78,5 @@ Session: {id[:8]} — "{title[:60]}" - `internal/mcp/server.go` — all tool implementations (`run`, `list_lxcs`, …) - `web/src/lib/components/ToolCallGroup.svelte` — tool result display - `nomos/SOUL.md` — agent persona and tool selection rules -- `plans/2026-07-09-chat-sessions-improvements.md` — prior session findings -- `plans/2026-07-09-session-execution-and-ux-fixes.md` — latest plan +- `plans/done/2026-07-09-chat-sessions-improvements.md` — prior session findings +- `plans/done/2026-07-09-session-execution-and-ux-fixes.md` — latest plan diff --git a/AGENTS.md b/AGENTS.md index 8a47109..6ebf005 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,67 +56,79 @@ Endpoint: `https://mcp.hubris.network/mcp`. Every call needs enrollment and `/healthz` (see "Authentication" below for where the token comes from). -Available tools (the authoritative list — count them below if a number is -needed; do not hardcode the count elsewhere): +Available tools (63 total — the authoritative list; do not hardcode the count +elsewhere; regenerate from `internal/mcp/` when tools change): - Context — observe + orient: - get_entity(slug), list_entities(type, limit, cursor), - get_relations(entity), get_blast_radius(entity), - search_knowledge(query) — ILIKE search over documents, investigations, - runbooks in the knowledge_entities table - get_entity_knowledge(entity_slug) — every document, investigation, and - runbook linked to one entity, in one call - get_patterns(status, entity_type, action) — learned action patterns - get_skills(status) — available automation skills - http_get(url) — fetch a public page/raw file (e.g. researching how to - deploy something before provisioning it); HTTP/HTTPS only, ~16KB cap - - Management — live state: - get_service_status(service_slug) — systemctl is-active on target host - tail_log(service_slug, lines=200) — journalctl - list_lxcs() — all LXC containers with ID, host, IP, health - get_lxc_state(lxc_slug) — pct status from Proxmox host - ping_service(service_slug) — HTTP reachability from entity_status - list_my_secrets(caller_pubkey) — secrets accessible to this client by - age public key - - Oikos — decisions: + Entity Tools — knowledge graph, discovery, and lifecycle: + ping — lightweight connectivity check + get_entity(slug_or_id) — get an entity by slug or UUID + list_entities(type, state, q, limit) — entities filtered by type, state, or search + get_relations(entity_id, types) — list inbound/outbound edges for one entity + get_blast_radius(entity_id, depth=3) — entities affected if this one goes down + create_entity(type, name, slug, attributes, state) — create a new entity in the graph + update_entity_attributes(slug, attributes) — merge discovered facts into an entity + set_entity_state(slug, state) — transition entity to a new lifecycle state + create_relationship(source, target, type) — record a discovered edge + end_relationship(source, target, type) — soft-delete an active edge + whoami(hostname) — entity record, peers, and health for a host explain(service_slug) — compact context card (type, state, health, relations) preflight(service_slug, action) — risk class + approval requirement - whoami(hostname) — entity record, peers, health for a client get_change_history(entity_slug, limit=20) — last audit-log entries per entity - get_state_snapshot() — fleet health, disk, drift count + get_state_snapshot() — last scheduler Observe-pass: fleet health, disk, drift + audit_knowledge_graph() — read-only drift report over the graph and checks + discover_infra_drift() — running guests vs DB: missing/ghost entities + find_entities_by(key, value, limit=25) — search entities by attribute values - Operations — observe + act: - get_health_summary() — fleet health counts (healthy/degraded/down/unknown) - get_signal_history(entity_slug, state, limit) — open + recent signals - get_audit_trail(entity_id) — audit log filter + browse - get_agent_activity(limit) — agent self-inspection - query_metrics(hours=24) — time-series metric bucketed averages - get_trend(entity_id, days=7) — metric slope over time - get_event_timeline(severity, entity_slug, limit) — recent events + Ops Tools — live state, signals, checks, and execution: + run(target, command, purpose, declared_risk) — general execution primitive; read-only auto-acts, mutations queue for approval, destructive always needs explicit confirmation + inspect_path(path, targets) — bulk mount/df/ls/stat across multiple hosts/LXCs + get_execution_status(execution_id) — poll execution progress + tail_log(service_slug, lines=50) — journalctl for a service + get_service_status(service_slug) — systemctl is-active/is-enabled + get_lxc_state(lxc_slug) — pct status from Proxmox host + ping_service(service_slug) — HTTP reachability + scheduler health state + list_lxcs(state) — all LXC containers with ID, host, IP, last-audited hint + ack_signal(signal_id) — acknowledge an open signal + resolve_signal(signal_id, resolution) — resolve a signal with optional note + mute_signal(signal_id, duration_s=3600) — temporarily mute a signal + cancel_execution(execution_id, reason) — cancel a queued/running execution + update_check(check_id, enabled) — enable or disable a health check + list_checks(entity_slug, enabled) — list health checks with verdict, probe kind + list_executions(entity_slug, status, limit=25) — cursor-paginated execution history + list_entity_sessions(entity_slug) — active Nomos sessions linked to an entity + get_dashboard_summary() — fleet overview: counts, health, signals, approvals + get_secret(key, path, environment) — retrieve a secret from the Infisical vault + list_secrets(path_prefix) — list secret keys in the Infisical vault + set_secret(key, value, path, environment) — store/update a secret (requires approval) - Knowledge — keep the graph current (none require approval; this updates - the knowledge graph, not live infrastructure): - upsert_knowledge(title, content) — record what you learned after solving - a non-obvious problem; the only way anything persists past a session - update_entity_attributes(slug, attributes) — merge a discovered fact - (IP, version, port, ...) into an entity so a future task doesn't - rediscover it from scratch - create_relationship(source, target, type) — record a discovered edge - (depends-on, hosts, routes-to, ...) between two entities + Knowledge Tools — search, read, and maintain the knowledge base: + search_knowledge(query) — full-text search across docs (snippets, not full body) + get_entity_knowledge(entity_slug) — all docs/investigations/runbooks linked to a slug + get_knowledge_content(slug) — full markdown body of one knowledge entry + upsert_knowledge(title, content, about, tags, kind) — write what you learned + delete_knowledge(knowledge_slug) — soft-delete a knowledge entry + restore_knowledge(knowledge_slug) — restore a soft-deleted entry + merge_knowledge(target_slug, source_slugs) — fold entries into a target + rename_knowledge_tag(from, to) — bulk-rename tags across all entries + get_knowledge_revisions(knowledge_slug) — version history for a knowledge entry + get_knowledge_duplicates(threshold=0.6) — near-duplicate detection via trigram similarity + get_knowledge_orphans(stale_days=90) — unlinked, untagged, or stale entries + list_knowledge_tags() — all tags with usage counts and casing variants + list_my_secrets(caller_pubkey) — secrets accessible to a client by age public key - Execution — mutating the live infrastructure: - run(target, command) — the general execution primitive. Run any shell - command against a host or LXC; every command is auto-classified — - read-only inspection runs immediately, anything state-changing needs - operator approval, and destructive patterns (rm -rf, dd, mkfs, - pct/qm destroy, DROP TABLE, reboot, curl-pipe-to-shell, ...) always - need approval regardless of what you declare. This is the ONLY - mutation tool — `request_execution` was retired 2026-07-14; the - former enum actions (restart, systemctl, pct_exec, apt_upgrade, - pct_create) are all expressed as `run(target, command)` now. - get_execution_status(execution_id) — poll progress + Analysis Tools — fleet health, metrics, and introspection: + get_health_summary(health) — fleet health per entity, optionally filtered + get_audit_trail(entity_id) — query the audit log + query_metrics(hours=24) — time-series with bucketed avg/min/max + get_signal_history(entity_slug, state, limit=50) — open and recent signals + get_patterns(status, entity_type, action) — learned action patterns + get_skills(status) — available automation skills + get_trend(entity_id, days=7) — metric slope, variance, and averages + get_event_timeline(severity, entity_slug, limit=50) — recent events + get_agent_activity(limit=50) — agent self-inspection log + classify_command(command, declared_risk) — pre-flight risk classification before `run` + get_ontology() — entity types, relationship types, and lifecycle definitions + http_get(url) — fetch a public web page/raw file; ~16KB cap **When to prefer MCP over grepping the clone:** always for knowledge queries. `search_knowledge("jellyfin hardware acceleration")` returns ranked results from diff --git a/VERSION b/VERSION index c25c8e5..1a44cad 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.30.0 +0.30.1