Skip to content

Releases

The unified steelframe binary ships from a versioned Cloudflare R2 channel. The same binary is the operator TUI (default), the project agent (steelframe agent run), and the updater (steelframe update).

install.sh # bootstrap
latest.json # { version, released_at, target, url, sha256 }
vX.Y.Z/
steelframe-linux-x64 # the agent/TUI binary
steelframe-gui-linux-x64 # native GUI
gui/index.html
gui/dist/<hash>/steelframe_gui.js
gui/dist/<hash>/steelframe_gui_bg.wasm
manifest.json
SHA256SUMS
latest gui/… (mirrored at the root for Web)

latest.json is the only mutating key; version directories are immutable. The web GUI pair is content-addressed: gui/index.html imports ./dist/<hash>/steelframe_gui.js and the glue loads its wasm relative to itself, so JS and wasm always come from one build. Only gui/index.html revalidates; the hashed pair is immutable. install.sh downloads the binary named in latest.json, verifies its SHA-256, and installs to $STEELFRAME_INSTALL_DIR or the first writable of /usr/local/bin, ~/.local/bin. The release job fails closed if the GUI wasm artifacts are missing.

The workspace version and the git tag must match:

Terminal window
# bump [workspace.package] version in Cargo.toml
git add Cargo.toml Cargo.lock
git commit -m "chore: release 0.2.0"
git tag v0.2.0
git push origin main
git push origin v0.2.0

The release-r2 workflow builds the agent, native GUI, and GUI wasm on separate runners, then uploads them. To re-publish the current Cargo.toml version without a new tag, run Actions → release-r2 → Run workflow. To publish locally with the same R2 secrets in the environment:

Terminal window
make release

Setup and secrets (R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET, R2_ENDPOINT, R2_PUBLIC_BASE) are in docs/releasing.md.

Terminal window
curl -fsSL https://pub-320c65d12c95410aa947b2b539e51834.r2.dev/install.sh | sh
steelframe version

steelframe update fetches latest.json, compares semver, downloads and verifies the newer binary, and atomically replaces the running file (*.tmp then rename). It only replaces when the remote version is strictly newer; equal version with a different SHA does not replace, to avoid flapping.

  • TUI startup checks the channel before entering the alternate screen and re-execs the new binary with the same arguments if it was replaced.
  • The agent systemd unit runs ExecStartPre=-steelframe update. The - prefix means a downed CDN does not take the agent offline.
  • STEELFRAME_UPDATE_BASE overrides the channel base.
  • STEELFRAME_NO_UPDATE=1 skips the TUI/agent update check entirely. The agent also skips the poll when auto_update_minutes=0.

The agent polls latest.json every auto_update_minutes (default 5; 0 disables). When a newer version is pending:

  1. Dispatch is locked, so no new workers start.
  2. The agent waits for in-flight workers to finish — the drain gate. It fails closed: if the worker count cannot be read, the gate holds and the next watchdog tick retries.
  3. The agent applies the binary and re-execs.

Restart=always plus ExecStartPre=steelframe update cover the case where the process exits instead of re-execing.

The Dashboard shows auto_update.enabled, pending, and draining.

FailureBehavior
CDN timeout / 5xxKeep the current binary, warn, start normally
SHA-256 mismatchDelete the temp file, do not replace, warn
Disk full / permissionWarn, start the current binary
Unknown targetinstall.sh exits non-zero with an OS/arch message
agent install missing master keyRefuse and print how to set STEELFRAME_MASTER_KEY
  • A failed update never bricks a running install: the current binary stays in place.
  • The GUI origin in front of R2 must send Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp for the Web target to work.
  • The agent does not delete steelframe.db, credentials, or projects.toml when it updates.