- Terminal renamed Eithan: mobile font A−/A+ (per-URL ttyd opts), same-origin
xterm Copy/Paste buttons, scroll-to-live, touch-default 17px
- Dross voice: no keyboard pop after transcribe (fine-pointer only focus),
autogrow textarea to ~5 lines, live amplitude meter on the mic while recording
- Dross improvements: propose_improvement tool (CSS layer, exfil-sanitized,
owner-approved, per-improvement rollback/restore), public /improvements.css,
Settings panel. External MCP registry unchanged (no tool leak).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
These two assertions asserted the pre-2.8.0 shape; the canvas feature
added geom+extras to the repo/route defaults. push.sh doesn't run unit
tests, so they went red unnoticed until the full vitest run.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
migration 026 backup_runs; POST ingest (owner) from offsite-backup.sh, GET for the
Sacred Valley card showing last run, per-guest sizes, Farm free, schedule.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Covers GET (open, returns bundled devices set), POST without auth
(must return 401 not 500), POST with owner bearer (uploads icon,
returns set), and GET /:set/:file (serves with correct content-type).
Uses _setDirs for temp-dir isolation.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
'Scan Now' triggers POST /api/devices/scan from the band header. '+ Add by MAC'
renamed '+ Manual Add' with an optional IP field (addBody/addManual accept ip)
and a MAC input that auto-inserts colons as you type. Frontend test 4/4; DB-backed
api/repo tests written (run with the suite — skipped locally to avoid colliding
with a concurrent test run on void_test).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
'+ Add by MAC' in the band header → POST /api/devices → lan_devices.addManual
(status=known, present=false; enriched on next scan). Repo + API + frontend tests.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Summarises the Homelab Monitor (CT300 :8080) into a blackflame card: Claude Code
token usage today/week + top model, and OpenClaw/Ollama p50/p95 latency + error rate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Q3: prod void DB role NOSUPERUSER (vector marked trusted; deploy/README documents it)
- Q4: buildChildEnv allow-list for the claude subprocess (no OWNER_TOKEN/DATABASE_URL/secrets leak)
- Q5: pending-change approve claims-before-applying + reopens on failure (no re-approvable dup)
- Q6: /capture/upload validates space_id (UUID+existence); pg pool statement_timeout 30s
- Q9: disabled failing syncoid-donatello timer on Z
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Addresses final-review findings: I1 render-generation guard prevents a double-mount
/timer leak on rapid re-navigation; I2 adds anonymous-rejection tests for the owner-only
POST /speedtest/run and /health/check; M1 CSS comment; M2 cron↔worker dedup note;
M4 full 8-byte PNG signature check; M5 card-contract unit test for all 7 cards.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
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>
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>
POST /api/capture with a youtube.com / youtu.be / vimeo.com URL
enqueues ingest.video (Python worker) instead of ingest.url
(Node worker). Detection by URL hostname; idempotency_key + response
shape unchanged.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
POST /api/ingest/karakeep accepts Karakeep webhook payloads. HMAC
signature on the raw body captured by express.json's verify hook.
Mounted on app before mountApi so it bypasses agentOrOwner — the
shared secret IS the auth.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
safe_fetch.js validates URLs before fetch: rejects non-http(s), literal
or DNS-resolved loopback / RFC1918 / link-local / CGNAT / metadata
addresses; follows redirects manually with the same checks on each hop.
Test fixtures gate the check with VOID_INGEST_ALLOW_PRIVATE for offline
fixtures that hit 127.0.0.1.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Single GET /api/search?q=&space_id=&kinds=&limit=&offset= unions FTS
hits across pages / refs / source_docs / messages with a `kind`
discriminator and ts_rank ordering. Each branch's to_tsvector matches
the GIN index expression on its source table so indexes are used.
Messages have no space_id and are excluded when a space filter is set.
Hybrid vector / RRF lands in Plan 3.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Owner-only routes wired with an applyPendingChange dispatch helper
covering page/project/task/ref/resource/source_doc create/update/delete.
Approve and reject emit their own audit_log entries (actions already in
the CHECK vocab) so the audit trail is self-contained.
Documents a latent bug in security-followups.md: pending_changes.action
CHECK constraint blocks 'upsert' / 'add_dependency' / 'remove_dependency'
divertToPending paths in refs/resources routes when an agent at suggest
tier hits them.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/routes/tags.js: list + upsert at /api/tags, and an
entity-scoped router mounted at /api/:entity_type/:entity_id/tags
for attach (idempotent), list, and detach. entity_type is bounded by
a zod enum covering space/project/task/page/ref/resource/source_doc/
conversation.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add conversations CRUD-lite (list, create, get, PATCH status, PATCH
summary which flips status to summarized) and conversation-scoped
messages (append, list ordered by created_at).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/routes/agents.js: list/create/get, PATCH capabilities,
mint token (plaintext returned exactly once, then bcrypt-hashed),
revoke token. All endpoints gated by requireOwner so an agent token
can never bootstrap a new agent or grant itself capabilities.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/cap.js: requireWrite(entity_type) maps HTTP method to
action, runs canAct, and tags req.capTier as allow|suggest|deny→403.
Mutating routes (pages, projects, tasks, refs, resources, source_docs)
now check req.capTier and either run the repo (allow) or divert to
pending_changes returning 202 (suggest). Owner and worker actors stay
on the allow path. requireOwner helper added for Task 11.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/middleware/agent_auth.js: agentOrOwner accepts the owner
token (kind=user actor) or a hashed agent token (kind=agent actor
carrying capabilities + scopes). /api router now mounts this in place
of ownerOnly so agent tokens become first-class.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/routes/source_docs.js: scoped POST under a resource,
get/patch/delete by id, and POST /:id/resync as a Plan 3 stub gated
by ENABLE_RESYNC (503 by default, 202 once workers ship). upstream_url
is required by the DB so the zod schema enforces it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/routes/resources.js: CRUD scoped to space; dependency
add/list/remove (cross-space attempts mapped to 409 conflict via the
composite FK); source-docs index per resource; change history via
audit.listForEntity.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/routes/refs.js: list with space_id/kind filters and
paginated, create, get/patch/delete by id, and /upsert that maps to
repo.upsertByExternal for idempotent capture-source ingest.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/routes/pages.js: list by space, create/get/patch/delete,
get-by-slug, list revisions, and backlinks via entity_links.listTo
enriched with the source entity's title (whitelisted entity_type set
to keep the dynamic-table SELECT bounded).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/routes/tasks.js: list by space (status filter), list by
project (position then created_at), create scoped to space with
optional project_id, get/patch/delete by id. status=done flips
completed_at via the repo's existing trigger logic.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/routes/projects.js: list by space (with status filter),
create scoped to space, get/patch/delete by id. FK violation from
unknown space_id maps to 400 invalid_space.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/routes/spaces.js: list, create, get-by-id, get-by-slug,
patch, delete. Mounted under /api/spaces. Server smoke restored.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add lib/api/{errors,validate,pagination,index}.js: typed ApiError
subclasses, errorMiddleware, zod-backed validate(), parsePagination
with caps, and a mountApi() that owns /api routing + 404 + error tail.
server.js delegates /api to mountApi and drops the inline /api/spaces
smoke (returns in Task 2).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>