Files
oikos/docs/infrastructure/oikos-check-lifecycle.md
dtoro f127925d5a archive: prune stale dirs, move actual content to docs/
Deleted stale (Python-era artifacts, superseded by DB):
- archive/oikos-cards/ (46 files, predecessor to DB entity graph)
- archive/ledger/ (5-line JSONL fragment, superseded by DB audit log)

Moved to docs/ (actual, current architecture docs):
- docs/infrastructure/ — 12 infrastructure docs (network, DNS, mesh,
  SSH, ingress, media-permissions, backups, homelab-context, auto-deploy,
  VPS-hardening, monitoring, check-lifecycle)
- docs/secrets/ — secrets README and rotation runbook
- docs/GLOSSARY.md — 28-term homelab glossary

Added STALE.md markers to hermes-plans/ and secrets-issuance/.
Added MOVED.md pointers in archive sources.
Updated docs/index.md to include new paths.
2026-08-16 11:26:27 +02:00

4.8 KiB

Oikos check lifecycle — how monitoring works

This runbook covers how Oikos health checks are derived, created, and wired so an agent (Nomos) doesn't reverse-engineer source when asked to add monitoring to an entity — the problem that stranded session 23da10db (2026-08-03).

Concepts

  • check_defs (scheduler config, table check_defs): the row the scheduler reads to know what to probe and when. One per check instance.
  • check entity (type check, slug check:<kind>:<target>:<n>): the knowledge-graph entity for that check. It carries attributes (check_type, target, port, …) and checks edges to the probed target.
  • monitoring spec on an entity type (entity_types.monitoring_spec): the default list of check kinds (e.g. [http, process] for service).
    • Per-entity override: set monitoring in the entity's attributes — "none" for zero checks, ["http"] to replace the type defaults.
  • checkdefaults.Ensure (internal/checkdefaults/defaults.go): the function that reads the monitoring spec, resolves host/port/URL from attributes + relationships, and writes check_defs rows. Idempotent.

When checks are derived

checkdefaults.Ensure runs in three situations (as of v0.17.1+):

  1. Seed/deploy ingestinternal/db/seed.go:231. Every entity gets its default checks once on initial ingest.
  2. HTTP POST /api/v1/entities (create)ensureDefaultChecks at internal/httpapi/impl.go:1012. Creating an entity via the REST API derives its checks in the same transaction.
  3. HTTP PATCH /api/v1/entities (patch)ensureDefaultChecks at internal/httpapi/impl.go:1280. Changing an entity's attributes (especially monitoring) via the REST API regenerates its checks.
  4. MCP create_entity — SAME hook. Creating an entity via the MCP tool derives checks. (Added 2026-08-03; previously MCP had no create.)
  5. MCP update_entity_attributes — SAME hook. Changing an entity's monitoring attribute via MCP now regenerates checks. (Added 2026-08-03; previously MCP updates silently skipped check derivation — the exact bug that stranded the haos session.)

Check slug grammar

check:<kind>:<target-type>:<target-name>:<n>

Examples: check:http:service:jellyfin:0, check:vm-status:vm:haos:0, check:cert-expiry:cert:house.hubris.network:0.

Adding monitoring to an entity

If the entity already exists:

update_entity_attributes(slug="service:haos", attributes={"monitoring":["http"]})

This regenerates checks via checkdefaults.Ensure. The result message tells you how many checks were derived and whether any kinds were skipped (and why).

If the entity does not exist yet (a new check, ingress, cert, etc.):

create_entity(type="check", name="HAOS http check",
  slug="check:http:service:haos:0",
  attributes={"check_type":"http:service","target":"service:haos","port":"8123"})

This creates the entity AND derives its check_defs. Same for a new ingress (type=ingress, monitoring [http]) or cert (type=cert, monitoring [cert-expiry]).

To remove monitoring: set monitoring:["none"] or transition the entity to a terminal lifecycle state (set_entity_statedeprecated/destroyed).

Caveats

  • A service without a url attribute AND without a probe_unit gets no process check (the http check covers liveness; the process check would be redundant without an opt-in probe_unit). The skip is logged.
  • A service whose address comes from a hosts edge may produce no checks on initial create because the edge doesn't exist yet — the next inventory ingest (or a later update_entity_attributes after the edge is created) fills it in.
  • A not found error from update_entity_attributes means the entity doesn't exist — use create_entity instead.
  • check_defs has target columns (target_id, target_type). A check entity needs a checks relationship (create_relationship(source=check:…, target=service:…, type="checks")) so the scheduler can resolve what to probe. create_entity derives the check_def; create_relationship links the check entity to its target in the graph.
  • internal/checkdefaults/defaults.goEnsure, Target, LogResult
  • internal/httpapi/default_checks.goensureDefaultChecks (HTTP hook)
  • internal/db/checks.godb.EnsureEntityChecks (shared hook)
  • internal/db/seed.go — seed-time check derivation
  • internal/mcp/tools.gocreate_entity, update_entity_attributes

Revision history

  • 2026-08-03: Created after session 23da10db stranded for lack of entity- creation tool and unawareness of check-derivation triggers. Covers the MCP create_entity + update_entity_attributes regen paths added same day.