Repository navigation
1.0.0
Added
-
Token login: the access token is created on first start in
~/.config/spacee-herdr/token(mode 0600) and exchanged for an HttpOnly, Secure, SameSite=Strict session cookie. -
Authenticated WebSocket
/wsthat relays bytes between the browser and the local herdr-core socket; upgrades from a foreign Origin are rejected. -
The web client is embedded in the binary and served with single-page-app fallback.
-
/healthzendpoint for local checks. -
Docker image and Compose file: runs as the host user, publishes only on host loopback (
127.0.0.1:8080), mounts the core socket directory and the token directory, restarts with Docker. -
Host setup guide (
docs/host-setup.md): tailnet settings, tailnet policy with tests that keep tagged machines (the VPS) away from the host,tailscale serve(tailnet only, no Funnel), iPhone login, verification table, token rotation, migration to a new machine, troubleshooting. -
Web Push: the bridge watches core's
events.subscribestream and sends a push (Angular service worker format, TTL 1 h) when a terminal's agent becomesblocked("Needs you", high urgency) ordone, or a terminal exits or loses its connection. Only transitions notify (never snapshots or reconnects), at most one per terminal per 10 s, and no terminal output is ever included. VAPID keys are created on first start in~/.config/spacee-herdr/vapid.jsonand subscriptions kept inpush.json(both 0600, at most 10 browsers; an unreadablepush.jsonis renamed topush.json.corrupt-<unix-seconds>and the bridge starts with no subscriptions, an unreadablevapid.jsonstops it). New authenticated endpointsGET /api/push/key,POST/DELETE /api/push/subscriptionandPOST /api/push/test; new flag-push-subject. Usesgithub.com/SherClockHolmes/webpush-gofor VAPID signing and RFC 8291 encryption. -
Image upload: authenticated
POST /api/upload(same-origin only) takes a raw PNG, JPEG, WebP or GIF (type checked from the bytes, at most 15 MiB) and stores it on the host as<YYYYMMDD-HHMMSS>-<random>.<ext>(file 0600, directory 0700), returning{"path": "<absolute host path>"}so a terminal agent can read the image. Files older than 7 days are deleted at startup and hourly. New flag-upload-dir(default$XDG_CACHE_HOME/spacee-herdr/uploads, else~/.cache/spacee-herdr/uploads); Compose mounts that directory at the same path inside the container. -
Chat transcripts: authenticated, same-origin
GET /api/chat?terminal=<address>streams the normalized, read-only transcript of the agent running in a terminal as Server-Sent Events (resetwith the last 200 messages, thenappend/update,nonewith a reason, 15 s heartbeat; the file is polled every 500 ms and only appended bytes are read). Adapters for Claude Code (~/.claude), pi (~/.pi) and Codex (~/.codex); the agent is found by walking the terminal's foreground process tree in/proc. Text parts are cut at 64 KiB and tool input/output at 8 KiB, images are never sent as base64, paths and messages are never logged, at most 8 streams (429beyond). New flags-homeand-proc. -
Chat summaries: authenticated, same-origin
GET /api/chat/summariesreturns one entry per live local terminal whose agent has a transcript (terminal,agent,session,title= first user prompt without slash commands (80 chars),last= plain-text preview of the newest message or tool call (140 chars) with its role and time,messages) for the conversations list. Only the last 256 KiB of each transcript is read (the first 64 KiB too when the tail has no user prompt), somessagescounts that window, and results are cached per file until its size or mtime changes. Shells and unsupported agents are omitted; contents and paths are never logged. -
Chat adapter for opencode: sessions are read straight from its SQLite database (
~/.local/share/opencode/opencode.db), opened read-only (mode=ro,query_only, 2 s busy timeout; never written or checkpointed, WAL rows from a running opencode are visible) through the pure-Gomodernc.org/sqlitedriver (no CGO; the binary grows about 6 MB). The session is the newest top-levelsession_v2row whosedirectoryis the terminal's cwd (same caveat as pi and Codex); messages come fromsession_messagebyseq(text, reasoning, tool calls with status/input/output, attachments as notes), polled every 500 ms for new rows and for rows updated in place (a running tool completing). Works for/api/chatand/api/chat/summaries. -
Layout management through herdr-core, with the desktop window closed: authenticated, same-origin JSON endpoints (bodies up to 8 KiB) edit the
herdr-appdocument and start or kill the terminals behind it.POST /api/layout/space(name, absolutefolder, optionalmachinefrommachine.list, optionalargv),POST /api/layout/tab,POST /api/layout/split(right/down, next topaneor the last pane),PATCH /api/layout/space/{id}and/tab/{id}(rename),POST /api/layout/moveandDELETE /api/layout/{space,tab,pane}/{id}?confirmed=1(withoutconfirmed=1they answer 400; deleting also kills the terminals; a tab keeps its last pane and a space its last tab,409).argvis limited to a shell (none) or exactlyclaude,pi,codex,opencodeoragy. New terminals carry theherdr.space/herdr.tablabels and are started before the document is written (a failed write kills them, no orphans); writes use the document version and retry up to 3 times from a fresh read on conflict. Unknown document fields are preserved. Only operation names and outcomes are logged. -
Compose:
pid: host(to see the agents' processes) and read-only mounts of~/.claude,~/.pi,~/.codexand~/.local/share/opencodeat the same paths; the security impact is explained indocs/host-setup.md.
Changed
- The bridge runs as a Docker Compose service instead of a systemd user service; update with
docker compose up -d --build.
Fixed
- PWA serving: a missing file (
/ngsw.json,/nope.js) now returns 404 instead ofindex.html; only extensionless app routes fall back to the SPA..webmanifestis served asapplication/manifest+json, and cache headers are set (no-cacheforindex.html, the service worker and the manifest; immutable for hashed build files; one day for icons and fonts) so updates reach installed apps.