crumb is a command line tool designed to securely store, manage, and export API keys and secrets for developers. It uses an encrypted plain text file as the backend, leveraging the age encryption library with SSH public/private key pairs for encryption and decryption.
crumb is inspired by tools such as:
but, designed for the non-enterprise developer without access to a cloud Secrets Manager, raft storage, etc. Crumb was born out of the need to be able to switch secrets between different projects, without leaving project secrets unencrypted on disk.
- SSH Key Encryption/Decryption: Securely encrypt your password strorage file using SSH Keys
- Bulk Export: Explorts multiple secrets from a entire path like
/app/prod/ - Multi-Profile Support: Manage separate secret stores for work, personal, or different projects
- .env Import: Import multiple secrets from
.envfiles
- Download and add binary to $PATH from https://github.com/crhuber/crumb/releases
OR
- Use kelp
kelp add crhuber/crumb --installThe setup command initializes the secure storage backend for a specific profile.
crumb setup [--profile <profile-name>]This command:
- Creates a directory at
~/.config/crumb/if it doesn't exist - Creates or updates
~/.config/crumb/config.yamlwith the profile configuration - Prompts for your SSH public key path (e.g.,
~/.ssh/id_ed25519.pub) - Prompts for your SSH private key path (e.g.,
~/.ssh/id_ed25519) - Creates an empty encrypted secrets file at the specified storage location
Before running setup, you need to have SSH keys generated. If you don't have them, create them with:
# For Ed25519 keys (recommended)
ssh-keygen -t ed25519 -C "[email protected]"
# For RSA keys
ssh-keygen -t rsa -b 4096 -C "[email protected]"Default profile setup:
$ crumb setup
Enter path to SSH public key (e.g., ~/.ssh/id_ed25519.pub): ~/.ssh/id_ed25519.pub
Enter path to SSH private key (e.g., ~/.ssh/id_ed25519): ~/.ssh/id_ed25519
Setup completed successfully for profile 'default'!
Config file: /Users/username/.config/crumb/config.yaml
Storage file: /Users/username/.config/crumb/secretsSetup with non default profile
$ crumb --profile work setupThe set command adds or updates a secret key-value pair. The value is entered securely on a new line and is not echoed to the terminal or stored in shell history.
crumb set <key-path>This command:
- Validates the key path (must start with
/, no spaces or special characters) - Decrypts the secrets file using the private key
- Checks if the key already exists and prompts for confirmation if it does
- Adds or updates the key-value pair
- Re-encrypts and saves the secrets file
# Add a new secret
$ crumb set /prod/api_key
Enter secret value: [secret not shown]
Successfully set key: /prod/api_key
# Update an existing secret (with confirmation)
$ crumb set /prod/api_key
Key '/prod/api_key' already exists.
key already exists. Overwrite? (y/n): y
Enter secret value: [secret not shown]
Successfully set key: /prod/api_keyThe ls command lists all stored secret keys, optionally filtered by path.
crumb ls [path]This command:
- Decrypts the secrets file using the private key
- Displays all secret keys (not values) in sorted order
# List all secrets
$ crumb ls
/any/other/mykey
/any/path/mykey
# Filter by path prefix
$ crumb ls /prod
/prod/api_key
/prod/auth-svc/secretThe get command retrieves a secret by its key path.
crumb get <key-path> [--show] [--export] [--shell=bash|fish]This command:
- Validates the key path (must start with
/, no spaces or special characters) - Decrypts the secrets file using the private key
- By default, displays
****to mask the value - Supports
--showflag to display the actual secret value - Supports
--exportflag to output in shell-compatible format for sourcing - Supports
--shellflag to select output format when using--export(bash or fish, defaulting to bash) - When
--exportis used,--showis ignored and the secret value is always displayed - Requires a full key path (partial paths are not supported)
# Get a secret (masked value)
$ crumb get /prod/api_key
****
# Get a secret with actual value
$ crumb get /prod/api_key --show
secret123
# Export a secret for bash sourcing
$ crumb get /prod/api_key --export
export API_KEY=secret123
# Export a secret for fish shell
$ crumb get /prod/api_key --export --shell fish
set -x API_KEY secret123
# Source the export directly in bash
$ eval "$(crumb get /prod/api_key --export)"
$ echo $API_KEY
secret123When using the --export flag, the key path is automatically converted to a valid environment variable name:
- Leading slash (
/) is removed - Remaining slashes (
/) are converted to underscores (_) - Hyphens (
-) are converted to underscores (_) - The result is converted to uppercase
Examples:
/api/key→API_KEY/prod/my-service/auth-token→AUTH_TOKEN
The --export flag makes it easy to integrate secrets into shell scripts and workflows:
Bash:
# Source a single secret
eval "$(crumb get /api/key --export)"
echo "Key: $KEY"
# Source multiple secrets in a script
#!/bin/bash
eval "$(crumb get /prod/ --export)"Fish:
# Source a single secret
eval (crumb get /api/key --export --shell fish)
echo "Key: $KEY"
# Source multiple secrets
eval (crumb get /prod/ --export --shell fish)The init command creates a YAML configuration file in the current project directory.
crumb initThis command:
- Creates a
.crumb.yamlfile in the current directory - Uses a default structure with empty configuration
- Prompts for confirmation if the file already exists
- Validates the YAML structure before writing
# Create a new .crumb.yaml file
$ crumb init
Successfully created .crumb.yaml
#### Default Configuration Structure
The created `.crumb.yaml` file contains:
```yaml
version: "1.0"
environments:
default:
path: ""
remap: {}
env: {}This structure allows you to configure multiple environments, each with:
path: A path to sync secrets from (e.g.,/prod/billing-svc)remap: Key remapping for environment variablesenv: Individual environment variable configurations
You can add additional environments for different deployment contexts:
version: "1.0"
environments:
default:
path: "/myapp/dev"
remap: {}
env:
FOO: "bar"
production:
path: "/myapp/prod"
remap: {}
env:
FOO: "baz"You can change what finally gets exported to shell by using the remap section within an environment:
version: "1.0"
environments:
default:
path: "/some/path"
remap:
"FROM": "TO"
env: {}For example:
version: "1.0"
environments:
default:
path: "/some/path"
remap:
"SOME_SECRET_KEY": "MY_KEY"
env: {}will result in SOME_SECRET_KEY being exported as MY_KEY
export MY_KEY=******The delete command deletes a secret key-value pair from the encrypted file.
crumb delete <key-path># Delete a secret (with confirmation)
$ crumb delete /prod/billing-svc/vars/mg
Type the key path to confirm deletion: /prod/billing-svc/vars/mg
Successfully deleted key: /prod/billing-svc/vars/mgThe move (or mv) command renames a secret key to a new path, preserving its value. This is useful for reorganizing or refactoring your secret key structure without losing data.
crumb move <old-key-path> <new-key-path>
# or using the alias
crumb mv <old-key-path> <new-key-path>The import command allows you to import multiple secrets from a .env file into your encrypted storage. This is particularly useful when migrating from .env files to Crumb or when setting up a new project with existing environment variables.
crumb import --file <path-to-env-file> --path <destination-path>This command:
- Parses a
.envfile to extract environment variables and their values - Imports all variables as individual secrets under the specified path
- Re-encrypts and saves the secrets file
The import command supports standard .env file formats:
# Comments are ignored
API_KEY=secret123
DATABASE_URL="postgresql://localhost:5432/mydb"
DEBUG=true
EMPTY_VAR=
QUOTED_VALUE='single-quoted-value'
COMPLEX_URL=https://api.example.com?token=abc123&refresh=def456Basic import:
# Create a .env file
$ cat > .env << EOF
API_KEY=secret123
DATABASE_URL="postgresql://localhost:5432/mydb"
REDIS_URL=redis://localhost:6379
DEBUG=true
EOF
# Import to /dev/myapp path
$ crumb import --file .env --path /dev/myapp
Found 4 environment variables in .env
New keys to import: 4
Successfully imported 4 secrets from .env to /dev/myapp
# Verify the imported secrets
$ crumb ls /dev/myapp
/dev/myapp/API_KEY
/dev/myapp/DATABASE_URL
/dev/myapp/DEBUG
/dev/myapp/REDIS_URLUsing with different profiles:
# Import to work profile
$ crumb --profile work import --file work.env --path /work/secretsUsing with different storage location:
# Import to custom storage location
$ crumb --storage ~/project-secrets import --file project.env --path /project/configThe storage command provides subcommands to manage storage file paths for profiles.
Set the storage file path for the current profile:
crumb storage set <path> [--profile <profile-name>]Example:
# Set storage path for work profile
$ crumb --profile work storage set ~/.config/crumb/work-secrets
Storage path set to: /Users/username/.config/crumb/work-secrets (profile: work)
# Set storage path for default profile
$ crumb storage set ~/personal-secrets
Storage path set to: /Users/username/personal-secrets (profile: default)Show the current storage file path for the current profile:
crumb storage get [--profile <profile-name>]Example:
# Check storage path for work profile
$ crumb --profile work storage get
Storage: /Users/username/.config/crumb/work-secrets (profile: work)
# Check storage path for default profile
$ crumb storage get
Storage: /Users/username/.config/crumb/secrets (profile: default)
# Override storage path for one command
crumb --storage ~/backup-secrets lsClear the storage file path for the current profile (reverts to default):
crumb storage clear [--profile <profile-name>]Example:
# Clear custom storage path for work profile
$ crumb --profile work storage clear
Storage path cleared for profile: work (using default)
# Override storage path temporarily
export CRUMB_STORAGE=~/temp-secrets
crumb set /temp/key "temporary value"You can maintain separate secret stores for different contexts (work, personal, projects):
# Set up different profiles
crumb --profile work setup
# Add secrets to different profiles
crumb --profile work set /api/key
# Enter "work-secret" when prompted
# List secrets by profile
crumb --profile work ls
# Set default profile via environment variable
export CRUMB_PROFILE=work
crumb ls # Lists work profile secretsThe export command exports secrets as shell-compatible environment variable assignments. It supports two modes:
- Config-based export: Uses a
.crumb.yamlconfiguration file (traditional mode) - Direct path export: Exports all secrets from a specific path without requiring a config file (new!)
# Config-based export
crumb export [-f config-file] [--env environment] [--shell=bash|fish] [--profile <profile-name>]
# Direct path export
crumb export --path <secret-path> [--shell=bash|fish] [--profile <profile-name>]First, create a .crumb.yaml configuration file:
version: "1.0"
environments:
default:
path: "/myapp/dev"
remap:
DEFAULT_SECRET: "SECRET"
env:
MESSAGE: "Hello default"
staging:
path: "/myapp/staging"
remap:
STAGING_SECRET: "SECRET"
env:
MESSAGE: "Hello staging"Then export the secrets:
# Export default environment for bash (default)
$ crumb export
# Exported from /myapp/dev (environment: default)
export API_SECRET=secret123
export MESSAGE=Hello default
export SECRET=somesecret
# Export staging environment
$ crumb export --env staging
# Export for fish shell
$ crumb export --shell fish
# Exported from /prod/billing-svc (environment: default)
set -x API_SECRET secret123
set -x DATABASE_URL postgres://user:pass@localhost/db
set -x MG_KEY mgsecret
set -x STRIPE_KEY stripesecret
# Use a custom config file
$ crumb export -f my-project.yaml
# Exported from /prod/my-project
export MY_SECRET=value123
# Use custom config file
$ crumb export --file my-project.yaml --shell fish
# Exported from /prod/my-project
set -x MY_SECRET value123
# Export from work profile
$ crumb export --profile work
# Exported from /prod/billing-svc
export WORK_API_KEY=work-secret
export WORK_DB_URL=postgres://work-db
# Use environment variable for profile
$ CRUMB_PROFILE=work crumb export --shell=fish
# Exported from /prod/billing-svc
set -x WORK_API_KEY work-secret
set -x WORK_DB_URL postgres://work-db
# Source the output directly
$ source <(crumb export)
$ echo $MG_KEY
mgsecret
#### Direct Path Export Examples
The `--path` flag allows you to export secrets directly without a `.crumb.yaml` file:
```bash
# Export all secrets from /api path
$ crumb export --path /api
# Exported from /api
export CLIENT=foo
export CLIENT_SECRET=bar
# Export with fish shell format
$ crumb export --path /api --shell fish
# Exported from /api
set -x CLIENT foo
set -x CLIENT_SECRET bar
# Use with different profile
$ crumb export --path /prod/api --profile production
# Exported from /prod/api
export API_KEY=secret123
export SERVICE_TOKEN=token456
# Source directly into shell
$ eval "$(crumb export --path /api)"Path to Variable Name Conversion:
- Only the final segment (actual secret name) is used, intermediate path segments are ignored
- Hyphens in the secret name are converted to underscores, and the result is uppercase mgsecret
~/.config/crumb/config.yaml - Stores profile configurations with SSH key paths and storage locations.
Multi-profile structure:
profiles:
default:
public_key_path: ~/.ssh/id_ed25519.pub
private_key_path: ~/.ssh/id_ed25519
storage: ~/.config/crumb/secrets
work:
public_key_path: ~/.ssh/work.pub
private_key_path: ~/.ssh/work
storage: ~/.config/crumb/work-secrets
personal:
public_key_path: ~/.ssh/personal.pub
private_key_path: ~/.ssh/personal
storage: ~/personal-secretsEach profile has its own encrypted storage file:
- Default profile:
~/.config/crumb/secrets(unless customized) - Named profiles: Configurable per profile (e.g.,
~/.config/crumb/work-secrets)
This project uses Task for build automation. Common tasks:
task test- Run all teststask test-coverage- Run tests with coverage reporttask bench- Run benchmark teststask build- Build the applicationtask ci- Run CI pipelinetask clean- Clean build artifactstask --list- Show all available tasks
If you find bugs, please open an issue first.