Skip to content

CLI Commands Reference

The SecretSpec CLI provides commands for managing secrets across different providers and profiles.

These options are available on every command:

OptionDescription
-f, --file <FILE>Path to secretspec.toml (default: auto-detect). Env: SECRETSPEC_FILE
--reason <REASON>Reason for accessing secrets, recorded by providers that support audit logging (e.g. Proton Pass agent sessions). Takes precedence over PROTON_PASS_AGENT_REASON. Env: SECRETSPEC_REASON
Terminal window
$ secretspec run --reason "Deploying web frontend" -- ./deploy.sh

Initialize a new secretspec.toml configuration file from an existing .env file.

Terminal window
secretspec init [OPTIONS]

Options:

  • --from <PATH> - Path to .env file to import from (default: .env)

Example:

Terminal window
$ secretspec init --from .env.example
Created secretspec.toml with 5 secrets

Initialize user-global configuration. The explicit global namespace is available in SecretSpec 0.17+; without options, the command prompts for the provider and profile.

Terminal window
secretspec config global init [--provider <PROVIDER>] [--profile <PROFILE>] # 0.17+

SecretSpec 0.17+ accepts --provider and --profile so installations can save both defaults without interaction. Each omitted option still prompts; use --profile none to clear the saved default profile. The corresponding SECRETSPEC_PROVIDER and SECRETSPEC_PROFILE environment variables are also accepted. Project requirements remain in secretspec.toml; the namespace makes it clear that this command writes user-wide defaults. The legacy secretspec config init spelling remains supported as a hidden alias.

Example:

Terminal window
$ secretspec config global init # 0.17+
? Select your preferred provider backend:
> keyring: System keychain
? Select your default profile:
> development
Configuration saved to ~/.config/secretspec/config.toml
Terminal window
# SecretSpec 0.17+: save both defaults without prompting
$ secretspec config global init --provider env --profile default
Configuration saved to ~/.config/secretspec/config.toml

Display current user-global configuration. The explicit namespace is available in SecretSpec 0.17+; secretspec config show remains a hidden alias.

Terminal window
secretspec config global show # 0.17+

Example:

Terminal window
$ secretspec config global show # 0.17+
Provider: keyring
Profile: development

Add a provider alias to your user-level configuration (~/.config/secretspec/config.toml).

To share aliases with your team, declare them in a top-level [providers] table in secretspec.toml instead — they take precedence over user-level aliases on name conflict.

Terminal window
secretspec config global provider add <ALIAS> <URI> [--credential NAME=PROVIDER]... # 0.17+

