365 Commits

Author SHA1 Message Date
root
1eadd41990 chore: version 2.0.0-alpha.8 — Sacred Valley (Plan 6) 2026-06-02 23:02:26 +10:00
root
9c125cedba feat(health): Little Blue health band — avatar, grouped service tiles, local icons 2026-06-02 23:01:08 +10:00
root
b0d54a24cc feat(health): local icon cache /api/icons/:slug.png (no CDN leak)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:58:35 +10:00
root
60273a6204 feat(health): /api/health/services (grouped+counts) + owner /check
Adds GET /api/health/services returning registry services grouped by
category with merged cached status and per-group healthy counts, and
POST /api/health/check (owner-only) that enqueues a health.check
pg-boss job. Registers the health_check worker in the jobs index.
2026-06-02 22:56:50 +10:00
root
af0cac4e6b feat(health): probe + classify engine on a 60s cron
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:55:03 +10:00
root
5b05fd4730 feat(health): service_status cache table + repo 2026-06-02 22:53:39 +10:00
root
3ea34d9907 feat(health): service registry loader + seed config (fresh titles)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:52:46 +10:00
root
8d1950fcaa feat(card): speedtest 2026-06-02 22:51:22 +10:00
root
e36a87a50e feat(speedtest): worker + hourly cron + history/run routes
Adds speedtest pg-boss worker with injectable runner for testing, hourly
cron enqueue, and /api/speedtest/history (GET) + /run (POST, owner-only) routes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:50:19 +10:00
root
c59d2407ed feat(speedtest): results table + repo 2026-06-02 22:48:03 +10:00
root
449e849f4d feat(card): search spotlight 2026-06-02 22:46:47 +10:00
root
c78203f8e4 feat(card): inbox 2026-06-02 22:45:50 +10:00
root
bf79ab13bd feat(card): jobs / capture queue 2026-06-02 22:45:10 +10:00
root
e368ea41d8 feat(sacred-valley): drag-to-reorder with server-persisted layout
Adds HTML5 drag-to-reorder for .sv-card elements in Sacred Valley. The
pure moveId helper is unit-tested. Drop calls PUT /api/dashboard/layout
to persist the new card_order; DOM reflects the new order immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:39:57 +10:00
root
01177a2cbd feat(card): host-perf 2026-06-02 22:34:44 +10:00
root
3492b24dac feat(host): /api/host CPU/mem/disk/net from /proc
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:34:04 +10:00
root
03803f39f0 feat(card): weather 2026-06-02 22:32:22 +10:00
root
42a4b5ef33 feat(weather): /api/weather Open-Meteo proxy with 15-min cache
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:31:48 +10:00
root
eda61a19b2 feat(card): clock (Melbourne) 2026-06-02 22:30:24 +10:00
root
0a683c097d test: de-brittle /health version assertion (was pinned to stale alpha-6)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:29:57 +10:00
root
c3a3ac4feb feat(sacred-valley): card factory, registry ordering, view skeleton
Adds the Plan 6 card framework: svCard() chrome factory, pure orderCards()
ordering helper with unit tests, three stub card modules (clock/weather/host-perf),
and rewrites sacred_valley.js with the two-band layout that mounts ordered cards.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:26:09 +10:00
root
4b5faefa80 feat(sacred-valley): refined-B card chrome + theme tokens 2026-06-02 22:20:43 +10:00
root
5c6d2077c3 feat(dashboard): owner-only GET/PUT /api/dashboard/layout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:19:37 +10:00
root
c67ac27545 feat(dashboard): dashboard_layout table + repo
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-02 22:17:27 +10:00
root
629b42f502 docs: Plan 6 implementation plan (Sacred Valley widgets)
23 TDD tasks across 4 phases: grid framework + data cards, reuse cards,
speedtest, Little Blue health band. Verified against repo patterns (validate,
api.put, requireOwner, pg-boss, migrations).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:11:56 +10:00
root
34f5995810 docs: Plan 6 — cache service icons locally (no CDN slug leak)
Server-side icon cache (lib/health/icons.js + GET /api/icons/:slug.png) fetches
each icon once from dashboard-icons into a persistent dir and serves it from the
LAN. Browser never contacts the CDN; slug sanitized; first-letter fallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:04:04 +10:00
root
f2f1ee4b10 docs: Plan 6 (Sacred Valley widgets) design spec
Two-band dashboard: draggable data cards (clock/weather/host-perf/speedtest/
jobs/inbox/search) + Little Blue read-only Health band (config registry +
pg-boss health engine + grouped service tiles w/ auto-icons). Refined-B chrome,
server-side layout persistence, polling refresh. Fix-it agent deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 22:03:11 +10:00
root
28e66c6946 chore: version 2.0.0-alpha.7 — security hardening + Yerin security agent
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:24:34 +10:00
root
806e21cb13 docs: mark resolved items (auth hardening, crash-proofing, context allow-list, Yerin tools)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:17:53 +10:00
root
a3eb5a58f0 feat(security): seed Yerin agent + registry-selectable MCP server
- migration 011_yerin.sql: seed read-only 'yerin' agent ({read:true}, kind claude,
  model NULL = server default; switch to local Ollama via agents.model anytime)
