Release Notes
Changed
-
Cached values are written in cache envelope v4 so binary values survive
caching (0.21+). Entries written by earlier releases stay readable, but
SecretSpec 0.20 and earlier cannot read v4 entries: they warn, read the
authoritative provider instead, and never replace or clear those entries.
Do not share one cache store between 0.20 and 0.21. -
Command generators store their output exactly, including a trailing newline
(0.21+). A generator such ascommand = "openssl rand -hex 32"now produces
a value ending in\n; useprintf,tr -d '\n', or an equivalent to omit
it. Previously generated values are unaffected. Output that is empty or only
Unicode whitespace is still rejected. -
secretspec getwrites the exact value without a trailing newline when
stdout is a pipe or file; a terminal still gets one (0.21+).$(...)
substitution is unaffected. -
The Rust API is byte-native (0.21+).
Secrets::settakesSecretBytes:
replaceset(name, Some(value))withset_text(name, &value)and
set(name, None)withprompt_and_set(name). TheProvidertrait'sget,
set, andget_manyuseSecretBytes,Provider::namereturns&str, and
generator::generatereturns bytes.
Added
-
Versioned local IPC (0.21+).
secretspec serveruns a private stdio
resolver that answers the Secret Resolution Protocol, and out-of-tree
provider endpoints can be installed and discovered as trusted external
providers. Highlights:- Independent clients: the Rust
secretspec-ipccrate (async, plus a
blockingfeature that needs no async runtime, and SSH or existing
authenticated streams for remote connections) and the pure C
libsecretspec-resolver. Both enforce the same framing limits, request
deadlines (clamped to 300 seconds), and error kinds, and report an endpoint
that writes non-protocol text to stdout by name. - Exact-name resolution with inline values or resolver-owned file leases,
binary values, secret expiry (expires_at_unix_ms) kept separate from
cache freshness (refresh_at_unix_ms), and optional value-bound revision
tokens for downstream cache invalidation. - Optional
resolver.setandresolver.deletemethods store or remove a
declared secret on the route the session reads from, bounded by the active
scope.secretspec serve --read-onlyadvertises resolution only and
refuses any resolution that would write, including generation and
prompting. prompt = truedeclarations resolve through aclient.promptcallback to
the launching process when it says it can answer; headless clients are
never asked. The C client exposes this without callbacks through
SECRETSPEC_RESOLVER_ANSWER_PROMPTS.- External providers request URI-specific credentials at runtime.
config provider loginandsecretspec setprompt for missing ones and
store answers in the configured credential source or a system keyring
namespace private to the provider URI. Endpoints receive only a base
environment plus the variables their discovery claim declares in
environment, never other providers' tokens. - Endpoints answer
rpc.discoverwith a self-contained OpenRPC description.
Sessions carry declared project context, an optional requested
authorization lifetime, and opaque interaction references that are kept in
the audit log for approval surfaces. - Provider discovery and lease files are isolated with Unix permissions and
Windows ACLs.
Building
libsecretspec-resolverfrom source needs a system yyjson
(pkg-config for Meson,find_package(yyjson CONFIG)for CMake); static
consumers add-lyyjson, whichsecretspec-resolver.pcrecords as
Requires.private. - Independent clients: the Rust
-
Tailscale Setec provider (
setec://, 0.21+): store, retrieve, discover,
and delete secrets, including binary values, through a tailnet-authenticated
Setec server, with reads pinned to a Setec version. -
Doppler provider (
doppler://PROJECT[/CONFIG], 0.21+): read, write, and
delete secrets over Doppler's REST API, authenticated withDOPPLER_TOKENor
thetokenprovider credential. Secret names are stored verbatim in the
Doppler config named by the SecretSpec profile, or in a config pinned in the
URI, so they stay readable throughdoppler runand the Doppler dashboard.
Batch reads fetch only the declared names,init --fromdiscovers names
without reading values, and values assembled from${...}references arrive
resolved. Doppler's reserved names, and names or values Doppler cannot store
unchanged, are refused rather than rewritten. Configs are read concurrently,
a manifest too large to name in one request URI is split across several, and
a rate-limited or failed request is retried after Doppler's suggested wait.
Arestrictedsecret Doppler will not serve to a personal or CLI token is
reported as the refusal it is, and is not deleted either. Projects and configs
must already exist. A cached alias whose cache and source name the same
Doppler secret under the active profile is refused. -
Secret values are byte-native (0.21+). Values flow through providers,
fallback chains, imports, and the cache as arbitrary bytes.
secretspec set --from-fileaccepts exact byte input,as_pathpreserves it
byte-for-byte, and Rust callers can useresolve_bytes()and
resolve_named_bytes(). The file, systemd credential, environment, keyring,
Google Secret Manager, Kubernetes, and Scaleway providers preserve binary
values, and AWS Secrets Manager supportsSecretBinary.runpasses
non-UTF-8 values on Unix and rejects NULs before starting the child. Text-only
consumers (SDK text responses, exports, SOPS, LastPass) return explicit UTF-8
errors that name the affected secret. Provider credentials retain their bytes,
and unusable explicit credentials, including empty values, never silently
select an environment fallback with another identity. -
JSON Schemas for
secretspec.tomland userconfig.tomlprovide editor
autocomplete, hover descriptions, and structural validation. Export schemas
matching the installed CLI withsecretspec schema --config projector
secretspec schema --config global(0.21+). -
Projects can select a default provider chain with
[defaults].providers
(0.21+). Secret and profile provider chains retain precedence, and the
project default can name a user-global alias whosereftemplate expands
{project},{profile}, and{key}for the active project. Native SDK
inline declarations expose the same setting through inline schema v2; v1
declarations remain accepted. -
Claude Code can retrieve Anthropic API and LLM gateway credentials from any
SecretSpec provider through its nativeapiKeyHelper.secretspec claude configureandunconfiguresafely manage repository or user settings,
including worktrees andCLAUDE_CONFIG_DIR, without replacing unrelated
helpers.loginandlogoutmanage credentials isolated by settings scope
and API resource; custom manifests remain available through--file(0.21+). -
Bitwarden Password Manager references accept an exact item UUID for reads and
writes, so one of several same-named items can be addressed without renaming
it (0.21+). Imports that would target one item field twice are rejected
before writing. -
OpenPGP and OpenSSH private keys can be generated with
type = "openpgp_private_key"(with an explicitgenerate.user_id) andtype = "ssh_private_key"(0.21+). OpenPGP keys use signing, encryption, or combined
capability profiles; both default to Ed25519/Curve25519, with configurable
RSA available for compatibility. -
The JVM SDK supports inline secret declarations through
withInlineSpec
(0.21+).
Fixed
-
HTTP providers (Vault, OpenBao, Infisical, Cloudflare, Scaleway, Azure App
Configuration, Doppler, and Setec) now use a 10 second connect timeout and a
60 second request timeout, so a stalled connection fails instead of hanging
runorcheck. Vault and OpenBao retries on timeout now take effect. -
On macOS, the keyring provider no longer prompts for the login keychain
password on every run after SecretSpec is upgraded. Keychain items are bound
to the code signature of the build that created them, which changes with
every release of an ad hoc signed build (Nix, Homebrew,cargo install).
The first read of each secret by a new build may prompt once; approve it with
"Always Allow" so later runs of that build stay silent. SecretSpec explains
how to avoid repeated prompts, leaves items untouched during reads, and
retries in-place writes after requesting access when an older build created
the item (0.21+). -
macOS keyring reads preserve existing items and their access settings even
when approval or a later write fails. Keychain lookups no longer change the
prompt setting for other operations in the same process (0.21+). -
KeePass KDBX 4.0 databases can be written after creation or editing in
KeePassXC. Writes upgrade the file format to KDBX 4.1 while preserving
encryption and key-derivation settings. -
Bitwarden Password Manager resolves a batch of secrets with a single
bw list itemsinstead of one listing per secret. Single reads whose
bw list items --searchprefilter returns only similarly named items now
fall back to the full listing, so they find the same items as batch reads
and writes. -
pass, gopass, and LastPass preserve whitespace and multiline secrets across
generation and subsequent reads. pass entries are stored newline terminated
like thepassCLI writes them, and exactly one final newline is removed on
read, so entries created withpass insertresolve to their password.
gopass keeps storing single-line values as plain text entries thatgopass showand earlier releases read, and stores multiline, whitespace-padded, or
non-UTF-8 values in its lossless binary-entry format; existing text entries
still return only their trimmed first line until they are written again.
LastPass's CLI newline handling no longer changes stored values. -
LastPass rejects NUL-containing values before writing instead of silently
truncating them; use a manifest encoding such as base64 to store these values.
Install secretspec 0.21.0
Install prebuilt binaries via shell script
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/cachix/secretspec/releases/download/v0.21.0/secretspec-installer.sh | shDownload secretspec 0.21.0
| File | Platform | Checksum |
|---|---|---|
| secretspec-aarch64-apple-darwin.tar.xz | Apple Silicon macOS | checksum |
| secretspec-x86_64-apple-darwin.tar.xz | Intel macOS | checksum |
| secretspec-x86_64-pc-windows-msvc.zip | x64 Windows | checksum |
| secretspec-aarch64-unknown-linux-gnu.tar.xz | ARM64 Linux | checksum |
| secretspec-x86_64-unknown-linux-gnu.tar.xz | x64 Linux | checksum |
| secretspec-aarch64-unknown-linux-musl.tar.xz | ARM64 MUSL Linux | checksum |
| secretspec-x86_64-unknown-linux-musl.tar.xz | x64 MUSL Linux | checksum |