A comprehensive secrets management library for Clojure with support for multiple sources and encryption.
- Plugin Architecture: Extensible plugin system for multiple secret sources
- Priority-Based Lookup: Configure lookup order across plugins
- Deep Merging: Intelligently merge secrets from different sources
- Encryption: AES-GCM encryption with PBKDF2 key derivation
- Flexible Lookup: Support for both flat and nested secret paths
- Load from
secrets.edn(plain) andsecrets.edn.enc(encrypted) - Environment variables support (
SECRET__*prefix) - Priority-based source merging
- HashiCorp Vault integration for enterprise secret management
- Full KV v2 secrets engine support
- Vault namespace support (Enterprise)
- Read, write, and list operations
- See VAULT_PLUGIN.md for details
Add to your deps.edn:
{:deps {secrets {:local/root "/path/to/secrets"}}}(require '[secrets.core :as secrets])
;; Get a secret (searches all sources)
(secrets/get-secret :api-key)
;; => "your-api-key"
;; Get nested secret
(secrets/get-secret [:openai :api-key])
;; => "sk-..."
;; Get all loaded secrets
(secrets/all-secrets)
;; => {:api-key "..." :openai {:api-key "sk-..."}}
;; Reload secrets from all sources
(secrets/reload-secrets!)Secrets are loaded from multiple sources with the following priority (rightmost wins):
- Home plain:
~/secrets.edn - Home encrypted:
~/secrets.edn.enc - Local plain:
./secrets.edn - Local encrypted:
./secrets.edn.enc - Environment variables:
SECRET__*prefixed (highest priority)
Example environment variable:
export SECRET__OPENAI__API_KEY="sk-..."
# Maps to [:openai :api-key]The library supports environment-specific secret management via the ENV_NAME environment variable. When set, the library automatically:
- Loads environment-specific files (e.g.,
secrets.staging.edn) - Uses environment-specific Vault paths (e.g.,
pyjama/staging) - Checks environment-specific env variables (e.g.,
SECRET_STAGING__*)
# Set the environment name
export ENV_NAME=staging
# Now the library will look for:
# - Files: secrets.staging.edn, secrets.staging.edn.enc
# - Vault: pyjama/staging/* (instead of pyjama/*)
# - Env vars: SECRET_STAGING__* (in addition to SECRET__*)When ENV_NAME=staging, the priority order becomes:
~/secrets.edn(home default)~/secrets.edn.enc(home default encrypted)~/secrets.staging.edn(home environment-specific)~/secrets.staging.edn.enc(home environment-specific encrypted)./secrets.edn(local default)./secrets.edn.enc(local default encrypted)./secrets.staging.edn(local environment-specific)./secrets.staging.edn.enc(local environment-specific encrypted)SECRET__*environment variables (generic)SECRET_STAGING__*environment variables (environment-specific, highest priority)
This allows you to:
- Keep common secrets in
secrets.edn - Override with environment-specific values in
secrets.staging.edn - Further override with environment variables
When ENV_NAME=staging:
;; Without ENV_NAME:
(vault/read-secret config "secret" "pyjama/database")
;; Reads from: secret/data/pyjama/database
;; With ENV_NAME=staging:
(vault/read-secret config "secret" "pyjama/database")
;; Reads from: secret/data/staging/pyjama/database# Generic (works in all environments)
export SECRET__API_KEY="default-key"
# Environment-specific (only used when ENV_NAME=staging)
export SECRET_STAGING__API_KEY="staging-key"
# When ENV_NAME=staging, SECRET_STAGING__API_KEY takes precedence(require '[secrets.core :as secrets])
;; Set passphrase
;; export SECRETS_PASSPHRASE="your-secure-passphrase"
;; Write encrypted secrets
(secrets/write-encrypted-secrets!
"~/secrets.edn.enc"
{:api-key "secret-value"
:database {:password "db-pass"}})(require '[secrets.plugins.vault :as vault])
;; Configure Vault (or use VAULT_ADDR and VAULT_TOKEN env vars)
(def config (vault/vault-config))
;; Read secret from Vault
(vault/read-secret config "secret" "myapp/database")
;; => {:username "admin" :password "secret123"}
;; Write secret to Vault
(vault/write-secret! config "secret" "myapp/api-keys"
{:openai "sk-..." :anthropic "sk-ant-..."})
;; List secrets
(vault/list-secrets config "secret" "myapp")
;; => ["database" "api-keys"]
;; Convert Vault secrets to map
(vault/vault->secrets-map config "secret" "myapp")
;; => {:database-username "admin" :database-password "secret123" ...}See VAULT_PLUGIN.md for complete Vault plugin documentation.
{:api-key "your-api-key"
:database {:host "localhost"
:port 5432
:password "db-pass"}
:openai {:api-key "sk-..."}}Binary file encrypted with AES-GCM. Use write-encrypted-secrets! to create.
# Run all tests
clojure -M:test
# Run specific test namespace
clojure -M:test -n secrets.core-test
clojure -M:test -n secrets.plugins.vault-testSee the src/secrets/examples/ directory for examples:
vault_demo.clj: Comprehensive Vault plugin demonstrationenv_demo.clj: Environment-based secret management demonstration
Run examples:
# Vault plugin demo
clojure -M -m secrets.examples.vault-demo
# Environment-based secrets demo
clojure -M:env-demo- Never commit secrets files: Add
secrets.ednandsecrets.edn.encto.gitignore - Use encrypted files: For sensitive production secrets, use encrypted files
- Protect passphrases: Store
SECRETS_PASSPHRASEsecurely (e.g., in password manager) - Environment isolation: Use different secrets per environment
- Vault for production: Consider using the Vault plugin for production environments
(get-secret k-or-path): Retrieve a secret by key or path vector(all-secrets): Get all loaded secrets as a map(reload-secrets!): Reload all secrets from sources(write-encrypted-secrets! path m): Write encrypted secrets to file
(vault-config): Get Vault configuration from environment(read-secret config mount path): Read secret from Vault(write-secret! config mount path data): Write secret to Vault(list-secrets config mount path): List secrets at path(vault->secrets-map config mount path): Convert Vault secrets to map
# Start REPL
clojure
# Run tests
clojure -M:test
# Start Vault dev server (for Vault plugin testing)
vault server -devCopyright © 2026
- ENV_BASED_SECRETS.md - Environment-based secret management guide
- VAULT_DOCKER_SETUP.md - Vault Docker setup with persistent storage
- PLUGIN_ARCHITECTURE.md - Plugin system architecture and custom plugin guide
- VAULT_PLUGIN.md - Vault plugin documentation
- IMPLEMENTATION_SUMMARY.md - Implementation details