v0.8.0
Security
- Model-triggered
bashcalls now run with a scrubbed subprocess environment:
ambient variables whose name matchesKEY/TOKEN/SECRET/PASSWORD/
PASSWD/CREDENTIAL, plus the configuredbackend.api_key_env, are dropped
before the child process starts. Previously bash subprocesses inherited the
full parent environment, so a shell command could read out the backend API
token or other ambient credentials it was never granted (#190). guardedmode's local-read guard now also denies known secret-bearing paths
— the resolved token file, a distinct configuredbackend.api_key_file, and
common credential fragments such as.ssh,.env,id_rsa,token,
secret,credentials— the same checkreadonlyalready enforced.
Previously this path check only ran inreadonly, so the defaultguarded
mode couldfile_reada secret file straight through (#191).- Tool call input, tool observation, thought, final replies, and PostToolUse
audit-hook payloads are now scanned for known secret values (the resolved
backend token and the value of any ambient secret-named env var) and
redacted to[REDACTED]before they reach the audit log, trace output, or
structured event sink. Previously these channels recorded raw text
verbatim, so a secret surfaced by any tool call could be durably persisted
inlogs/audit.jsonl. The live conversation history sent back to the model
is unaffected — only recording/observability channels are redacted (#189).
Added
- The audit log (
logs/audit.jsonl) now rotates into a bounded, gap-tracked
chain of numbered generations (logs/audit.jsonl.<gen>) instead of a single
destructively-overwritten.1backup. A durablelogs/audit.jsonl.gen
sidecar tracks the current generation across process restarts; up to
[audit].max_retained_generations(default8, override
SCOOT_AUDIT_MAX_RETAINED_GENERATIONS) retired generations are kept, and any
eviction beyond that cap is durably recorded in
logs/audit.jsonl.gaps.jsonlrather than silently disappearing.scoot doctornow reportsaudit.retentionasWARNif any gap was ever
recorded. This removes the audit-history data-loss blocker that a future
scoot-edgeaudit shipper would otherwise hit (#187). scoot-edgeE2 job dispatch:scoot-edge dispatch(one-shot) and
scoot-edge run --enable-jobs(folded into the heartbeat loop) poll aGET
job lease, schema-validate each envelope, and execute accepted jobs as
scoot --unattended -e "<goal>" --session-id job-<job_id>with cwd confined
to a required--job-root(never$HOMEor/). Both require--job-root
and--lease-url; either missing, or--lease-urlnot HTTPS without
--allow-insecure-http, is a config error (exit2), matching the existing
--center-url/token gate. A bounded, persistent idempotency store
(edge/idem.jsonl, capped by--idem-cap, default500) re-acks a
redeliveredidem_key's prior outcome instead of re-running the job, and
every phase transition (accepted→done/failed/rejected) is both
POSTed as ajob_eventand appended to an edge-side provenance log
(logs/edge-audit.jsonl), correlated bysession_idto Scoot's own run
audit. A new core--session-id <id>flag onscoot -elets a caller pin
the session file name instead of the default UUID, which is what makes this
job_id↔session_idcorrelation possible (#186).scoot-edgeE3 packaging (complete): the release workflow builds,
archives (scoot-edge-<target>.tar.gz+.sha256), and publishes a
scoot-edgeHomebrew formula (brew install jamiesun/tap/scoot-edge,
depending onscoot) for every tagged release, mirroring the existing
scoot-wasmpackaging.install.shgained an opt-inSCOOT_INSTALL_EDGE
variable that additionally downloads and installsscoot-edgealongside
corescoot; it is never installed unless explicitly requested. A new
optionalaptjob (gated byAPT_TAP_TOKEN, mirroring the Homebrew job's
HOMEBREW_TAP_TOKEN) builds a.debper Linux architecture and pushes it
to the sharedjamiesun/apt-tap
repository, which owns the GPG signing key and publishes the signed apt
index to GitHub Pages on every push, closing out #171.
Documentation
- Fixed docs, README, and book examples that showed the unattended one-shot
clamp asscoot -e --unattended <goal>— a rejected argv order, since
-e/--evalgreedily consumes the very next token as the goal string.
Examples and prose now consistently show the accepted
scoot --unattended -e "<goal>"order (#192).
Install
curl -fsSL https://raw.githubusercontent.com/jamiesun/scoot/main/install.sh | shAdd SCOOT_INSTALL_EDGE=1 to the same command to also install the optional
scoot-edge fleet companion (never installed unless requested).
macOS (Homebrew):
brew install jamiesun/tap/scoot # the agent
brew install jamiesun/tap/scoot-wasm # optional Wasm compute-unit host (pulls in scoot)
brew install jamiesun/tap/scoot-edge # optional fleet companion (pulls in scoot)Debian/Ubuntu (apt, scoot-edge only, amd64/arm64/armhf):
curl -fsSL https://jamiesun.github.io/apt-tap/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/jamiesun-apt-tap.gpg
echo "deb [signed-by=/usr/share/keyrings/jamiesun-apt-tap.gpg] https://jamiesun.github.io/apt-tap stable main" | sudo tee /etc/apt/sources.list.d/jamiesun-apt-tap.list
sudo apt update && sudo apt install scoot-edgeBuild flavors
Prebuilt archives are ReleaseSafe only (runtime safety checks on). If you
need a smaller binary, compile from source:
zig build -Doptimize=ReleaseSmall # smallest, fewer safety checksEach target also ships a separate scoot-wasm-* archive (the optional
standalone Wasm host) and a separate scoot-edge-* archive (the optional
fleet companion). The zero-dependency core scoot binary never embeds either.