secretenv uses two configuration files with completely separate responsibilities.
| File | Location | Committed to git | Purpose |
|---|---|---|---|
secretenv.toml |
Repo root | Yes | What secrets this project needs |
config.toml |
~/.config/secretenv/ |
Never | Where secrets live, how to reach them |
- Only two value types:
fromanddefault. No exceptions. - No backend URIs; use
secretenv://aliases or static defaults only. - One
[secrets]block; no environment blocks. - Safe to commit. Zero infrastructure information.
# secretenv.toml
# Committed to git. Contains no secrets, no paths, no environment logic.
[secrets]
# Alias lookup, resolved via registry at runtime
STRIPE_KEY = { from = "secretenv://stripe-key" }
DATABASE_URL = { from = "secretenv://db-url" }
DATADOG_API_KEY = { from = "secretenv://datadog-api-key" }
REDIS_URL = { from = "secretenv://redis-url" }
VAULT_TOKEN = { from = "secretenv://vault-token" }
# Static default, injected directly, no backend involved
LOG_LEVEL = { default = "info" }
APP_ENV = { default = "development" }| Type | Syntax | Behavior |
|---|---|---|
| Alias lookup | { from = "secretenv://alias-name" } |
Resolves alias via active registry, fetches from named backend |
| Static default | { default = "value" } |
Injected as-is, no registry or backend involved |
| Direct backend URI | { from = "aws-ssm://..." } |
Hard error. Prohibited without exception. |
An attacker reading this file learns only what secrets the project needs, not where they live.
~/.config/secretenv/config.toml
One file per machine (XDG convention). Two top-level sections:
[registries], named registry configurations with cascading sources[backends], named backend instances with credential configuration
[registries.default]is optional; missing registry name is a hard error.- Each registry has a
sourcesarray. Sources cascade. First match wins. - Earlier sources shadow later ones by design.
# Optional default registry
[registries.default]
sources = [
"aws-ssm-platform:///secretenv/org-registry",
]
# Named registries, activated via --registry <name>
[registries.dev]
sources = [
"aws-ssm-dev:///secretenv/dev-registry", # team aliases, checked first
"aws-ssm-platform:///secretenv/org-registry", # org-wide fallback
]
[registries.int]
sources = [
"aws-ssm-int:///secretenv/int-registry",
"aws-ssm-platform:///secretenv/org-registry",
]
[registries.local]
sources = [
"local:///Users/yourname/.config/secretenv/local-registry.toml",
]Every value in sources is a backend URI using the named instance as the scheme:
aws-ssm-platform:///secretenv/org-registry
└─────────────┘ └─────────────────────┘
named instance path to registry document
(defined in within that backend
[backends.*])
A backend instance is a named configuration of a backend type; the instance name becomes the URI scheme. Multiple instances of the same type are normal:
[backends.aws-ssm-dev]
type = "aws-ssm"
aws_profile = "dev"
aws_region = "us-east-1"
[backends.aws-ssm-prod]
type = "aws-ssm"
aws_profile = "prod"
aws_region = "us-east-1"One plugin, two instances, two credential sets, no new code.
aws-ssm: AWS SSM Parameter Store
[backends.aws-ssm-dev]
type = "aws-ssm" # required
aws_profile = "dev" # optional, omit to use ambient credentials
aws_region = "us-east-1" # requiredaws-secrets: AWS Secrets Manager
[backends.aws-secrets-prod]
type = "aws-secrets"
aws_profile = "prod"
aws_region = "us-east-1"1password: 1Password
[backends.1password-work]
type = "1password"
op_account = "company.1password.com" # optional, omit for single-account setupsvault: HashiCorp Vault
[backends.vault-eng]
type = "vault"
vault_address = "https://vault.company.com" # required
vault_namespace = "engineering" # optional, Vault Enterprise onlygcp: GCP Secret Manager
[backends.gcp-prod]
type = "gcp"
gcp_project = "my-project-prod" # requiredazure: Azure Key Vault
[backends.azure-prod]
type = "azure"
azure_vault_url = "https://my-keyvault.vault.azure.net" # requiredkeeper: Keeper
[backends.keeper-work]
type = "keeper"keychain: macOS Keychain / Linux Secret Service
[backends.keychain]
type = "keychain"local: Local file (zero infrastructure)
[backends.local]
type = "local"
# no credentials required# ~/.config/secretenv/config.toml
# ── Registries ──────────────────────────────────────────────────────────────
[registries.default]
sources = [
"aws-ssm-platform:///secretenv/org-registry",
]
[registries.dev]
sources = [
"aws-ssm-dev:///secretenv/dev-registry",
"aws-ssm-platform:///secretenv/org-registry",
]
[registries.int]
sources = [
"aws-ssm-int:///secretenv/int-registry",
"aws-ssm-platform:///secretenv/org-registry",
]
# ── Backends ─────────────────────────────────────────────────────────────────
[backends.aws-ssm-platform]
type = "aws-ssm"
aws_profile = "platform"
aws_region = "us-east-2"
[backends.aws-ssm-dev]
type = "aws-ssm"
aws_profile = "dev"
aws_region = "us-east-1"
[backends.aws-ssm-int]
type = "aws-ssm"
aws_profile = "int"
aws_region = "us-east-1"
[backends.1password-work]
type = "1password"
op_account = "company.1password.com"
[backends.vault-eng]
type = "vault"
vault_address = "https://vault.company.com"
vault_namespace = "engineering"1. --registry <name-or-uri> ← explicit per-invocation
2. SECRETENV_REGISTRY=<name-or-uri> ← CI / shell-session override
3. [registries.default] in config ← machine default
4. hard error
The --registry flag and SECRETENV_REGISTRY variable accept either a name or direct URI:
- Contains
://→ treat as direct URI (single source, no cascade) - No
://→ treat as registry name, look up[registries.<n>]
| Condition | Error |
|---|---|
--registry foo but no [registries.foo] |
error: no registry named 'foo' in config |
| No registry configured anywhere | error: no registry configured. Use --registry <name-or-uri> or set SECRETENV_REGISTRY |
| Alias not found in cascade | error: alias 'stripe-key' not found in registry 'dev' (checked 2 sources) |
| Unknown backend instance in URI | error: unknown backend 'aws-ssm-dev'. Is it defined in config.toml? |
| Backend fetch fails | error: failed to fetch 'aws-ssm-dev:///myapp/dev/stripe' (alias: stripe-key): <backend error> |
| Direct backend URI in secretenv.toml | error: direct backend URIs are not allowed in secretenv.toml. Use a secretenv:// alias |