What it is live
Vigil Steward is Teagan's locally-hosted personal-assistant operating system — a 297-route FastAPI service running on vigil-server that orchestrates morning briefings, portfolio status sync, project triage, calendar/email digestion, and cross-product agent work. Iris is the conversational persona; the product name on the repo and in code is vigil-steward, but the face the user interacts with is "Iris" (named after the Goo Goo Dolls song).
The product is the eventual destination for the operator-mode workflows currently being prototyped in the Workstation Hub repo — once Iris can do morning briefings, status checks, calendar triage, and project triage natively, the manual workflows migrate here.
Hosting
| Component | Location |
|---|---|
| Code | /home/teagan/vigil-steward on vigil-server |
| API service | FastAPI, port 8003, systemd vigil-steward-api.service |
| Celery worker | vigil-steward-worker.service |
| Celery beat | vigil-steward-beat.service |
| Database | Postgres on vigil-server (shared vigil-postgres instance) |
| Public surface | None — Tailscale-only at http://100.98.48.21:8003 |
| Production env | /home/teagan/vigil-steward/.env.production (teagan:teagan 0600), shared across all three units |
vigil-steward-{worker,beat,api}.service. The Python package is being renamed from vigil_steward to iris (PR #22 on feat/iris-rename-pr1a — 1,211 occurrences across 329 files). Until that ships, code-level imports use the old name.
Architecture lock — Ollama orchestrates, Claude Code executes
A Phase 2 architectural decision (XDEC-122, locked 2026-05-10) sets the dispatch model: Ollama does upstream reasoning for planning and routing, and Iris invokes Claude Code as a subprocess for execution rather than calling the Anthropic SDK directly. This shape leans into Teagan's Max plan instead of paying per-call API rates on agent fan-out.
FastAPI :8003] CRON[Celery beat
scheduled jobs] --> WORKER[Celery worker] API --> WORKER WORKER -->|plans + tools| OL[Ollama
local LLM
39.8 GiB VRAM] OL -->|dispatch| CLAUDE[claude subprocess
Max plan] CLAUDE -->|tool calls| TOOLS[GitHub / Mailcow / CF /
vigil-server / dev-context] WORKER --> PG[(vigil-postgres)] WORKER -->|outbound| SMTP[Mailcow SMTP
iris@sandpointstudios.ltd] SMTP -->|via SES| USR
Daily flows
Morning briefing
A Celery-beat job runs each morning and produces a "morning briefing" digest covering: portfolio rollup (active products, recent commits, open PRs, deploy state), calendar events for the day, overnight email triage, anomaly signals (failed jobs, error spikes, GlitchTip new issues). Output emails from iris@sandpointstudios.ltd to Teagan's primary inbox, with a 24-hour dedupe window so the same item doesn't echo across multiple days.
Owned by XDEC-126 Option C. PR #21 was merged 2026-05-18 (merge commit 7e44b1f); IRIS_SMTP_* env vars are staged in .env.production; the celery units still need a restart to pick up the new code.
Mood signal — YouTube Music
A separate Celery-beat job runs every 30 minutes hitting the unofficial ytmusicapi library (cookie-authenticated) to capture what Teagan is currently listening to. Result lands in the mood_signals Postgres table and gets surfaced to the briefing orchestrator as tone-aware context — so a morning briefing on the day after a heavy listening session in a particular genre can adapt tone accordingly. Low-stakes if Google breaks the unofficial API; the signal degrades, the rest keeps working.
Data
- Briefings + decisions log — Postgres. Append-only narrative of what Iris did each day.
- Mood signals — Postgres. Listening history, joinable by day.
- Outbound email log — Postgres. Every iris-sent email is recorded for trust auditing (post-Vigil-bot-hallucination, this matters).
- Schedule state — Celery's own beat schedule, persisted to disk.
idle_in_transaction_session_timeout=30min is set on vigil-postgres after 61/70 connections leaked from a Vigil Steward cron leaking BEGIN; statements. The underlying VS bug is still owed, but the guard prevents the connection-pool exhaustion mode.
Dependencies
- Ollama on vigil-server (local LLM, 39.8 GiB VRAM across 5090 + 2060S) — upstream planner / router.
- Claude Code CLI — invoked as subprocess for execution dispatch.
- Mailcow on DO — SMTP submission for Iris-authored emails (port 587 + STARTTLS).
- vigil-postgres — the shared Postgres instance on vigil-server.
- ntfy — push notifications to Teagan's phone (topic
vigil-steward-254034edb384). - GitHub API — for portfolio repo state.
- Cloudflare API — for deploy / DNS status sync.
Roadmap
- Now: celery units need a restart to pick up PR #21 (briefing task) code. Then briefings start.
- Now-ish: Python package rename
vigil_steward → iris(PR #22). Dirty/conflicting after PR #21 merge — needs rebase onto fresh develop before merge. - Phase 2: bridge to the operator-mode workflows currently in Workstation Hub. Move the help-desk triage AI here; rebuild it on the tool-grounded + reviewer-pass pattern (the Vigil bot hallucination fix design).
- Phase 2: calendar/email triage native (read inbox, propose actions, dispatch via Claude Code subprocess).
- Phase 3: migrate the architecture-KB authoring workflow (this site) into Iris's command palette.
Related
- NWZ — Iris is the eventual successor to the NWZ help-desk's auto-triage layer once the tool-grounded reviewer pattern is built here.
- Shared infrastructure — Mailcow, vigil-server, the cloudflared tunnel, vigil-postgres all underpin Iris.