Files
oikos/oikos/console/deploy/README.md
dtoro 205d8a1a43 Oikos Week 4: Console v0, approval hardening, docs pass, backlog
Oikos Console v0 (oikos/console/) — read-mostly, server-rendered FastAPI
+ Jinja2 web UI, no SPA build chain. Signals landing page, service grid
+ detail, node/blast-radius view, live Mermaid relationship graph, drift
findings, approvals queue (approve/deny, destructive confirmation-phrase
enforced), daily/weekly reports. Tested end-to-end via the preview tools
against live production data, including a real click-through of the
approve/deny flow.

Found and fixed two bugs during that testing:
- Severity-dot CSS classes didn't match the actual severity strings
  (dot-warn/dot-crit vs "warning"/"critical") — warning-severity signals
  rendered with no visible indicator at all.
- The console's sys.path setup pointed at its own webhook checkout
  (/opt/oikos-console) rather than /opt/homelab-context, which would have
  made its oikos.* imports resolve to a SEPARATE copy of oikos/signal.py
  etc. than the scheduler and CLI use — silently forking signal/approval
  data into two locations in production. Fixed to match mcp/server.py's
  CONTEXT_DIR pattern. Also added _commit_push() so the console's writes
  (approval replies, signal ack/resolve) don't sit uncommitted against
  the 5-min-synced clone.

Split oikos/gen_topology_lib.py out of oikos/gen-topology.py (hyphenated
filenames aren't importable) so the console's /graph route can render
live without shelling out.

oikos/console/deploy/ — third webhook on dtoro/Homelab-Docs (port 9831),
matching the homelab-mcp/secrets-issuance precedent. README documents the
Caddy route and Gitea webhook registration this repo can't do for itself,
and that Authentik step-up on /approvals needs a live instance to
configure.

Approval hardening: grants are now single-use (oikos/approve.py
check_grant marks the request "executed" atomically, so a second call
for the same id fails even within the TTL) — verified with a test. Per-
agent age-key-signed requests, as originally planned, turned out not to
be buildable as stated: age is encryption-only, no signing primitive.
Documented the real alternative (SSH-key signing) and moved it to the
60/90-day backlog pending an inventory schema gap (no SSH pubkeys
recorded today).

Docs pass: added the Oikos command surface to operations/commands.md,
new MCP tools to AGENTS.md. Found two more stale references while at
it — commands.md and AGENTS.md both still pointed DNS at the destroyed
LXC 124/dnsmasq instead of Technitium on dns (107), and a claudio-monitor
reference deprecated since 2026-06-04 — fixed both.

60/90-day backlog written into OIKOS.md, derived from gaps actually
observed this month, not guesswork.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 00:03:51 +02:00

3.0 KiB

Oikos Console — deploy notes

Deploys the same way homelab-mcp and secrets-issuance already do: Shape B webhook (own checkout, own systemd units, own deploy secret) on LXC 105 (apps), reading HOMELAB_CONTEXT_DIR=/opt/homelab-context for all data. See infrastructure/auto-deploy.md for the general pattern; webhook ids 10 (homelab-mcp, :9811) and 11 (secrets-issuance, :9821) are the direct precedent — this is a third webhook on dtoro/Homelab-Docs, port :9831.

One-time setup on apps (105)

git clone https://git.hubris.network/dtoro/Homelab-Docs.git /opt/oikos-console
cd /opt/oikos-console
./oikos/console/deploy/deploy.sh                 # first install
./oikos/console/deploy/webhook/install.sh        # generates the deploy secret, prints it
systemctl enable --now oikos-console.service oikos-console-deploy.service

Then register the Gitea webhook (dtoro/Homelab-Docs → Settings → Webhooks) with the URL/secret install.sh printed, same as webhooks 10/11.

Caddy route — NOT in this repo, needs manual addition to dtoro/caddy-conf

The console binds 127.0.0.1:8091 only (see oikos-console.serviceProtectSystem=strict, no LAN listener). Caddy on LXC 121 needs a new site block proxying to it, forward-auth gated the same way paperless/other LAN-only services are (via the shared (authentik) snippet referenced in containers/106-auth-outpost.md). Confirm the exact snippet name/import syntax against the live dtoro/caddy-conf repo — this is the shape, not verified against it:

oikos.hubris.network {
    import (authentik)
    reverse_proxy 192.168.8.205:8091
}

Add oikos.hubris.network to the split-horizon DNS zone (Technitium, LXC 107) pointing at Caddy's LAN IP, same as every other *.hubris.network host.

Authentik step-up re-auth on approval actions — deferred, needs live Authentik

The Week-4 plan calls for the /approvals/{id}/reply POST specifically to require fresh re-authentication (not just an existing session), so a stolen session cookie can't approve a mutation. That's an Authentik policy binding (a PromptStage/reauth flow scoped to that path), which needs a live Authentik instance to configure and test — not buildable or verifiable from a repo checkout alone. Today the whole console (including this route) is protected the same way every other console-with-a-forward- auth-gate service is: the ingress-level Authentik check, not a per-action step-up. Tracked in the 60/90-day backlog (OIKOS.md).

What this deploy does NOT do

  • Does not run any homelab command directly. Approving in the console issues a grant token exactly like approving via Matrix would — actually executing the gated action still goes through the homelab CLI on whichever host runs it, with --approval-id.
  • Does not touch inventory.yaml, secrets/, or anything outside signals/ and approvals/ (both committed+pushed immediately on write, see oikos/console/app.py's _commit_push()).