Skip to content

Releases

v1.3.2

Choose a tag to compare

@spacecodee spacecodee released this 04 Nov 14:50

Changed

  • The embedded web client shows changed files as a collapsible, keyboard- and screen-reader-accessible folder tree on the Changes screen (see the frontend changelog).

v1.3.1

Choose a tag to compare

@spacecodee spacecodee released this 04 Nov 00:06

Changed

  • The embedded web client shows agent messages, pasted text and background-task notices as readable chat rows instead of raw harness tags (see the frontend changelog).

v1.3.0

Choose a tag to compare

@spacecodee spacecodee released this 03 Nov 23:53

Added

  • scripts/deploy.sh: builds the image from the checkout with the git version and restarts it, then checks /healthz.

Fixed

  • Images built locally with docker compose report their version instead of dev (BRIDGE_BUILD_VERSION).
  • scripts/build.sh tags the image ghcr.io/spacecodee/spacee-herdr-bridge, and release.sh builds with the release version instead of a -dirty git describe.
  • The chat shows only the live branch after a rewind: Claude Code (/rewind), pi (/tree), Codex (backtrack) and opencode (/undo, /redo) no longer render abandoned messages, in the conversation view and in the home summaries.
  • Messages pulled back from the Claude Code queue with Up no longer stay in the chat as queued.
  • After a Codex rewind the chat keeps the earlier turns: the new rollout file continues the old one (history_base), and the bridge reads both.

1.2.1

Choose a tag to compare

@spacecodee spacecodee released this 03 Nov 21:31

Added

  • Prebuilt multi-arch image (linux/amd64, linux/arm64) on ghcr.io/spacecodee/spacee-herdr-bridge, with OCI labels; docker compose pull && docker compose up -d runs it, BRIDGE_VERSION pins a release. release.sh --push-image builds from the release tag in a temporary worktree and always logs out of ghcr.io afterwards.
  • MIT LICENSE, SECURITY.md (threat model, how to report a vulnerability), CONTRIBUTING.md, a README rewritten as the public landing page, and a host setup guide with placeholders instead of machine-specific values. scripts/herdr-core-restart messages are now in English.
  • Local release scripts (no CI): scripts/build.sh builds the frontend, embeds it, runs the checks, and produces linux/amd64 and linux/arm64 binaries with SHA256SUMS in bin/release/ plus a local Docker image; scripts/release.sh <version> updates both changelogs, tags both repos and never pushes; scripts/release.sh --push-image <version> pushes a multi-arch image to ghcr.io. The bridge has a -version flag.

Changed

  • The Dockerfile cross-compiles with Go instead of emulating the target platform; compose.yaml names the GHCR image and still builds locally with --build.

The web app changes for this version are in spacee-herdr-frontend v1.2.1; its build is embedded in this binary.

1.2.0

Choose a tag to compare

@spacecodee spacecodee released this 03 Nov 21:15

Added

  • GET /api/chat/summaries items gain an optional asking (count, first question) while the newest agent question is still pending; absent once answered. Never carries options, answers or secrets.
  • The chat stream marks agent questions for Claude Code (AskUserQuestion), pi (the ask_user_question and ask_user_choice tools) and Codex (request_user_input, Plan mode) and opencode (question): the tool part carries questions (question, header, multiSelect, other when a typed answer is allowed, secret for Codex secret questions whose answer is masked, options (empty for a free-text question) with label and description; at most 4 questions and 6 options, text clipped) and stays running while pending; once answered it becomes ok and carries answers (question text to chosen answer, multiple choices joined with ", "). A reconnect reset shows unanswered questions as pending.

The web app changes for this version are in spacee-herdr-frontend v1.2.0; its build is embedded in this binary.

1.1.0

Choose a tag to compare

@spacecodee spacecodee released this 03 Nov 21:15

