Files
Void-Homelab/docs/yerin-security-agent.md
root afbf075d26 docs: security sweep, code review, Yerin design, Plan 6 brainstorm brief
- security-sweep-2026-06-01.md: fresh sweep of alpha.6 (1 fixed, findings + carry-overs)
- code-review-2026-06-01.md: optimisation/cleanliness notes (pool error handler,
  O(n) bcrypt token scan, FTS index alignment, dup auth parsing)
- yerin-security-agent.md: security-agent design + tool roadmap + Orthos role proposal
- plan-6-brainstorm-brief.md: Sacred Valley widget inventory + open design questions
- security-followups.md: marked the pending_changes CHECK finding RESOLVED

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 23:26:46 +10:00

5.4 KiB

Yerin — the Void 2.0 Security Agent

Cradle note: Yerin is the sword artist — fast, vigilant, the one who notices the threat first and calls it. In Void 1.x she already owns the 5-minute alert cron. In Void 2.0 she becomes a first-class read-only security/observability agent: she watches, reports, and proposes — she never silently acts.

Design principles

  1. Read-only by capability. Yerin's agent record gets { read: true } and no write/suggest. Anything she wants changed she raises to you; she cannot mutate state. (If you later want her to be able to propose a remediation, add suggest: true and the existing pending-change flow gives you an approval gate for free.)
  2. Own toolset, own registry. Security tools live in lib/ai/agent/tools/security/ behind securityRegistry — separate from Dross's companionRegistry, so she gets investigative tools, not propose_change.
  3. No secret material, ever. Tools project explicit columns and rely on the audit layer's write-time redaction. agent_inventory deliberately never does SELECT *.

Built this pass (TDD, on main, not deployed)

lib/ai/agent/tools/security/ + securityRegistry, tests in tests/ai/security_tools.test.js:

Tool What it answers
audit_log "Who did what, newest first?" Filter by actor_kind / actor_id. Reads the redacted audit trail. Capped at 200.
agent_inventory "Which agents exist and what is each allowed to do?" Returns id/slug/name/kind/model + capabilities + scopes. Never returns token material.

Roadmap — tools to add next (designed, not yet built)

Each is read-only and maps to an existing repo, so each is a small TDD task:

  • pending_review — list pending_changes awaiting approval (the queue of agent-proposed mutations). Wraps pendingChanges.listPending. Security-relevant because it's exactly where a prompt-injected agent's intent surfaces.
  • resource_exposure — inventory of resources with url/host/status (attack surface / what's reachable). Wraps resources.listBySpace across spaces.
  • token_audit — agent tokens with label, last_used, revoked_at (NOT the hash) to spot stale/unused credentials. Needs a small repo read.
  • recent_captures — newly ingested refs/source_docs (untrusted external content entering the system), so Yerin can flag suspicious inbound material.

Stretch (needs new plumbing, your call):

  • tls_expiry / service_health — port Void 1.x's lib/security.js cert checks + the Yerin alert cron into a tool she can call on demand.
  • Active probes (e.g. "is this CT's admin port exposed?") would require network egress — must go through safeFetch and be owner-gated. Defer.

Wiring Yerin up (the remaining integration steps — left for you)

These touch live agent seeding / MCP config, so they're documented rather than done unsupervised:

  1. Seed the agent. A migration (010) inserting a yerin agent row with capabilities = {"read": true}, kind: 'claude' (or 'ollama' if you want her cheap/local), and a persona prompt. Mirror migration 007's companion seed.
  2. Expose securityRegistry over MCP. lib/mcp/companion-stdio.js currently hardcodes companionRegistry. Parameterise it (env VOID_TOOL_REGISTRY=security selects the registry) so a Yerin session spawns with her tools. ~10 lines.
  3. A Yerin entry point. Either a route (POST /api/security/ask) reusing the claude_cli driver with Yerin's persona + securityRegistry, or a scheduled cron that runs a standing "anything suspicious in the last 24h?" pass and files the result as a Sacred Valley card (ties into Plan 6).

Adjacent agent roles (so the roster stays coherent)

  • Dross — companion/chat (Lindon's own spirit-AI; the right-rail assistant). Read + suggest. Already live.
  • Yerin — security/vigilance (this doc). Read-only.
  • Orthos — see the role proposal below.

Proposed role: Orthos — the local-model Advisor / Council

Orthos in Cradle is the ancient dragon-turtle: a powerful ally with long experience and a deep spiritual (Path of Black Flame) connection — exactly your framing. That maps cleanly onto a specific, useful Void 2.0 role:

Orthos = the reflective Advisor, running on the local Ollama model (free, always-on), not Claude. He plays to a local model's strengths (summarisation & synthesis, not precise tool-use):

  • The long view / "council briefing." A scheduled pass that fuses recent captures, open tasks, and audit highlights into a short reflective digest — the Void 1.x "Orthos council briefing" reborn as a Sacred Valley widget.
  • "Spiritual connection" = the knowledge graph. Let Orthos traverse the embedding/RRF links to surface non-obvious connections across Spaces ("these three captures in two different Spaces are converging on X"). That's the literal mechanisation of his deep-sight: semantic connection-finding over the vector store.
  • Why local: it's high-frequency, low-stakes, cost-sensitive reflection — perfect for llama3.1:8b on 192.168.1.185, keeping Claude budget for Dross. Capability: { read: true }, kind: 'ollama', model: 'llama3.1:8b'.

So the trio reads naturally: Dross converses, Yerin guards, Orthos reflects — and two of the three (Yerin alerts, Orthos council) become Plan 6 Sacred Valley widgets. See docs/plan-6-brainstorm-brief.md.