SecretSpec 0.20: Git, Docker, inline specs, and five new providers
Let Git and Docker retrieve credentials from any provider, declare secrets in application code, use the JVM SDK, and deploy on Alpine.
SecretSpec 0.20 brings SecretSpec-managed credentials to Git and Docker, lets applications declare secrets directly in code, adds five providers, and expands support for JVM and Alpine applications.
This release includes:
- Git and Docker credential helpers: let ordinary Git and Docker commands retrieve credentials from any SecretSpec provider without copying them into another credential store.
- Declarations in application code: build secret specifications in Rust and prompt for missing values from typed loaders.
- libsecretspec and SDKs: pass inline specifications from eight SDKs, attach caller context, and adopt the new name for
secretspec-ffi. - Five new providers: use Azure App Configuration, Kubernetes, EJSON, Fly.io, and Cloudflare Secrets Store.
- JVM and Alpine support: resolve secrets from Java and Kotlin, install static musl CLI builds, and run the Node.js SDK in Alpine images.
- Other changes: forward container signals, report missing generated secrets correctly, emit shell completions, and make dotenv values round-trip.
Git and Docker credential helpers
Git and Docker can now read credentials directly from any SecretSpec provider. Configure each helper once, then keep using the usual Git and Docker commands.
Git credentials
The Git integration registers a helper for a host, then stores its token through your normal SecretSpec provider:
$ secretspec git configure \
--url https://github.com \
--username YOUR_USERNAME
$ secretspec git login https://github.com
After that, normal commands invoke git-credential-secretspec automatically:
$ git clone https://github.com/OWNER/PRIVATE_REPOSITORY.git
$ git push
The helper works before a repository has been cloned and can scope credentials by host or URL path. It also supports SMTP credentials for git send-email, so the password does not need to live in sendemail.smtpPass.
Docker credentials
The Docker integration uses one helper per registry. Configure the registry and store its token separately:
$ secretspec docker configure \
--registry ghcr.io \
--username YOUR_USERNAME
$ secretspec docker login ghcr.io
docker pull, docker push, docker build, and Docker Compose can now retrieve the value through docker-credential-secretspec:
$ docker pull ghcr.io/OWNER/IMAGE:TAG
Docker’s config.json contains only the helper configuration, not the credential value. Separate DOCKER_CONFIG directories can use different credentials for the same registry.
The helpers only read credentials. Use secretspec git login / logout and secretspec docker login / logout to change stored values. Use configure and unconfigure to add or remove the helpers themselves.
Declarations in application code
secretspec.toml remains the portable contract shared by the CLI and every SDK. Applications can now define that contract directly when keeping a separate manifest is inconvenient.
Rust-first specifications
Rust applications can use the new public Spec, Profile, and Secret types to build a specification in code:
use secretspec::{Profile, Secret, Secrets, Spec};
let spec = Spec::builder("checkout")
.provider("env", "env://")
.secret(
"DATABASE_URL",
Secret::required("PostgreSQL connection URL").providers(["env"]),
)
.profile(
"production",
Profile::new().secret(
"SENTRY_DSN",
Secret::required("Production Sentry endpoint"),
),
)
.build()?;
let mut secrets = Secrets::from_spec(spec)?;
secrets.set_profile("production");
let resolved = secrets.resolve()?;
Rust-built specs use the same validation as secretspec.toml. Spec::schema_json generates the same JSON Schema as secretspec schema, and SpecBuilder can update a file-backed spec without losing comments or formatting.
See Describing secrets in Rust for the complete builder and format-preserving editing API.
Typed Rust loaders generated by declare_secrets! can now call prompt_missing() to ask for and store missing required values. Prompting remains opt-in.
libsecretspec and SDKs
SecretSpec 0.20 gives the SDKs the same new capabilities across languages. It also renames secretspec-ffi to libsecretspec. Packaged SDKs handle the rename automatically; update your build only if you link or load libsecretspec directly.
Inline specifications
The SDKs for Go, Python, Node.js, Ruby, Haskell, PHP, C#, and Swift can now accept an inline specification. This is useful when the application already owns its configuration or cannot rely on a manifest on disk. For example, in Go:
spec := map[string]any{
"project": map[string]any{"name": "checkout"},
"profiles": map[string]any{"default": map[string]any{
"secrets": map[string]any{
"API_TOKEN": map[string]any{"description": "API token"},
},
}},
}
resolved, err := secretspec.New().
WithInlineSpec(spec, "/logical/project").
Load()
The base directory tells SecretSpec where to resolve relative provider paths and inherited manifests. Invalid fields and unsupported spec versions fail with an error.
Caller context
Git and Docker set caller context automatically, recording which tool and operation requested a secret. CLI and SDK callers can provide the same context:
$ secretspec get GITHUB_TOKEN \
--caller git \
--caller-version 2.51.0 \
--caller-operation credential_get \
--caller-resource github.com \
--reason "push release tag"
Audit records include the caller name, version, operation, and resource. This is separate from require_reason, so policies can record both which tool accessed a secret and why.
JVM and Alpine support
The new JVM SDK lets Java, Kotlin, and other JVM applications load SecretSpec secrets directly. Add it with Gradle:
dependencies {
implementation("org.cachix:secretspec-jvm:0.20.0")
}
Then load secrets with the same builder pattern as the other SDKs:
import org.cachix.secretspec.SecretSpec;
try (var resolved = SecretSpec.builder()
.withProvider("keyring://")
.withProfile("production")
.withReason("boot web app")
.load()) {
System.out.println(resolved.secret("DATABASE_URL").get());
resolved.setAsSystemProperties();
}
The package supports JDK 11 or newer on Linux, macOS, and Windows, including x64 and Arm64 systems. It includes everything needed to resolve secrets, so the application does not need a separate SecretSpec CLI or Rust toolchain.
The standalone installer and secretspec-update now work on Alpine Linux. The Node.js SDK also works directly in images such as node:alpine, without a glibc compatibility layer.
Five new providers
SecretSpec 0.20 adds five providers, bringing the total to 33.
Azure App Configuration
Use Azure App Configuration to read and manage ordinary key-values or follow references to Azure Key Vault secrets.
$ az login
$ secretspec set DATABASE_URL --provider aac://payments-production
$ secretspec run --provider aac://payments-production -- ./payments
It works with service principals, Azure CLI sessions, managed identity, workload identity, and connection strings. Provider URLs can select labels, prefixes, and tags.
Kubernetes
Kubernetes stores values in a ConfigMap or Secret using the current kubeconfig context:
$ secretspec set DATABASE_URL \
--provider k8s+secret://app-credentials@production
$ secretspec run \
--provider k8s+secret://app-credentials@production \
-- ./application
You can use convention keys or point declarations at existing .data entries. The provider supports reads, writes, deletion, and declaration discovery.
EJSON
Shopify’s EJSON lets teams keep encrypted secrets alongside application source. Secret values are encrypted with a public key and decrypted with the matching private key, while the JSON structure stays visible.
SecretSpec’s EJSON provider reads string values from those files. A ref.item selects any RFC 6901 JSON Pointer, while convention addresses use /{project}/{profile}/{key}.
Supply the EJSON private key as a SecretSpec provider credential. It can come from Google Cloud Secret Manager, the system keyring, or any other readable provider. The EJSON provider is read-only.
Fly.io
Use Fly.io to publish and delete application secrets with flyctl. Secret values stay out of process arguments, and app-scoped deploy tokens can come from provider credentials. Fly.io does not return plaintext, so keep the original value in a readable provider and use fly as the deployment destination.
Cloudflare Secrets Store
Use Cloudflare Secrets Store to publish and delete account-level secrets through Cloudflare’s API. Authenticate with a scoped API token from another provider or an existing Wrangler session. Cloudflare does not return plaintext, so keep the original value in a readable provider and use cloudflare as the deployment destination.
Other changes
Shell completions now come directly from the CLI definition:
$ source <(secretspec completions bash)
Bash, Elvish, Fish, Nushell, PowerShell, and Zsh now complete commands, profiles, scopes, secret names, providers, files, and executables. Completion does not read secret values. See secretspec completions for persistent installation instructions (#330).
0.20 also includes these CLI and automation changes:
- On Unix,
secretspec runforwardsSIGTERM,SIGINT, andSIGHUPto its child, so containers can shut down gracefully (#391). check --json,check --explain, and SDK reports now mark an unprovisioned requiredgeneratedeclaration asmissing_requiredinstead of resolved. Runsecretspec checkorsecretspec runonce to generate and store it (#394).- Human-readable
secretspec checkoutput moves to stdout, matching its JSON and explain modes. Diagnostics remain on stderr (#372). - Dotenv files, age-encrypted dotenv blobs, and
export --format dotenvnow use dotenv-ng. Dollar signs and bcrypt-style values now round-trip correctly (#73). extractsupports INI documents, selecting unsectioned keys with/keyand named-section keys with/section/key(#386).- The age provider can now delete secrets, enabling
secretspec delete,import --delete-source, and age-backed provider caches (#328). - Closing a stdout pipe is quiet on Unix, so commands such as
secretspec export | headbehave like other Unix tools (#377).
Provider-specific fixes keep 1Password batches fast when optional items are missing (#401), prevent Bitwarden convention names from colliding across projects and profiles (#390), make Infisical Universal Auth sessions more reliable (#402), and let Node.js applications exit cleanly after AWS resolution (#365).
Upgrading
$ cargo install secretspec
For most users, 0.20 is a drop-in upgrade. The new integrations, providers, and inline declarations are opt-in. Check these cases before upgrading:
- scripts that captured human-readable
secretspec checkoutput from stderr should capture stdout; - Google Cloud Secret Manager writes now use the collision-safe
secretspec2--{project}--{profile}--{key}convention. Reads fall back to the matching 0.19 name until a write moves the value, leaving the old secret in place for rollback; - Bitwarden Password Manager convention items now use
secretspec/{project}/{profile}/{key}titles.init --from bw://preserves references to legacy bare items so they can keep working or be migrated deliberately.
Rust applications using secretspec-derive can remove direct serde or secrecy dependencies if they were needed only for generated types. Code using the old raw configuration or code-generation APIs should move to Spec, SpecBuilder, Profile, and Secret.
See the full changelog for every change and fix in this release.
What’s next
Resolver and provider IPC
The resolver and provider IPC protocols are currently RFCs in PR #362. The resolver protocol would let applications request individual secrets from secretspec serve. The provider protocol would let external executables act as SecretSpec providers. Both are targeted for 0.21 and may change during review.
Nix secret-provider plumbing
Work on the Nix integration identified that SecretSpec first needs the IPC layer above, so the concrete integration is blocked until 0.21 is released. In the meantime, the underlying refactoring has been split into a generic secret-provider interface for Nix.
devenv machines
Separately, devenv’s experimental machines interface aims to handle NixOS installation, NixOS and nix-darwin deployment, and home-manager activation in one workflow. Its SecretSpec integration can supply deployment credentials without putting their values in Nix evaluation or store paths. This work follows devenv’s own release schedule and is not part of SecretSpec 0.21.
Questions or feedback? Join us on Discord.
Fencer for Open Source
In partnership with SecretSpec, Fencer has expanded its offering to open source. Public repositories now get free static analysis, dependency scanning, secret scanning, and GitHub configuration scanning, with no credit card and no expiry.
We recommend Fencer for code security scanning, and Fencer recommends SecretSpec for secrets management. Fencer finds credentials and other risks already in a repository, while SecretSpec helps keep the next secret in the right provider instead of source code or a dotenv file.
Last updated Oct 11, 2026