Declarative secrets manager
Declare secrets once.
Store them anywhere.
Stop leaking .env files. Define what your application needs in monosecret.toml, then plug in keyring, 1Password, Vault, AWS, or any of 32 providers — same code, every environment.
[project]
name = "my-app"
revision = "1.0"
[profiles.default]
DATABASE_URL = { description = "PostgreSQL", required = true }
REDIS_URL = { description = "Redis cache" }
TLS_CERT = { as_path = true }
DB_PASSWORD = { type = "password", generate = true }
[profiles.development]
# Inherits from default — override what changes
DATABASE_URL = { default = "postgresql://localhost/dev" } # 1. Initialize from existing .env
$ monosecret init --from dotenv
✓ Created monosecret.toml with 5 secrets
# 2. Pick a storage backend (one-time)
$ monosecret config init
? Default provider: keyring, dotenv, env, onepassword, lastpass, bws, vault, awssm, gcsm, protonpass, pass, age (0.2+), akv (0.2+), awsps (0.2+), bw (0.2+), dashlane (0.2+), file (0.2+), gopass (0.2+), infisical (0.2+), kdbx (0.2+), keeper (0.2+), null (0.2+), openbao (0.2+), passbolt (0.2+), scaleway (0.2+), sops (0.2+), systemd-credential (0.2+), aac (0.20+), fly (0.20+), cloudflare (0.20+), kubernetes (0.20+)
✓ Saved to ~/.config/monosecret/config.toml
# 3. Run your app with secrets injected
$ monosecret run --profile production -- npm start
✓ Loaded DATABASE_URL from keyring
✓ Loaded REDIS_URL from keyring
✓ Generated DB_PASSWORD (32 chars)
✓ Wrote TLS_CERT to /tmp/monosecret-tls-cert
› npm start One declaration · Any provider
Three commands. Done.
From a leaky .env to a portable, declarative secrets setup the whole team can use.
Declare
Generate monosecret.toml from your existing .env, or write it by hand. Names and descriptions only — never values.
$ monosecret init --from dotenv Configure
Pick any supported provider, from a local keyring or encrypted file to a team password manager or cloud secret store.
$ monosecret config init
? Default provider: keyring, dotenv, env, onepassword, lastpass, bws, vault, awssm, gcsm, protonpass, pass, age (0.2+), akv (0.2+), awsps (0.2+), bw (0.2+), dashlane (0.2+), file (0.2+), gopass (0.2+), infisical (0.2+), kdbx (0.2+), keeper (0.2+), null (0.2+), openbao (0.2+), passbolt (0.2+), scaleway (0.2+), sops (0.2+), systemd-credential (0.2+), aac (0.20+), fly (0.20+), cloudflare (0.20+), kubernetes (0.20+) Run
Secrets are loaded at runtime and injected as environment variables. Same command, every environment.
$ monosecret run -- npm start One file. Every secret. Every environment.
Built around the three questions every project answers: what secrets, how requirements differ per environment, and where values live.
Providers
Local keyrings and files, password managers, cloud secret stores, Vault-compatible servers, encrypted files, and ephemeral values — one pluggable interface.
Profiles
Different requirements, defaults, and validation per environment. Optional locally, required in production — without touching app code.
[profiles.production]
DATABASE_URL = { required = true } Auto-generation
Passwords, tokens, and keys created automatically when missing — no manual setup, no copy paste.
DB_PASSWORD = { type = "password", generate = true } Per-secret fallback chains
Each secret can specify its own ordered provider list. Tries Vault first, falls back to keyring, then env — until the value is found.
API_KEY = { providers = ["vault", "keyring", "env"] } Type-safe Rust SDK
Proc macro reads monosecret.toml at compile time and generates strongly-typed structs. Typos fail to compile.
monosecret_derive::declare_secrets!("monosecret.toml");
let s = Monosecret::builder().load()?;
println!("{}", s.secrets.database_url); Config inheritance
Extend shared configs across services with extends. Define once, reuse everywhere with proper precedence.
File-path secrets
Secrets with as_path = true get written to temp files — perfect for TLS certs and service account keys.
One-line migration
Move all secrets between providers without changing application code. monosecret import does the rest.
Declare a secret. Swap the source.
Your monosecret.toml never changes. The same code reads from Keychain, 1Password, Vault, AWS, or any of 32 providers.
[project]
name = "my-app"
[profiles.default]
DATABASE_URL = { required = true }
STRIPE_SECRET_KEY = { required = true } DATABASE_URL $ monosecret run — injected as env var at runtime DATABASE_URL from env One config. Every environment.
Profiles let the same secret be optional in development, required in production — without changing application code. How profiles work →
Fallback chains, per secret.
Specify an ordered list of providers for each secret. Monosecret walks the chain until it finds the value — perfect for shared team vaults with personal overrides.
[providers]
team_vault = "onepassword://Shared"
keyring = "keyring://"
env = "env://"
[profiles.production]
API_KEY = {
description = "Third-party API key",
providers = ["team_vault", "keyring", "env"]
} API_KEY in team_vault (1Password) Same code. Same secret. Different source per machine.
Know who read a secret — and why.
Every access is appended to a local, append-only log — values never written, URI credentials redacted. And when an AI agent is driving, Monosecret makes it state a reason first. How auditing works →
# An AI agent runs your app — no reason given
$ monosecret run -- ./deploy.sh
Error: accessing secrets requires a reason.
Provide one with --reason "<why...>"
# State why — required for agents by default
$ monosecret run --reason "Deploy web frontend" \
-- ./deploy.sh
✓ Loaded DATABASE_URL from keyring
› ./deploy.sh # Review who accessed what, why, and the outcome
$ monosecret audit -n 1
2026-06-04T17:04Z run started ./deploy.sh
DATABASE_URL (my-app/production via keyring://)
reason: Deploy web frontend [claude-code]
The default require_reason = "agents" policy is checked into monosecret.toml, so every clone, CI runner, and agent is held to it — humans running interactively are unaffected. Configure the policy →
Compile-time secrets in Rust.
The proc macro reads monosecret.toml at compile time and generates strongly-typed structs. Misspelling a secret name fails the build, not your deploy. SDK reference →
// Generate typed structs from monosecret.toml
monosecret_derive::declare_secrets!("monosecret.toml");
fn main() -> Result<(), Box<dyn std::error::Error>> {
let secrets = Secrets::builder()
.with_provider("keyring")
.with_profile(Profile::Production)
.load()?;
// DATABASE_URL → database_url (compile-time checked)
println!("Database: {}", secrets.secrets.database_url);
// Optional secrets are Option<String>
if let Some(redis) = &secrets.secrets.redis_url {
println!("Redis: {}", redis);
}
secrets.secrets.set_as_env_vars();
Ok(())
} Switch backends with one command.
Outgrowing dotenv? Standardizing on Vault? monosecret import moves every secret without touching a single line of application code.
DATABASE_URL=postgres://…
STRIPE_SECRET_KEY=sk_live_…
REDIS_URL=redis://… $ monosecret import dotenv://.env.production
✓ Imported 5 secrets to keyring://
✓ Application code unchanged Native devenv & Nix integration.
Enable Monosecret in your devenv.yaml and every secret declared in monosecret.toml is loaded into config.monosecret.secrets — wire them into env vars, services, or processes from devenv.nix.
monosecret:
enable: true
provider: keyring # keyring, dotenv, env, 1password, …
profile: default { config, ... }:
{
# Wire any declared secret into the shell env
env.DATABASE_URL = config.monosecret.secrets.DATABASE_URL;
}
Switch providers per machine without touching devenv.nix: devenv --monosecret-provider dotenv --monosecret-profile dev shell.
Provider trait and ship a new backend. Stop leaking secrets.
Declare what your application needs. Store the values anywhere. Onboard new developers in one command.
Fencer for Open Source
In partnership with SecretSpec, Fencer has expanded its security offering to open-source projects. Public repositories 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.