Arguments:

  • <ALIAS> - Short name for the provider (e.g., prod_vault, shared)
  • <URI> - Provider URI (e.g., onepassword://Production, env://)

Options:

  • --credential <NAME=PROVIDER> - Declare a provider credential and its source. NAME is semantic and provider-specific, such as access_token or role_id. Repeatable. Only the bare-string source form is expressible on the command line; add a ref by editing the config.

Example:

Terminal window
$ secretspec config global provider add prod_vault "onepassword://Production" # 0.17+
Provider alias 'prod_vault' added: 'onepassword://Production'
$ secretspec config global provider add bws "bws://project-uuid" --credential access_token=keyring # 0.17+
Provider alias 'bws' added: 'bws://project-uuid'
credentials: access_token=keyring
run 'secretspec config provider login bws' to store the credentials

List all configured user-level provider aliases. Project-level aliases declared in secretspec.toml are not shown by this command.

Terminal window
secretspec config global provider list # 0.17+

Example:

Terminal window
$ secretspec config global provider list # 0.17+
prod_vault onepassword://Production
shared onepassword://Shared
env env://

Remove a provider alias from your user-level configuration. To remove a project-level alias, edit the [providers] table in secretspec.toml directly.

Terminal window
secretspec config global provider remove <ALIAS> # 0.17+

Arguments:

  • <ALIAS> - Name of the alias to remove

Example:

Terminal window
$ secretspec config global provider remove prod_vault # 0.17+
Provider alias 'prod_vault' removed

Store the credentials a provider alias declares. Prompts (hidden input) for each credential and writes it to its source provider at the exact location resolution reads it back from. Runs in a project, like set and check.

Terminal window
secretspec config provider login <ALIAS>

Arguments:

  • <ALIAS> - Name of the alias whose credentials to store

Example:

Terminal window
$ secretspec config provider login bws
Enter access_token for provider 'bws' (source: keyring): ****
stored access_token in keyring at myproject/default/access_token
Run 'secretspec check --provider bws' to verify authentication.

A read-only source provider is rejected. An alias that declares no credentials reports that there is nothing to store.

Check if all required secrets are available, with interactive prompting for missing secrets.

Terminal window
secretspec check [OPTIONS]

Options:

  • -p, --provider <PROVIDER> - Provider backend to use
  • -P, --profile <PROFILE> - Profile to use
  • -S, --scope <SCOPE> - Resolve only a [scopes] subset of the profile (SecretSpec 0.17+)
  • -n, --no-prompt - Don’t prompt for missing secrets (exit with error if any are missing)
  • --json - Print a value-free resolution report as JSON instead of prompting
  • --explain - Print a value-free, human-readable resolution trace instead of prompting

Example:

Terminal window
$ secretspec check --profile production
DATABASE_URL - Database connection string
API_KEY - API key for external service (required)
Enter value for API_KEY (profile: production): ****
Secret 'API_KEY' saved to keyring (profile: production)

--json and --explain report how every declared secret resolved for the active profile without prompting and without ever printing a secret value. Both exit non-zero when a required secret is missing, so they work as a CI gate.

--explain prints a human-readable trace:

Terminal window
$ secretspec check --profile development --explain
profile: development
provider: keyring://
DATABASE_URL ok source keyring://
DEV_SESSION_SECRET ok default value
JWT_SECRET ok generated
SENTRY_DSN missing optional
STRIPE_KEY MISSING required

--json emits a versioned, machine-readable object for tooling and CI. Each entry reports the status (resolved, missing_required, missing_optional), whether the value came from a provider (source_provider, credential-free), a generator (generated), or a committed default (default_applied), and whether it is exposed as_path. No secret values appear. The canonical JSON Schema is committed at schema/resolution-report.schema.json.

Terminal window
$ secretspec check --profile production --json
{
"schema_version": 1,
"provider": "keyring://",
"profile": "production",
"secrets": [
{ "name": "DATABASE_URL", "status": "resolved", "required": true, "source_provider": "keyring://", "default_applied": false, "generated": false, "as_path": false },
{ "name": "STRIPE_KEY", "status": "missing_required", "required": true, "default_applied": false, "generated": false, "as_path": false }
]
}

Get a secret value.

Terminal window
secretspec get [OPTIONS] <NAME>

Options:

  • -p, --provider <PROVIDER> - Provider backend to use
  • -P, --profile <PROFILE> - Profile to use

Example:

Terminal window
$ secretspec get DATABASE_URL --profile production
postgresql://prod.example.com/mydb

For a composed secret, get resolves its transitive dependencies and prints the derived value. Available since SecretSpec 0.16.

Emit a single-root JSON Schema for the manifest’s typed shape: by default the union SecretSpec (safe for any profile); with --profile, that profile’s exact fields. Value-free: reads only the manifest, never a provider.

Terminal window
secretspec schema [OPTIONS]

Options:

  • -P, --profile <PROFILE> - Emit the schema for this profile’s fields instead of the union
  • -o, --output <FILE> - Write to this file instead of stdout

Rather than ship a typed-accessor generator per language, feed this schema to quicktype, which generates an idiomatic type and deserializer for any language. Name the type with --top-level. At runtime, hand the generated deserializer the flat {SECRET_NAME: value} map from the SDK’s fields() helper:

Terminal window
$ secretspec schema | quicktype -s schema --top-level SecretSpec --lang python -o secrets_gen.py
from secretspec import SecretSpec
from secrets_gen import SecretSpec as Secrets # quicktype-generated, typed
resolved = SecretSpec.builder().with_reason("boot").load()
s = Secrets.from_dict(resolved.fields())
print(s.database_url) # typed str

The same pattern works in every SDK: Go UnmarshalSecretSpec(resolved.FieldsJSON()), TypeScript Convert.toSecretSpec(resolved.fieldsJson()), Ruby SecretSpec.from_dynamic!(resolved.fields).

Set a secret value.

Terminal window
secretspec set [OPTIONS] <NAME> [VALUE]

Options:

  • -p, --provider <PROVIDER> - Provider backend to use
  • -P, --profile <PROFILE> - Profile to use

Example:

Terminal window
$ secretspec set API_KEY sk-1234567890
Secret 'API_KEY' saved to keyring (profile: development)

set rejects composed secrets because their values are derived and read-only. Available since SecretSpec 0.16.

Run a command with secrets injected as environment variables.

Terminal window
secretspec run [OPTIONS] -- <COMMAND>

Options:

  • -p, --provider <PROVIDER> - Provider backend to use
  • -P, --profile <PROFILE> - Profile to use
  • -S, --scope <SCOPE> - Inject only a [scopes] subset of the profile (SecretSpec 0.17+)

Examples:

Terminal window
# Run npm with secrets available as environment variables
$ secretspec run --profile production -- npm run deploy
# Verify secrets are injected
$ secretspec run -- env | grep DATABASE_URL
DATABASE_URL=postgresql://localhost/mydb
# Inject only the `api` scope's secrets (SecretSpec 0.17+); secrets the
# scope excludes are removed from the child even if the parent exported them
$ secretspec run --scope api -- ./api-server

The --provider override applies to every secret, including those with a ref field: refs are redirected to the overriding provider just like convention secrets. This makes it easy to point refs at fixtures during tests without editing the manifest:

Terminal window
# Resolve every secret, refs included, from a fixtures file
$ secretspec run --provider dotenv:.env.fixtures -- cargo test

Resolve every secret for the active profile and write it to stdout in a chosen format, without running a command. Unlike run, it never prompts and exits non-zero when a required secret is missing, so CI can gate on it.

Terminal window
secretspec export [OPTIONS]

Options are -p, --provider <PROVIDER>, -P, --profile <PROFILE>, -S, --scope <SCOPE> (a [scopes] subset of the profile, SecretSpec 0.17+), and --format <FORMAT> (default shell).

Unlike run --scope, export --scope only emits the scoped subset; it unsets nothing, because no output format can express an unset. A shell that already holds a wider set keeps those values after a scoped export, so use run --scope when the point is to narrow an existing environment.

FormatOutput
shellexport KEY='value' lines, ready for eval "$(secretspec export)"
dotenvKEY="value" lines in dotenv syntax (double-quoted, with \, ", $, and newline escaped)
jsona single compact JSON object mapping each secret name to its value
ghaappends KEY=value to the file named by $GITHUB_ENV and prints an ::add-mask:: command per value to stdout, so later workflow steps and third-party actions see the secrets
Terminal window
# Load secrets into the current shell
$ eval "$(secretspec export --profile production)"
# Emit JSON for another tool to consume
$ secretspec export --profile production --format json
{"DATABASE_URL":"postgresql://prod.example.com/mydb"}

The gha format targets a secretspec export --format gha step in a GitHub or Forgejo Actions job: it masks the values in the runner log and persists them to the job environment for the steps that follow.

Import secrets from one provider to another.

Terminal window
secretspec import <FROM_PROVIDER>

The destination provider and profile are determined from your configuration. Secrets that already exist in the destination provider will not be overwritten.

Arguments:

  • <FROM_PROVIDER> - Provider to import from (e.g., env, dotenv:/path/to/.env)

Example:

Terminal window
# Import from environment variables to your default provider
$ secretspec import env
Importing secrets from env to keyring (profile: development)...
DATABASE_URL - Database connection string
API_KEY - API key for external service (already exists in target)
REDIS_URL - Redis connection URL (not found in source)
Summary: 1 imported, 1 already exists, 1 not found in source
# Import from a specific .env file
$ secretspec import dotenv:/home/user/old-project/.env

Use Cases:

  • Migrate from .env files to a secure provider like keyring or OnePassword
  • Copy secrets between different profiles or projects
  • Import existing environment variables into SecretSpec management

import skips composed secrets because they have no stored value to copy; their component secrets are imported normally. Available since SecretSpec 0.16.

Delete cached provider values for one secret, or for every cached secret in the active profile. Authoritative fallback providers are not modified.

Terminal window
secretspec cache clear [NAME] [--profile <PROFILE>]

Arguments and options:

  • [NAME] - Cached secret to clear. Omit it to clear all cached secrets in the profile.
  • -P, --profile <PROFILE> - Profile whose logical cache entries are cleared.

The reported count is the number of entries that were actually removed, so a profile with nothing cached reports Cleared 0 cache entries. --provider and SECRETSPEC_PROVIDER are ignored: clearing always addresses the cache of the route the manifest declares. When one cache store cannot be cleared, the remaining secrets are still cleared and the command then reports what failed.

Terminal window
# Force the next API_KEY read through its authoritative fallback route
$ secretspec cache clear API_KEY
Cleared 1 cache entry
# Clear every cached secret in production
$ secretspec cache clear --profile production
Cleared 4 cache entries

See Provider caching for configuration and resolution behavior.

Show the local audit log of secret access.

Terminal window
secretspec audit [--project <NAME>] [--action <ACTION>] [-n <N>] [--json]

Options:

  • --project <NAME> - Only show entries for this project
  • --action <ACTION> - Only show entries for this action (get, set, check, run, import, export, or cache_clear and cache_refresh in 0.17+)
  • -n, --tail <N> - Show only the last N entries
  • --json - Output raw JSON Lines instead of the formatted summary

The log location is read from your user-global config ([audit] in ~/.config/secretspec/config.toml), defaulting to the per-user state directory.

Example:

Terminal window
$ secretspec audit --action run -n 5
2026-06-04T18:06:29Z run found ./deploy.sh API_KEY,DATABASE_URL (my-app/production) reason: deploy [claude-code]
# Pipe raw entries to jq
$ secretspec audit --json | jq 'select(.outcome == "missing")'
VariableDescription
SECRETSPEC_PROFILEDefault profile to use
SECRETSPEC_PROVIDERDefault provider to use
SECRETSPEC_FILEPath to secretspec.toml (same as --file)
SECRETSPEC_REASONReason for accessing secrets (same as --reason)
Terminal window
# Initialize from existing .env
$ secretspec init --from .env
# Set up user-global defaults (0.17+)
$ secretspec config global init
# Import existing secrets (optional)
$ secretspec import env # or: secretspec import dotenv:.env.old
# Check and set missing secrets
$ secretspec check
# Run your application
$ secretspec run -- npm start