- companion-stdio.js: select the toolset from VOID_TOOL_REGISTRY ('security' →
  Yerin's securityRegistry; default → Dross's companionRegistry)
- tests/mcp/registry_select.test.js

Remaining for Yerin (left for review): an entry point (route or cron) + persona
prompt — see docs/yerin-security-agent.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:17:53 +10:00
root
c45246b918 feat(security): grow Yerin's toolset (pending_review, resource_exposure, token_audit)
Three more read-only tools on securityRegistry:
- pending_review: agent-proposed changes awaiting approval (injection surface)
- resource_exposure: host/url/status attack-surface inventory (resources.listExposure,
  scalar cols only — no monitoring/metadata/credentials)
- token_audit: token label/last_used/revoked, never the hash

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:17:45 +10:00
root
aa9cf0917e fix(auth): O(1) selector+verifier token verification
verifyToken loaded every non-revoked token and bcrypt-compared each (O(n) per
request — auth-latency DoS + linear scaling). New token format
vk_<selector>.<verifier>: the non-secret selector is indexed and locates exactly
one row; only the verifier is bcrypt-hashed. Legacy NULL-selector tokens still
verify via a fallback scan. Dropped the useless idx_agent_tokens_hash.

- migration 010_token_selector.sql (adds selector col + unique partial index)
- createToken/verifyToken reworked; also adds listTokenMeta (read for Yerin's
  token_audit tool)
- tests/repos/token_selector.test.js

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:17:45 +10:00
root
10a8b813a5 fix: crash-proofing + small robustness fixes
- pool.js: add pool.on('error') handler — an idle-client error (DB restart /
  .215 failover) previously crashed the process (no 'error' listener → throw)
- context tool: project a SAFE_COLUMNS allow-list for resources (never the
  monitoring/metadata JSON blobs); also add 'resource' to TABLE (was unhandled)
- applyPendingChange: guard the 'upsert' arm so a non-upsertable entity_type
  fails with a clear ValidationError instead of a bare TypeError

Tests: pool_error, context (resource case), pending_extended_actions (guard).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 00:17:15 +10:00
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
root
6c393d8069 feat(security): Yerin security-agent toolset (read-only)
New securityRegistry (separate from companionRegistry) with two read-only,
secret-free tools for the Yerin security agent:
- audit_log: query the redacted audit trail by actor_kind/actor_id
- agent_inventory: list agents + capabilities/scopes (explicit projection,
  never SELECT *, no token material)

Follows the existing createRegistry() pattern. Design + wiring roadmap in
docs/yerin-security-agent.md. Not yet seeded/exposed over MCP (left for review).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 23:26:46 +10:00
root
459a7749c9 fix(auth): constant-time owner-token comparison
Owner bearer token was compared with === / !==, which short-circuits on the
first differing byte and leaks token length+prefix via response timing
(security-sweep-2026-06-01.md). New timingSafeStrEqual (crypto.timingSafeEqual
with a length pre-check so it never throws on length mismatch); wired into both
owner.js and agent_auth.js.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 23:26:46 +10:00
root
c591b2aed1 fix(pending): allow suggest-tier 'upsert' drafts; make dependency wiring owner-only
The pending_changes.action CHECK only permitted create/update/delete, so a
suggest-tier agent hitting POST /api/refs/upsert (or the resource dependency
routes) 500'd on the INSERT (docs/security-followups.md HIGH finding).

