v0.25.0 — opt-in self-update for git-clone installs (security-reviewed)
Opt-in self-update for git-clone installs
Marketplace installs already self-update via Claude Code. A local git clone install didn't — it sat on old code until someone ran git pull. Now:
export CAPTAIN_MEMO_AUTO_UPDATE=1On session start, Captain fast-forwards your checkout to the newest stable vX.Y.Z tag on its own origin, runs bun install, restarts the worker, and shows a ⚓ Captain Memo auto-updated: vX → vY banner — then verifies the worker actually booted and rolls back if it didn't. Off by default (auto-pulling a developer's checkout should be a deliberate choice). No-op on a marketplace / non-git install. Throttled to one git fetch per 6h (CAPTAIN_MEMO_AUTO_UPDATE_INTERVAL_MS).
The git mechanics are a moat-safe re-lift of the battle-tested federation self-updater.
This was security-reviewed before release — two serious holes never shipped
The feature went through a 4-lens adversarial review with per-finding verification. It caught two real defects:
RCE via git argument-injection (fixed)
The current branch name (git rev-parse --abbrev-ref HEAD) flowed unsanitized into git fetch … origin <branch>. A repo whose HEAD is a branch named --upload-pack=… executed arbitrary code on an opted-in user's next session — reproduced end-to-end on git 2.47.3, and it fired before the clean-tree / ff-only gates. Fixed two ways: the branch positional is dropped from the fetch entirely, and any dash-leading ref is refused. Re-verified against a real dash-named-HEAD repo — it now bails before any git command runs.
Origin invariant not actually enforced (fixed)
Candidate tags came from git tag --list — the entire local tag namespace — so a contributor who added a fork remote could get a malicious v99.0.0 fast-forwarded in, despite the "only from your own origin" promise. Now candidates are scoped to git ls-remote --tags origin, and git fetch --tags --force guarantees a name in that set resolves to origin's exact sha.
Also hardened from the same review
- Rollback if the new code crash-loops (capture prior HEAD sha → reset + reinstall + restart old code).
- Repo-identity gate — only touch a checkout whose
package.jsonname iscaptain-memo, so a marketplace install nested in an unrelated git repo can't mis-target it. - Concurrency lock — only one session updates at a time.
bun installgets its own 300s budget instead of the 20sgit fetchcap (a half-writtennode_moduleswould crash the worker).GIT_TERMINAL_PROMPT=0+ sshBatchMode— an auth prompt can't stall session start.- Suppressed a redundant second worker restart.
20 unit tests (fake git port, no real git) cover every gate and both exploits; the git path was also dry-run and the RCE reproduction re-run against the real repo. Suite: 1119/1119.