v0.21.0
🚀 Features
-
(transport) Verify agent binary checksum before caching - (c332ddb) by @bchatard
The downloaded agent runs on the target (often via sudo) but was cached and
executed with no integrity check. verifyChecksum now fetches the release
checksums.txt and verifies the binary's SHA-256 before the cache write, failing
on a mismatch or a missing entry for the platform asset. --agent-binary still
bypasses both download and verification.Part of M19 (agent binary integrity verification). ADR-0011 updated.
Co-Authored-By:Claude Fable 5 noreply@anthropic.com
🐛 Bug Fixes
-
(deploy) Emit hook output as JSON events in --output json mode - (a3675e2) by @bchatard
In JSON mode the deployer wrote raw hook stdout/stderr to the same stream as the
JSON events, so over SSH the client's NDJSON parser silently dropped all hook
output (migrations, build logs) and a local --output json deploy produced invalid
NDJSON. A new tui.HookOutputWriter wraps each write as a hook_output event;
ForwardStream renders it; the deployer routes hook output through it in JSON mode
and writes raw to stdout as before in human/plain mode. A WithRunner seam lets a
fake runner drive the deployer test without real shell execution.Part of M19 (hook output over SSH / JSON mode).
Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(deploy) Classify hook and deploy runtime failures as exit 3 - (11755f3) by @bchatard
Hook, template, extraction, link, symlink, and purge failures returned bare
errors that forge resolved to exit 1 (Usage), but specs 01/03/05 all document
exit 3 (Runtime) for these — a contract CI/orchestration relies on. The hooks
runner now wraps hook-exec and template failures as Runtime, the deployer wraps
its extract/link/symlink/purge/release-dir failures, and emitError reports the
resolved code in the JSON error event instead of a hardcoded 1. cmderr re-exports
Wrap/Resolve. The specs already matched; only the code changed.Part of M19 (exit-code classification).
Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(transport) Download agent as a raw binary, not a tar.gz - (ec289ad) by @bchatard
goreleaser ships raw binaries (formats: [binary], asset
bifrost_{version}{os}{arch}), but the downloader requested a .tar.gz and
re-prefixed an already-v-prefixed version, so every agent auto-download 404'd
and only --agent-binary worked. downloadURL now targets the raw asset and
strips a leading v; downloadAgent caches the bytes directly (no gzip/tar).Part of M19 (agent binary download). ADR-0011 and spec 07 updated to match.
Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(transport) Shell-quote interpolated values in remote commands - (689e901) by @bchatard
Agent invocation strings interpolated env, app, release name, and the artifact
basename straight into a remote shell via session.Run, unquoted — they broke on
spaces and were injectable from semi-trusted filenames. A new ShellQuote helper
single-quote-escapes each value; it is now applied at every interpolation site in
client_deploy, client_activate, client_list, client_rollback, and staging's rm -rf
and chmod calls. An integration test proves a space/semicolon payload round-trips
literally over a real SSH shell without injecting.Part of M19 (quote remote command arguments).
Co-Authored-By:Claude Fable 5 noreply@anthropic.com
📚 Documentation
-
(tasks) Add M19 hardening and M20 cleanup milestones - (bdd04a4) by @bchatard
Record the findings from the full code/doc review as roadmap tasks. M19
covers the critical agent-download fix, command-injection hardening, agent
integrity verification, CI integration tests, the hook-output protocol, and
exit-code classification. M20 collects the minor cleanups (S1-S10).Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(tasks) Record the container-readiness fix under M19 - (03b248c) by @bchatard
The integration-test CI job (M19) exposed and led to fixing the SSH container
readiness race (commit e837a96). Record it on the roadmap rather than only in
session memory, per the project's roadmap discipline.Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
(testing) Require container readiness to gate on a setup marker - (639c94d) by @bchatard
Record the forward-facing rule from the M19 container-readiness fix: SSH
integration containers must signal readiness via a setup-complete marker, not
wait.ForListeningPort (which the Docker port proxy can report open before the
startup script finishes). Complements the inline comment in ssh_container.go.Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
Reconcile specs and rules with shipped behaviour - (1ed87d6) by @bchatard
Fixes from the full review: spec 05 no longer claims interactive hooks are
unsupported in v0 (they prompt/skip); specs/README indexes specs 07 and 08; the
--config default in spec 03 and coding.md now states the real resolution chain;
spec 07's release list/activate/rollback agent invocations match the actual client
commands (--env/--app passed, activate uses --no-confirm); versions.md covers
M11-M19; and the .goreleaser.yml ADR reference is qualified as a forge ADR.Part of M19 (documentation reconciliation). Moving the working-doc artifacts out
of docs/ is deferred pending confirmation.Co-Authored-By:Claude Fable 5 noreply@anthropic.com
-
Move M18 working-doc artifacts to .claude/plans - (08d7b6e) by @bchatard
The dated hook-lifecycle plan and design doc were working artifacts cluttering
docs/ (the plan ~2,000 lines, the design doc unindexed in specs/README). Move
them to .claude/plans/ as m18-hook-lifecycle.md and m18-hook-lifecycle-design.md,
matching the existing m0/m2 plan artifacts and the descriptive-naming convention.
The durable links to the design notes (M18 roadmap entry, ADR-0012) are repointed;
docs/ now holds only specs, ADRs, tasks, and the sample config.Completes M19 (documentation reconciliation).
Co-Authored-By:Claude Fable 5 noreply@anthropic.com
🧪 Testing
-
(testutil) Wait for container setup to finish, not just the port - (e837a96) by @bchatard
wait.ForListeningPort('22/tcp') can return before the container's setup script
finishes: Docker's port proxy on Linux binds the mapped port early, so tests ran
container-side setup (e.g. 'chown deploy:deploy') before 'useradd deploy' had run,
failing with exit 1. macOS Docker networking hid this; the M19 CI integration job
exposed it on GitHub. The setup script now touches /tmp/.setup-complete after all
setup (just before exec sshd), and readiness waits on that marker instead — so the
deploy user and keys are guaranteed present before any test runs.Co-Authored-By:Claude Fable 5 noreply@anthropic.com
⚙️ Miscellaneous Tasks
-
Run the integration test suite in CI - (98d8254) by @bchatard
The reusable forge go-ci workflow runs go test ./... without -tags integration,
and the build job only compiled, so the entire testcontainers suite (real deploy,
SSH transport, multi-server) never ran in CI — which is how the broken agent
download shipped. A new integration job runs go test -tags integration ./... on a
Docker-enabled ubuntu runner. Verified locally: 238 tests pass.Part of M19 (run integration tests in CI).
Co-Authored-By:Claude Fable 5 noreply@anthropic.com
Commit Statistics
- 12 commit(s) contributed to the release.
- 1 day(s) passed between the first and last commit.
- 12 commit(s) parsed as conventional.
- 0 linked issue(s) detected in commits.
- 1 day(s) passed between releases.