diff --git a/VERSION b/VERSION index 54d1a4f..a803cc2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.13.0 +0.14.0 diff --git a/internal/httpapi/impl.go b/internal/httpapi/impl.go index 8e1257c..e03d085 100644 --- a/internal/httpapi/impl.go +++ b/internal/httpapi/impl.go @@ -24,7 +24,12 @@ import ( const ( defaultLimit = 50 maxLimit = 200 - graphNodeCap = 500 + // graphNodeCap bounds the whole-graph view. The cognition transactional + // types (execution, task) are audit records, not topology, and previously + // crowded out every host/lxc/service; the default whole-graph view below + // excludes them so the cap is spent on the actual fleet graph. Operators + // still reach executions/tasks via list_entities. + graphNodeCap = 2000 ) // actorInfo returns the caller's (type, label) from the request context, @@ -307,14 +312,19 @@ func (s *Server) GetGraph(ctx context.Context, req gen.GetGraphRequestObject) (g // alphabetically. Without this the cap fills with exec:* rows and // drops every host/lxc/service/vm — and every edge those entities // connect — because edges require both endpoints in the node set. + // Exclude the cognition transactional types (execution/task): they + // are audit records rather than topology, and at ~380 rows they + // consumed most of the old 500-node cap. nodes, err = s.queryEntities(ctx, ` SELECT `+entityCols+` FROM entities e LEFT JOIN entity_status st ON st.entity_id = e.id - WHERE e.id IN ( + WHERE e.type NOT IN ('execution','task') + AND e.id IN ( SELECT e2.id FROM entities e2 LEFT JOIN relationships r ON r.valid_to IS NULL AND (r.source_id = e2.id OR r.target_id = e2.id) + WHERE e2.type NOT IN ('execution','task') GROUP BY e2.id ORDER BY count(r.type) DESC, e2.slug LIMIT $1 diff --git a/plans/2026-07-29-health-check-reality-and-knowledge-graph.md b/plans/2026-07-29-health-check-reality-and-knowledge-graph.md new file mode 100644 index 0000000..1d4771d --- /dev/null +++ b/plans/2026-07-29-health-check-reality-and-knowledge-graph.md @@ -0,0 +1,410 @@ +# Plan: Make health reflect reality + complete the knowledge graph + +Status: ready for implementation · Created 2026-07-29 + +## Context + +`ws:mac-mini` reports health `down` despite being the healthy control-plane host. +Investigation showed the problem is systemic, not local: **49 enabled checks report +`down`**, almost all `ssh-script`, because the resource/updates probes assume +**scripts are deployed at `/opt/oikos/checks/` AND root SSH works on every target** — +both false for macOS, non-enrolled LXCs, and mesh-only entities. The knowledge graph +also has real gaps (unmodeled TLS certs, empty `skills` table, seed drift, a capped +topology view). + +The DB is the source of truth; live state was verified via the REST API +(`Authorization: Bearer $OIKOS_MCP_BEARER_TOKEN`, token in `oikos-api-1` container env) +and `docker exec oikos-postgres-1 psql`. Direct psql access is available for cleanup. + +## Decisions (confirmed with operator) + +1. **Monitoring philosophy: make checks work everywhere** — via the proven `pct exec`/ + `qm guest exec` host-routing the MCP `run` tool already uses (no per-guest SSH keys), + plus deploy the check scripts INTO each guest and make them macOS-aware. Hosts/workstations + use direct SSH with the correct per-target user. +2. **Canonical host-hop access** — `pct exec`/`qm guest exec` through the proxmox host is + the ONLY execution path for any LXC/VM command (scheduler + MCP `run` + agent). Direct + guest SSH is retired for execution; `lan_ip` stays for network probes only. (A1.) +3. **Auto-provision monitoring for new entities** — wire script-deploy + the + `health-check-answering` lifecycle gate into entity creation so any entity Nomos creates + becomes monitorable with zero manual steps (Track E). +4. **Lifecycle gate: skip monitoring for `deprecated`/`destroyed` targets** — no + permanent false alarms from retired things. +5. **Knowledge graph: address ALL gaps** — model TLS certificates, fix dns-zone gap, + re-export seeds, seed skills, raise graph cap. +6. **Read-only audit skill** — a `read_only` operator skill discovers live infra and diffs + it against the DB graph, producing a ranked drift report; the operator acts on findings + via existing lifecycle runbooks. No auto-fix. (Track F.) + +## Findings (evidence) + +### A. Health-check reality gaps (49 checks `down`) + +**Root cause is a routing mismatch, verified live (tests use the scheduler's own key +`-i /etc/oikos/ssh_key`, not a default-key test):** + +The MCP `run` tool already reaches every guest correctly via +`resolveExecTarget` (`internal/mcp/server.go:582`): resolve the proxmox host +(`attributes.host` → `hosts` edge → hubris default), SSH there, run +`pct exec -- bash -c 'echo | base64 -d | bash'` (VMs: `qm guest exec`). +That path needs **no per-guest lan_ip, no per-guest authorized_keys, no per-guest sshd**. + +The **scheduler's `checkSSHScript` does not use it** — it SSHes directly to each +entity's own resolved address (`internal/scheduler/scheduler.go:758`, +`internal/checkdefaults/defaults.go:376 resolveHost`) and runs +`/opt/oikos/checks/