Added

  • Per-device sessions: each login creates a device (id, name, platform from the User-Agent) kept in devices.json beside the token (mode 0600, at most 20; signing in on one more signs out the least recently seen). GET /api/devices lists them (current marks the caller), PATCH /api/devices/{id} renames, DELETE /api/devices/{id} and DELETE /api/session sign a device out: its push subscriptions are removed and its open /ws and /api/chat streams close within a second.
  • Pairing codes: a signed-in device gets a 6-character code with POST /api/devices/pair (valid 5 minutes, single use, at most 5 live) and a new device logs in with {"code": "..."} on POST /api/session. Ten wrong codes in ten minutes invalidate all codes and block code logins for ten minutes (token login keeps working).
  • Push subscriptions record the device that saved them (deviceId in push.json; old entries stay valid).
  • An agent is recognized before its first message: /api/chat sends an empty reset with the agent name (instead of none/not-found) and /api/chat/summaries lists the terminal with messages: 0, so the app shows its icon and opens the chat right away (Claude Code, pi, Codex, opencode).
  • Chat transcripts show Claude Code messages typed while the agent was working: a message waiting in Claude's queue is sent with queued: true and removed through a new remove SSE event ({"ids": [...]}) once Claude takes it; a queued message delivered in the middle of a turn is now kept as a user message (it used to be missing from the chat). Background task notifications in the queue are not shown.

Changed

  • Host setup guide: a systemd user unit for herdr-core (scripts/herdr-core.service) and a clean restart script (scripts/herdr-core-restart), signing out a lost device instead of rotating the token, and what lives in ~/.config/spacee-herdr.
  • The session cookie no longer holds the access token: it is <device id>.<device secret> and only the secret's hash is stored. Cookies from before (equal to the token) are upgraded to a device automatically on first use, so signed-in phones stay signed in.

Fixed

  • Chat transcripts no longer show the raw markup of Claude Code slash commands: /clear or /model opus appear as typed, and a command's output (for example "Set model to …") appears as a system message (empty output is dropped).

The web app changes for this version are in spacee-herdr-frontend v1.1.0; its build is embedded in this binary.

1.0.0

Choose a tag to compare

@spacecodee spacecodee released this 03 Nov 03:50

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 /ws that 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.

  • /healthz endpoint 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.subscribe stream and sends a push (Angular service worker format, TTL 1 h) when a terminal's agent becomes blocked ("Needs you", high urgency) or done, 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.json and subscriptions kept in push.json (both 0600, at most 10 browsers; an unreadable push.json is renamed to push.json.corrupt-<unix-seconds> and the bridge starts with no subscriptions, an unreadable vapid.json stops it). New authenticated endpoints GET /api/push/key, POST/DELETE /api/push/subscription and POST /api/push/test; new flag -push-subject. Uses github.com/SherClockHolmes/webpush-go for 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 (reset with the last 200 messages, then append / update, none with 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 (429 beyond). New flags -home and -proc.

  • Chat summaries: authenticated, same-origin GET /api/chat/summaries returns 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), so messages counts 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-Go modernc.org/sqlite driver (no CGO; the binary grows about 6 MB). The session is the newest top-level session_v2 row whose directory is the terminal's cwd (same caveat as pi and Codex); messages come from session_message by seq (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/chat and /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-app document and start or kill the terminals behind it. POST /api/layout/space (name, absolute folder, optional machine from machine.list, optional argv), POST /api/layout/tab, POST /api/layout/split (right/down, next to pane or the last pane), PATCH /api/layout/space/{id} and /tab/{id} (rename), POST /api/layout/move and DELETE /api/layout/{space,tab,pane}/{id}?confirmed=1 (without confirmed=1 they answer 400; deleting also kills the terminals; a tab keeps its last pane and a space its last tab, 409). argv is limited to a shell (none) or exactly claude, pi, codex, opencode or agy. New terminals carry the herdr.space / herdr.tab labels 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, ~/.codex and ~/.local/share/opencode at the same paths; the security impact is explained in docs/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 of index.html; only extensionless app routes fall back to the SPA. .webmanifest is served as application/manifest+json, and cache headers are set (no-cache for index.html, the service worker and the manifest; immutable for hashed build files; one day for icons and fonts) so updates reach installed apps.