# 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:::`): 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 ingest** — `internal/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:::: ``` 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_state` → `deprecated`/`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. ## Related files - `internal/checkdefaults/defaults.go` — `Ensure`, `Target`, `LogResult` - `internal/httpapi/default_checks.go` — `ensureDefaultChecks` (HTTP hook) - `internal/db/checks.go` — `db.EnsureEntityChecks` (shared hook) - `internal/db/seed.go` — seed-time check derivation - `internal/mcp/tools.go` — `create_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.