Repository navigation
Releases: Cd1s/ssm
Release list
v2.3.2
This is a v2 patch release with a security-driven toolchain update from
PR #130. It adds no new feature and changes no command behavior. The official
exact-tag release workflow creates the stable v2.3.2 Release with
make_latest=false; v2.3.1 remains GitHub latest until the published-asset
audit and canaries pass and a separate promotion is made. Ordinary ssm update
stays within v2.
Security: toolchain
- The build toolchain moves from Go 1.26.9 to Go 1.26.10, fixing GO-2026-6809
reported bygovulncheck. - The
crypto/x509fix stops trusting compromised intermediate certificates.
SSM is affected because release provenance verification in
internal/provenancereachesx509.Certificate.Verifythrough Sigstore
bundle verification.
There is no request-schema or JSON-field breaking change. No request or result
field and no error code is added or removed. The request schema structure is
unchanged; only its version-bound $id and title are updated to v2.3.2.
Changes since v2.3.1
- Updated the build toolchain from Go 1.26.9 to Go 1.26.10 to fix
GO-2026-6809 incrypto/x509(PR #130).
v2.3.1
This is a v2 patch release with a security-driven toolchain and dependency
update plus small fixes. It adds no new feature. The official exact-tag release
workflow creates the stable v2.3.1 Release with make_latest=false; v2.3.0
remains GitHub latest until published-asset canaries pass and a separate
promotion is made. Ordinary ssm update stays within v2.
Security: toolchain and dependencies
- The build toolchain moves from Go 1.26.8 to Go 1.26.9.
golang.org/x/netmoves to v0.60.0, which fixes GO-2026-6603,
GO-2026-6610, GO-2026-6611, GO-2026-6612, and GO-2026-6617 reported by
govulncheck.golang.org/x/crypto,golang.org/x/sys,
golang.org/x/term, and the othergolang.org/xmodules are updated with it.
Static release binaries
All official release binaries are now built with CGO_ENABLED=0. The
linux-amd64 binary was previously dynamically linked against glibc and is now
statically linked, which benefits hosts with an older glibc and Alpine. This is
a user-visible change in how the binary is built; command behavior does not
change.
sshctl tunnel port parsing
-L 0:host:portand-L [bind:]0:host:portnow correctly listen on port 0,
so the operating system assigns the listening port.- A remote IPv6 target that contains a
:0:segment is no longer altered. - Ports accept plain decimal digits only.
+80and-0are now rejected; this
tightens input that was previously accepted. - Remote target port 0 is still rejected.
Development script
scripts/ssh_matrix_test.sh now works with PAM public-key authentication when
run as root. It is a development and CI script only and is not part of the
release binaries.
There is no request-schema or JSON-field breaking change. No request or result
field and no error code is added or removed. The request schema structure is
unchanged; only its version-bound $id and title are updated to v2.3.1.
Changes since v2.3.0
- Updated the build toolchain to Go 1.26.9 and
golang.org/x/netto v0.60.0,
clearing fivegovulncheckfindings. - Built all official release binaries with
CGO_ENABLED=0, so linux-amd64 is
now statically linked. - Fixed
sshctl tunnellistener port 0 handling and remote IPv6 targets
containing:0:; ports now accept plain decimal digits only. - Fixed
scripts/ssh_matrix_test.shPAM public-key authentication when run as
root.
v2.3.0
This is a v2 minor release. The official exact-tag release workflow creates
the stable v2.3.0 Release with make_latest=false; v2.2.0 remains GitHub
latest until published-asset canaries pass and a separate promotion is made.
Ordinary ssm update stays within v2.
sshctl tunnel
sshctl tunnelprovides foreground-Llocal port forwarding and-D
dynamic forwarding. It reuses the existing connection path, so host-key
verification andproxy_jumpcontinue to apply.- Local listeners bind to loopback by default. A non-loopback bind requires
both--allow-remote-bindand--yes. --ready-filewrites readiness state, and--durationbounds the
foreground lifetime. Readiness and termination each produce one JSON line
when JSON output is selected.- An SSH disconnect exits the tunnel; it does not reconnect automatically.
- New error codes are
tunnel_invalid_arguments,tunnel_bind_failed, and
tunnel_remote_bind_refused. - This release does not include
-R, a background daemon, or automatic
reconnect.
Important update fix: Alpine and gcompat
Older ssm update versions could write the new version onto the system C
library on Alpine with gcompat (/lib/ld-musl-x86_64.so.1), causing most host
programs to crash with segmentation faults. The updater now replaces only a
file named ssm or sshctl; otherwise it refuses and explains the reason
without changing any file.
On Alpine, do not use ssm update to upgrade from v2.2.0 or earlier: those
versions contain this defect. Download the matching release asset manually,
verify its SHA-256, and replace the binary. After upgrading to v2.3.0,
ssm update is safe.
Sync server token history
Each account now retains 256 login-token hashes instead of 16. With 16
machines under one account, logging in on one more machine could evict another
machine's token and make its next sync fail with HTTP 401. Deploy the new
internal/syncserver server for this change to take effect.
There is no request-schema or JSON-field breaking change; no request schema or
JSON field has a newly added or changed breaking form.
The checked range has no request-schema file changes; request and result
additions remain additive. Error codes are additive: these tunnel codes are
new, and existing codes are unchanged.
Changes since v2.2.0
- Added foreground SSH tunnel forwarding and its lifecycle/error contracts.
- Fixed tunnel half-close handling so replies are not truncated.
- Fixed updater executable lookup and refusal to replace foreign executables.
- Increased sync-server login-token history from 16 to 256.
v2.2.0
This is a v2 minor release containing all fixes from PR #120 (the reviewed
commits in 6431cae..c77ffdf). It changes some default behavior and has a
vault-format compatibility requirement. The official exact-tag release
workflow creates the stable v2.2.0 Release with make_latest=false;
v2.1.0 remains GitHub latest until published-asset canaries pass and a separate
promotion is made. Ordinary ssm update stays within v2.
Upgrade before saving or publishing (read first)
- Vault format v2 encryption. New vaults use an authenticated monotonic
generation. Older clients (v2.1.0 and earlier) cannot read a v2-format vault;
upgrade all machines to v2.2.0 before any machine saves or publishes one.
v2.2.0 continues to read old-format vaults. - Conditional sync publication. The sync server now checks
If-Matchand
If-None-Matchpreconditions on uploads. If two machines upload at once,
only one succeeds and the other receivessync_conflict. A new client can
still push to an old server, but that server cannot provide this protection.
If the localremote.etagcache is missing while a remote vault exists, push
first requiressyncorpull. - Deploy the new
internal/syncserverbefore relying on the upload checks.
User-visible changes
- A push whose scope no longer matches an interrupted publication fails as
sync_push_failed(exit 1), and it does not publish extra inventory. - A downloaded vault that fails authentication is rejected and the existing
connections.enc.previs retained. - A client that has never synchronized reports empty
last_pulland
last_syncvalues. password_cacheandvim_keyssettings are ignored;putto an existing
remote directory fails.- If the server returns an older vault (a rollback), the client reports
sync_conflict; review it and usepull --adopt-remoteto adopt it. - When
HOMEcannot be determined, startup errors and instructs the operator
to setHOMEorSSM_CONFIG_DIR. - Credential files passed with
--password-file,--key-file,--secret, or
--master-pass-filethat are readable by other users produce a warning.
SetSSM_NO_PERMISSION_WARNING=1to suppress it; Windows does not warn. getdirectory downloads reject device files and remove special permission
bits. Cloud clients reject cross-host or downgrade redirects.statusreuses one unlock and completes faster.
There is no new error code and no request-schema or JSON-field breaking
change. The checked range has no request-schema file changes
(git diff 6431cae..c77ffdf --stat -- skills/agent-ssm/references/*.json is
empty); result and request additions remain additive.
v2.1.0
This is a v2 minor release. It fixes the agent-experience issues from the
2026-09 usage audit (tracking issue #89) and changes some default
behavior and some error codes. Only some of the changes have a
compatibility switch; the lists below say which. The official exact-tag release workflow creates the stable v2.1.0
Release with make_latest=false; v2.0.2 remains GitHub latest until
published-asset canaries pass and a separate explicit latest-promotion
decision is made. Ordinary ssm update stays within v2, and once v2.1.0 is
promoted, same-major ssm update (including automatic update) adopts these
defaults on existing installs. If a fleet needs the old behavior, pin
sync_mode: strict and SSM_RUN_OUTPUT=buffered first.
Default behavior changes (read before upgrading)
- Local-first inventory (#72). Read commands (
run,map,get,put,
check,doctor,list,host list|show,host-key,keys,status)
read the local vault and no longer contact the sync server first; a sync
server outage no longer fails them. Automatic sync runs in a detached
background process, rate-limited (sync_interval, default 10m) with
exponential backoff (30s doubling to 1h).statusreports freshness
(remote_state,last_successful_sync,last_sync_error,
next_sync_attempt,cache_age_seconds,inventory_stale,
inventory_unsynced); inventory older thanstale_after(default 7d) is
reported stale.runJSON addsinventory_stale,inventory_unsynced,
inventory_sync_error. Restore the v2.0.2 behavior with
"sync_mode": "strict"insettings.jsonorSSM_SYNC_MODE=strict.
--offlineis deprecated for reads (it now only suppresses background
sync);SSM_OFFLINE=1is equivalent.loginnow fetches the inventory
immediately. Local vault mutations and every pull (explicitsync/pull,
strict-mode refresh, background) take a short vault write lock; a
concurrent writer can produce a bounded (5s) "vault is busy ... retry"
failure. - Streaming human-mode
runoutput (#71). Output is streamed as it
arrives instead of being held until the command exits (no more 8 MiB
per-stream limit); stdout is byte-exact, stderr is redacted line by line.
--jsonstill returns one complete value.SSM_RUN_OUTPUT=buffered
restores the previous buffering. Local SIGINT/SIGTERM/SIGHUP are forwarded
to the remote command (interrupted, exit 128+signal). - Connect timeout covers the SSH handshake (#73).
--connect-timeout
(default 15s) bounds TCP connect plus the SSH handshake and
authentication; legacy--timeouton run/exec/plan/map/--streamis a
deprecated alias of it (last flag wins; flags beatSSM_CONNECT_TIMEOUT/
SSM_TIMEOUT).put/get/cpkeep--timeoutas the transfer timeout.
See the lists below for the resulting error-code changes. - SSH keepalive on by default (#73).
keepalive@openssh.comevery 15s;
the connection is closed after 3 unanswered probes (connection_lost).
SSM_KEEPALIVE=0disables it;SSM_KEEPALIVE=<duration>changes it and an
invalid value falls back to 15s.
Error-code changes (affects automation that matches error)
These replace codes that were too generic (internal) or split out a specific
cause. None has a compatibility switch. Exit 255 is the transport-failure exit
(dial_*, connection_lost, handshake_failed, session_limit).
- A dropped connection after the command was sent:
connection_lost
(stageremote_execution, exit 255, additiveoutcome:"unknown") instead
ofinternal(exit 1); aput/getwhose connection breaks midway is
connection_lost(exit 255) instead ofremote_write_failed/
remote_read_failed(exit 1) (#79). - A handshake failure or stall after TCP connected:
handshake_failed(stage
handshake, exit 255) instead ofinternalordial_timeout(#73, #79). - Windows refused connection:
dial_refused(stagedial, exit 255) instead
ofinternal(exit 1) (#85). - A session-limit wait that gives up:
session_limit(stagesession, exit
255) instead ofsession_failed(#76). - New codes:
exec_timeout(exit 124),remote_shell_unsupported(exit 1),
sftp_unavailable(exit 1) andproxy_jump_invalid(exit 2);error
values that v2.0.2 never produced.
Changes without a compatibility switch
Besides the error codes above:
--connect-timeout(and requesttimeouton run requests,--timeouton
run/exec/plan/map) now also covers the SSH handshake and authentication,
not only TCP connect. Mitigation: pass a larger--connect-timeout.- CLI duration flags reject integers with trailing text (
30abc) and other
malformed values.
Changes that do have a switch: sync_mode: strict / SSM_SYNC_MODE=strict
(local-first reads), SSM_RUN_OUTPUT=buffered (streaming run output),
SSM_KEEPALIVE=0 (keepalive), --no-stdin / SSM_FORWARD_STDIN=0
(stdin forwarding stays as before by default).
New features
--exec-timeoutfor run/exec/map/--streamand request-v1
exec_timeout: SIGTERM at the deadline, 5s grace,exec_timeout, exit 124,
timed_out:true, output so far kept (#73).- Explicit stdin forwarding:
--stdin,--no-stdin,--stdin-file,
request-v1stdin_file, JSONstdin_forwarded; defaults unchanged (#77). --interpreterfor non-shell-fscripts and shebang-aware script errors (#81).- Command and flag suggestions instead of
alias_not_foundfor typos (#82). --retry-dial N[:backoff](N at most 10, exponential backoff with jitter
frombackoff, default 250ms, capped at 30s) andsshctl wait <alias>
(--timeout,--interval,--until ssh|tcp): retry only transport
failures before the host key arrived; never retry authentication or
host-key failures; one SSH connection perwaitattempt (#85).- SFTP transfers for targets without a POSIX shell (#87):
put/get --sftp,
host setting--transfer auto|shell|sftp(host.transferin request v1,
defaultautothrough the shell path) and a per-operation request
transferfield. SFTP handles single regular files only. New error codes:
remote_shell_unsupported(stagediscovery) andsftp_unavailable
(stagecapability). - ProxyJump aliases (
host ... --proxy-jump <alias>,host.proxy_jumpin
request v1; chains up to 5 hops, no cycles, otherwiseproxy_jump_invalid,
exit 2) with per-hop host-key verification and per-hop local credentials,
an optionalviafield on failures, andsshctl cp <a>:<path> <b>:<path>
(single regular file) relayed through this machine with three-way SHA-256
verification.cp --directis not implemented.
Upgrade every client before usingproxy_jump: v2.0.2 and older
ignore the field and drop it if they re-save the synced vault.
Fixes
- Security: Go 1.26.8,
golang.org/x/cryptov0.56.0, grpc v1.83.1 (#69). -h/--helpand--jsoninside remote argv are no longer captured by
sshctl;ssm pull --helpno longer performs a pull (#70, #75).- host-key: a different recorded key type is no longer reported as a MITM
mismatch;acceptno longer deletes other key types (#74). - Sync failures keep and classify their cause (
cause) without exposing the
server address (#78). - Directory transfer error handling,
--sha256digest-tool detection,
--dir-mode, get/put option parity (#80). - The connection pool keeps the shared connection when the server's session
limit is reached and waits for a free slot (#76). - Help, README and agent skill aligned with the actual flags and environment
variables, enforced by a contract test (#83).
Maintenance
- The test suite no longer requires the pinned toolchain, CI triggers were
cleaned up, and a weeklygovulncheckruns (#84, #88).
Request schema (request v1, version stays 1)
Compared with v2.0.2, every change is additive, and no request that v2.0.2
accepted is now rejected (v2.0.2 already rejected unknown fields):
- New
run/planfields:interpreter,exec_timeout,stdin_file
(stdin_fileis not valid withscript_file;interpreteronly with
script_file;preflight:trueis invalid with a non-shell interpreter). - New
put/getfields:transfer(auto|shell|sftp);dir_modeon
put.getnow also acceptssha256andtimeout(v2.0.2 rejected them). - New
hostfields:transferandproxy_jump(empty string clears it). - Run-only fields (
interpreter,stdin_file,exec_timeout) stay invalid
onput/get;transferis invalid on run, check, doctor and host ops. op:getitself is not new; it was added in v2.0.0.
There is no request-schema or JSON-field breaking change: new result fields
(inventory_*, stdin_forwarded, via, outcome, timed_out,
dial_attempts) are additive. The behavior changes are the default changes
and the error-code changes listed above; v2 compatibility behavior remains
available only through the switches named there.
v2.0.2
This is a backward-compatible patch release containing the reviewed v2.0.2
repair work:
- add explicit, identity-checked reviewed remote adoption for true
no-transaction divergence; - honor
SSM_CONFIG_DIRfor the encrypted vault and private sidecars; - keep ordinary pull fail-closed and preserve the v2.0.1 public contract.
The official exact-tag release workflow creates the stable v2.0.2 Release with
make_latest=false; v2.0.1 remains GitHub latest until a separate explicit
latest-promotion decision and its canaries pass.
v2.0.1
This is a backward-compatible patch release. The official exact-tag release
workflow creates the stable v2.0.1 Release with make_latest=false; v2.0.0
remains GitHub latest until the published candidate assets and canaries pass
and a separate explicit promotion makes v2.0.1 latest.
- Fix fresh installation with GitHub CLI 2.92.0.
gh attestation verify
requires attestation bundle files to have a.jsonor.jsonlsuffix;
install.shnow downloads the bundle into a privatemktempdirectory as
attestation.jsonand removes the file and directory on both success and
failure. - There is no protocol or schema breaking change, and v2 compatibility
behavior remains unchanged.
v2.0.0
This section is prepared for the authorized stable v2.0.0 Release. The exact-tag
release workflow may publish it only with make_latest=false; v1.4.4 remains
GitHub latest. All child tickets through Issue #31 are closed and the #31 final readiness
proof is an executable, non-publishing verify release profile whose
final v2-readiness-report check validates the checked-in evidence map against
the live manifest. BC-1 through BC-10 are the approved compatibility migration
rows; their complete field-level old/new/action/machine/rollback matrix
is part of this release contract. Verification is non-mutating and does not publish
a tag or release. Only the official exact-tag release workflow may
publish after every source release blocker, migration review, pinned provenance
check, rollback rehearsal, native CI gate, and independent review passes.
Pinned release provenance
- The updater contract requires automatic and manual same-major updates,
explicitly authorized major migrations, and installer replacements to carry both the selected
SHA-256 digest and keyless provenance for the exact downloaded asset. - Provenance is pinned to
Cd1s/ssm, the reviewed release workflow identity,
GitHub Actions' OIDC issuer, the supported six-target manifest, and the
reviewed identity-rotation state. Missing, malformed, unverifiable,
wrong-subject, or expired provenance leaves the existing executable intact. - The official release build publishes one adjacent Sigstore bundle per binary.
Checksums remain an independent digest input but cannot authorize replacement alone. - Redirected and unknown-length release responses are streamed through exact
1 MiB metadata/provenance, 16 KiB checksum, and 64 MiB binary ceilings. - The installer uses a same-directory staging template accepted by both
GNU and macOS/BSDmktemp. Windows self-update renames the mapped old image
aside under an exclusive same-directory update lock, installs the
identity-checked sibling stage synchronously, and rolls back bytes and
security on failure. Ordinary users preserve and verify owner, primary
group, DACL, and DACL inheritance; tokens that can enable all three Windows
backup/restore/security privileges also preserve the complete descriptor.
A later launch removes only an identity-bound completed rollback image. - On a trust failure, keep using the preserved executable and wait for a
repaired release through a reviewed identity. Installer users need a current
GitHub CLI with attestation verification andhead -c; there is no
verification skip.
Reviewed saved-key publication
- The v2 publication preflight rejects unsatisfied cross-alias saved-key
create, replace, rename, delete, prune, and reference prerequisites before
any sync request. - The safe failure message lists every required stable transaction ID in
ledger order with alias, operation, key name, and reason. Publish those IDs
explicitly in order, then retry the original transaction; SSM never adds
prerequisites or unrelated pending mutations to the selected scope. - Modern direct and request-v1 host mutations retain their existing encrypted
vault/ledger schema, transaction ID format, and typed success fields. Legacy
remove, saved-key removal, and guarded import now append pending transactions
without automatic publication. - A state-changing host mutation returns a stable
transaction_id. An
idempotent update/upsert can instead returnchanged:false,
action:"unchanged", withtransaction_idomitted; it creates no new
transaction, so do not publish it.
Exact push scopes and empty-ledger safety
- Bare
pushis invalid and is rejected before vault unlock or any sync HTTP
request. Callers must choosesshctl --json push --only <transaction-id>or
sshctl --json push --all. sshctl --json push --allpublishes only the invocation-start ordered pending-ID snapshot;
later transactions remain pending.- An empty
push --allperforms one identity HEAD and no GET or PUT. Identical
local, cached, and remote encrypted-blob identities returnaction:"noop";
any missing or different identity returns a safesync_conflict, preserves
both blobs and private identity evidence, and never publishes a full blob. - English, Chinese, agent-skill, command-help, and recovery guidance now use
only explicit publication scopes. - The empty-ledger
sync_conflicthint recommends the guarded--merge
recovery path. It does not suggest--replace --yes; the longer recovery
guide permits replacement only after explicit full-replacement review.
Complete v2 compatibility migration
- BC-1: a present malformed or unreadable
cloud.jsonis a fatal
sync_config_errorfor every online inventory operation. Missing remains
unconfigured; explicit global--offlinealone accepts cached state and
performs no cloud parsing or network access. - BC-2: exact scoped publication preflights transitive alias and saved-key
prerequisites without automatically adding them. Publish the listed stable
IDs explicitly in ledger order. - BC-3: stream startup and terminal failures use compact NDJSON from the
first byte. After initialization, each consumed non-empty input line has one
ordered result; no ready, summary, or footer record is emitted. - BC-4: state-changing inventory entry points, including legacy remove,
saved-key removal, and guarded import, create stable pending transactions and
never auto-publish. A preserved idempotent host no-op haschanged:false,
action:"unchanged", omitstransaction_id, and creates no transaction. - BC-5: bare push is invalid, non-empty
--allfixes its ordered scope at
invocation start, and empty--allnever performs a PUT. - BC-6: online streams require a positive refresh interval. Zero refresh
is valid only with explicit global offline mode and one fixed cached
snapshot. - BC-7: direct and request-v1 file/directory put/get results have matching
direction,kind, andstage. Directory operations report
atomic:false,integrity:not_available, andresume:unsupported;
directory get omitsbytes_receivedrather than inventing a tar byte count. - BC-8: automatic and ordinary manual replacement remains within the
installed major.ssm update --majorreviews the migration and
ssm update --major --yesis the sole non-interactive major authorization. - BC-9: checksums cannot authorize replacement without pinned keyless
provenance and the exact 14-name release manifest. - BC-10:
make checkis the non-mutatinggo run ./cmd/verify ciadapter;
verify releaseis a non-publishing strict superset.
The completed issues #2 through #11 remain preserved baselines rather than new
v2 features: non-interactive execution, scoped transactions, stable machine
errors, explicit offline/freshness, exact alias and host-key handling,
observable/resumable transfers, agent-safe invocation, and pre-unlock help all
remain compatible.
Migration and rollback
- Back up the encrypted vault,
remote.etag,publishing-intent.json,
sync-conflict.json, pending ID order, and authenticated executable-recovery
state without exposing their contents. - Run
ssm --json update --majorfirst. Require all automated checks and
manually review bare-push consumers, zero-refresh online streams, and
directory-transfer parsers before using--major --yeson a canary. - Authorization never bypasses digest/provenance verification. A failed trust,
migration, or replacement gate preserves the old executable and evidence. - There is no automatic down-migration. Reinstall v1 only when the encrypted
on-disk state is compatible, no publication/update recovery is outstanding,
and remote identities and pending transactions have been reconciled. When
state is uncertain, restore/recover v2 first and do not mutate further. - Verification does not merge, tag, install, upload, publish artifacts, or
create a release. Only the official exact-tag workflow can publish after all
migration, CI, provenance, rollback, and review blockers pass.
v1.4.4
These notes are prepared for the authorized official v1.4.4 maintenance bridge.
Publication is permitted only from the immutable v1.4.4 tag by the official
exact-tag release workflow. A source checkout or non-publishing candidate gate
does not create a tag or GitHub Release.
Major-version safety bridge
- Make automatic update and ordinary
ssm updatesame-major only. Seeing a v2
GitHub Release never requests a v2 binary or replaces the running v1
executable; same-major v1 maintenance updates remain available. - Add the sole explicit migration path:
ssm update --majorpresents the
BC-1..BC-10 review and release evidence without mutating state, while
ssm update --major --yesauthorizes replacement only after the approved
read-only preflight succeeds. - Require the exact selected binary digest and pinned keyless provenance for
repositoryCd1s/ssm, the release workflow, the GitHub Actions issuer and
hosted runner, one exact subject, and the exact selected tag ref. Missing,
malformed, replayed, branch-based, or mismatched evidence fails closed. - Preserve the executable and encrypted v1 state on preflight, verification,
or replacement failure. Use authenticated native Unix replacement and
Windows deferred replacement/recovery semantics.
Staging and compatibility
- Keep all legacy asset names,
checksums.txt, and theinstall.shasset so
existing v1.4.3 clients can receive the official v1.4.4 bridge. The bridge
installer now verifies the exact 14-entry selected-tag manifest, digest, and
pinned provenance before replacement. For an existing installation it also
refuses an older or cross-major selected release before any asset request;
major migration remains solelyssm update --major --yes. Adjacent
provenance also supports that later explicitly authorized v2 migration. - Document the reviewed identity overlap/expiry/removal gates and fail-closed
emergency recovery. Rotation never permits checksum-only fallback or a
verification bypass. - Use bridge-first staging: a v1 bridge decision must happen before any v2
latest decision. v2 cannot become GitHub latest without a separate human
release authorization after bridge rollout evidence is reviewed. - Add a credential-free, non-publishing candidate gate that verifies exact
v1.4.3 ancestry, source version, release notes, all six platform builds,
manifest/checksums, and synthetic exact-tag provenance.
v1.4.3
Fast agent execution
- Keep one-shot literal commands on the direct
sshctl --json run <alias> --argv ...path; typed request files remain the safer path for dynamic argv, scripts, secrets, and mutations. - Add
sshctl run <alias> --stream, a headless NDJSON argv stream that syncs and decrypts once, reuses the SSH connection across commands, refreshes inventory every 30 seconds by default, and stops rather than using stale data after a refresh failure. - Reuse the already-validated decrypted vault for the command's first load, removing the duplicate Argon2 decrypt previously performed immediately after unlock while allowing later same-process loads to observe intervening saves.
- Open the actual pooled SSH session directly instead of opening and closing a probe channel first. Serialize dials per destination rather than globally, allowing different fleet targets to establish SSH connections concurrently.
Validation
- Add strict stream parsing/error tests and an in-process SSH regression proving that two commands use one connection and exactly two command sessions.