- migration 009: widen CHECK to include 'upsert'
- applyPendingChange: dispatch 'upsert' -> refsRepo.upsertByExternal on approve
- resources.js: add_dependency/remove_dependency are now owner-only (requireOwner),
  infra wiring is never diverted to pending_changes
- tests/api/pending_extended_actions.test.js: regression coverage

Full suite green (278 pass / 1 skip).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 23:19:44 +10:00
root
8ce97bbacc feat(companion): Dross persona (Cradle) + migration 008 rename; remove dead API-key path
- system prompt = Dross (Ozriel's construct fragment, per Void 1.0), with tool guidance
- migration 008 renames the seeded agent 'companion' → display name 'Dross'
- removed lib/ai/anthropic.js + lib/ai/agent/runtime.js + tests + @anthropic-ai/sdk dep (companion now runs via the claude CLI; kept lib/ai/secret.js for the Vaultwarden roadmap)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:58:43 +10:00
root
d35e43a8ad polish(ui): friendly tool-chip labels (strip mcp__void__ prefix)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:50:27 +10:00
root
f8beff8702 fix(companion): accept view:null in turn schema (rail sends null when no active entity)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:49:08 +10:00
root
02efff83ce test: isolate tests on void_test DB (stop resetDb wiping prod void)
resetDb() DROPs schema; dev DATABASE_URL pointed at the shared prod void DB on
.215. setup.js now forces a dedicated void_test DB (TEST_DATABASE_URL or derived)
and throws if it would target prod. Created void_test + pg_hba rule on CT 310.
Verified: full suite green, prod void space count unchanged (2→2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:45:17 +10:00
root
3988425e67 chore: gitignore python build artifacts (untrack pycache/egg-info)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:23:06 +10:00
root
16497bd9db chore: version 2.0.0-alpha.6 — companion on Claude CLI subprocess (Max subscription)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:22:53 +10:00
root
1b8dc91800 fix(companion): emit draft from user-turn tool_result + stamp space_id on created entities
- driver: tool_results arrive as type:'user' content blocks (not bare); parse them
- route: tool_result content is a JSON string; parse it for pending_change_id → draft event
- propose_change: inject ctx.space_id into create payloads (model can't know the uuid; tables require it)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:21:15 +10:00
root
1e8bbca2a5 fix(claude-cli): --resume for continuing turns (reusing --session-id errored on turn 2)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:18:13 +10:00
root
c73be6681f fix(claude-cli): feed prompt via stdin (variadic --tools/--allowedTools ate the positional)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:15:14 +10:00
root
23616d24d6 fix(companion): absolute node path for MCP spawn + restrict to mcp tools
claude resolves the MCP server command against the child env (no PATH), so a
bare 'node' failed to spawn (status:failed). Use process.execPath. Also pass
--tools to drop claude's built-ins (Bash/Read/Write/…) — companion gets only
the four mcp__void__* tools.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:11:09 +10:00
root
c4b014c15e fix(companion): pass DATABASE_URL/OLLAMA_URL to MCP stdio child explicitly
Don't rely on env inheritance/cwd-dotenv for the claude-spawned MCP server.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 22:01:26 +10:00
root
51bc5912ff feat(api): companion route drives claude CLI + MCP tools (subscription auth)
Replaces the runTurn/callModel/Anthropic-API-key path in POST /turn with
runClaudeTurn (claude CLI) backed by a per-turn MCP config that spawns
companion-stdio.js. Extracts pending_change_id from tool_result events
defensively (structuredContent → text-JSON fallback). Rewrites companion
test to inject fake-claude-draft.js via app.locals.claudeExe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 21:57:05 +10:00
root
bc1b820cc8 feat(ai): claude CLI subprocess driver (subscription auth, stream-json)
Implements runClaudeTurn() — spawns the claude CLI for a single companion
turn using subscription/OAuth auth (strips ANTHROPIC_API_KEY +
ANTHROPIC_AUTH_TOKEN from child env), streaming normalised events (delta,
tool, tool_result, result, error) via onEvent callback.

Includes hermetic test + fake-claude.js fixture that mimics real 2.1.159
stream-json output; zero network/CLI calls in the test suite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 21:51:55 +10:00