Introduction
What it is
cred is a command-line tool that stores encrypted secrets locally and safely pushes them to target platforms on demand.
⚠️ Status: Early Preview (v0.5.0)
cred is currently in active development. The on-disk format, CLI surface, and security model may change between minor versions. Do not rely on it as your sole secrets backup yet.
What it is not
- A hosted secrets manager
- A multi-user access control system
- A replacement for HashiCorp Vault or AWS Secrets Manager
- A bidirectional secrets sync tool
- A runtime secret injector for applications
Who is this for
- Open-source maintainers
- Small teams
- Solo developers
- People who don’t need enterprise infrastructure yet
Why cred exists
Managing secrets across projects, targets, and sources is a mess and a chore.
cred solves this by giving you:
1. A Matrix Vault per Project
Your secrets live inside .cred/vault.enc as an encrypted store with per-secret metadata (format, timestamps, description, source).
2. Sources and Targets
cred distinguishes between sources (where credentials come from) and targets (where secrets are pushed to):
- Sources: Platforms that can programmatically generate credentials (e.g., Resend API keys)
- Targets: Platforms where you push secrets for deployment (e.g., GitHub Actions secrets)
3. A global configuration store
Metadata and preferences live in ~/.config/cred/global.toml, while source and target tokens are stored securely in the OS credential store (keyring). Nothing sensitive is written to the TOML.
4. Target-specific secret pushing
You manage secrets locally, but cred can upload them to specified targets.
Supported sources:
- Resend (API key generation)
Supported targets:
- GitHub (Actions secrets)
Installation
Homebrew (macOS)
brew tap edneedham/cred
brew install edneedham/cred/cred
Quick install (shell)
curl -fsSL https://raw.githubusercontent.com/edneedham/cred/main/scripts/install.sh | sh -s
Install with Cargo:
cargo install cred
Pre-built binaries
Download the latest release for your platform from GitHub Releases.
Available binary targets:
cred-vX.Y.Z-aarch64-apple-darwin- macOS Apple Siliconcred-vX.Y.Z-x86_64-apple-darwin- macOS Intelcred-vX.Y.Z-x86_64-unknown-linux-gnu- Linux x86_64cred-vX.Y.Z-x86_64-pc-windows-msvc.exe- Windows
Make the binary executable and move it to your PATH:
chmod +x cred-*
sudo mv cred-* /usr/local/bin/cred
Check installation:
cred --version
Getting Started
Get from zero to your first secret push in under 5 minutes.
1. Initialize your project
cred init
This creates .cred/vault.enc in your project.
2. Add a target
cred target set github
You’ll be prompted for a fine-grained PAT with Actions secrets permission.
3. Store a secret
cred secret set DATABASE_URL "postgres://user:pass@localhost/db"
4. Push to GitHub
Preview first:
cred push github --dry-run
Then push:
cred push github
Done! Your secret is now in GitHub Actions.
Next steps:
- Commands Reference — all available commands
- Sources — generate credentials from APIs like Resend
- CI/CD Integration — automation patterns
Migrating Existing Secrets
If you choose to migrate to cred instead of continuing with manual .env files you’ll need the original values from wherever you stored them (password manager, .env files, etc.).
Migration Steps
1. Initialize cred:
cred init
2. Import your secrets:
From a .env file:
cred import .env
Or add individually:
cred secret set DATABASE_URL "postgres://..."
cred secret set API_KEY "sk-..."
3. Set up the GitHub target:
cred target set github
4. Push to GitHub:
Preview first:
cred push github --dry-run
Then push:
cred push github
Your existing workflows continue working unchanged — they reference secrets by name, and cred pushes to the same location.
Why Migrate?
| Before (manual) | After (cred) |
|---|---|
| Secrets in .env files or notes | Single encrypted vault |
| Copy-paste into GitHub UI | cred push github |
| No visibility into what’s deployed | cred status shows everything |
| Hard to sync across repos | Push to multiple targets |
Concepts
cred is built around three core concepts that work together to manage your secrets.
Vault
The vault is your local encrypted secrets store. Each project has its own vault at .cred/vault.enc containing all your secrets with metadata like format, timestamps, and descriptions.
Sources
Sources are platforms that can programmatically generate credentials. Instead of manually creating API keys, you can have cred generate them for you with the appropriate permissions.
Currently supported:
- Resend — Email API key generation
Targets
Targets are platforms where you push secrets for deployment. cred uploads your vault secrets to these platforms so your CI/CD pipelines can access them.
Currently supported:
- GitHub — Actions secrets
How They Work Together
┌─────────────┐ generate ┌─────────────┐ push ┌─────────────┐
│ Source │ ───────────────► │ Vault │ ──────────────► │ Target │
│ (Resend) │ │ (encrypted) │ │ (GitHub) │
└─────────────┘ └─────────────┘ └─────────────┘
▲
│ manual set
│
┌─────────────┐
│ You │
└─────────────┘
- Sources generate credentials and store them in your vault
- You can also manually add secrets to the vault
- Targets receive secrets when you push from the vault
Sources
Sources are platforms that can programmatically generate credentials. Unlike targets (which only receive secrets), sources create new API keys on demand.
Why Use Sources?
Instead of manually creating API keys in a web dashboard and copy-pasting them, sources let you:
- Generate keys directly from the command line
- Automatically store them in your vault with proper metadata
- Track which keys came from which source
- Revoke all generated keys at once when needed
Adding a Source
Add your master API key:
cred source add resend --token "$RESEND_API_KEY"
Or interactively (will prompt for token):
cred source add resend
The master token is stored securely in your OS credential store (keyring), not in plaintext files.
Generating Credentials
Generate a new API key from the source:
cred source generate resend RESEND_EMAIL_KEY --permission sending_access -d "Email service key"
This creates a new API key via Resend’s API and stores it in your vault with source: resend metadata.
Managing Source Keys
List API keys at the source:
cred source keys resend
Delete a generated key (removes from source AND local vault):
cred source delete resend RESEND_EMAIL_KEY --yes
List configured sources:
cred source list
Revoking a Source
Revoke source authentication (deletes all generated keys and removes master key):
cred source revoke resend --yes
This will:
- Delete all API keys generated from this source at Resend
- Remove them from the local vault
- Remove the stored master key
Resend
Resend is an email API service. cred can generate API keys with specific permission levels.
Permission Levels
| Permission | Description |
|---|---|
full_access | Can create, delete, get, and update any resource |
sending_access | Can only send emails (recommended for most use cases) |
Example
# Add your master key (needs full_access to create other keys)
cred source add resend
# Generate a restricted key for your app
cred source generate resend EMAIL_API_KEY --permission sending_access
# Push to GitHub Actions
cred push github
Why Sources Use Master Keys
Sources authenticate with a master API key that has permission to create additional keys. The generated keys can have narrower scopes (e.g., sending_access only), following the principle of least privilege.
Your application never sees the master key — it only gets the restricted key you generated.
Targets
Targets are platforms where you push secrets for deployment. cred uploads your vault secrets to these platforms so your CI/CD pipelines can access them.
Adding a Target
Authenticate a deployment target:
cred target set github
You will be securely prompted for a token. The token is stored in your OS credential store, not in plaintext on disk.
For non-interactive use (CI):
cred target set github --token "$GITHUB_TOKEN" --non-interactive
Managing Targets
List configured targets:
cred target list
Revoke a target:
cred target revoke github --yes
GitHub
GitHub Actions secrets are the primary target for cred. Secrets you push become available to your workflows.
Setup
- Create a fine-grained Personal Access Token at github.com/settings/tokens
- Select only the repository you want to manage
- Grant only the Actions secrets permission (read and write)
- Add the token to cred:
cred target set github
Pushing Secrets
# Push all secrets
cred push github
# Push specific secrets
cred push github DATABASE_URL API_KEY
cred automatically detects your repository from git metadata. If you’re not in a git repository, specify it explicitly:
cred push github --repo owner/repo
Using Secrets in Workflows
Once pushed, secrets are available in your GitHub Actions:
jobs:
deploy:
runs-on: ubuntu-latest
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
steps:
- run: echo "Secret is available"
Why Targets Use Simple Tokens
Targets need minimal permissions — just enough to write secrets. This follows the principle of least privilege.
Unlike sources (which need elevated permissions to generate new credentials), targets only need write access to a specific resource (e.g., GitHub Actions secrets for one repository).
Vault
The vault is your local encrypted secrets store. Each cred project has its own vault containing all secrets with rich metadata.
Structure
When you run cred init, cred creates:
.cred/
├── project.toml # Project metadata
└── vault.enc # Encrypted secrets
Global configuration lives at:
~/.config/cred/global.toml
Secret Metadata
Each secret in the vault includes:
| Field | Description |
|---|---|
key | The secret name (e.g., DATABASE_URL) |
value | The encrypted secret value |
format | Content format (raw, json, pem, etc.) |
created_at | When the secret was first added |
updated_at | When the secret was last modified |
description | Optional human-readable description |
source | Where it came from (manual or a source name) |
Secret Formats
cred auto-detects the format of your secrets:
| Format | Detection | Example |
|---|---|---|
pem | Starts with -----BEGIN | Certificates, private keys |
json | Valid JSON object or array | {"key": "value"} |
base64 | Single-line base64 content | SGVsbG8gV29ybGQ= |
multiline | Contains newlines | Multi-line text |
raw | Everything else (default) | super-secret-value |
You can also specify format explicitly:
cred secret set MY_KEY "value" --format json
Viewing the Vault
List all secrets:
cred secret list
Output:
Vault content:
API_KEY = ***** (OpenAI production key)
DATABASE_URL = *****
JWT_SECRET = ***** [modified]
Get a specific secret:
cred secret get JWT_SECRET
With full metadata (JSON):
cred secret get JWT_SECRET --json
{
"data": {
"key": "JWT_SECRET",
"value": "super-secret",
"format": "raw",
"created_at": "2025-12-11T12:00:00Z",
"updated_at": "2025-12-11T12:00:00Z",
"description": null
}
}
Hub-and-Spoke Status
For a complete overview of your project:
cred status
Vault: 3 secrets
RESEND_API_KEY [resend]
DATABASE_URL [manual]
JWT_SECRET [manual] [modified]
Sources: resend ✓
Targets: github ✓
Encryption
The vault is encrypted at rest using ChaCha20-Poly1305. The encryption key is derived from your project and stored securely. See Security Model for details.
Best Practices
- Add
.cred/to.gitignore— Never commit your vault - Use descriptions — Document what each secret is for
- Keep backups — Export periodically with
cred export - Use sources when possible — Generated keys have better audit trails
Commands
Complete reference for all cred commands.
Project Management
| Command | Description |
|---|---|
cred init | Initialize a new cred project |
cred status | Show vault, sources, and targets overview |
cred doctor | Check project health |
Secrets
| Command | Description |
|---|---|
cred secret set | Add or update a secret |
cred secret get | Retrieve a secret value |
cred secret list | List all secrets |
cred secret remove | Delete a secret from vault |
cred secret describe | Update a secret’s description |
cred import | Import from .env file |
cred export | Export to .env file |
Deployment
| Command | Description |
|---|---|
cred push | Push secrets to a target |
cred prune | Delete secrets from a target |
Sources
| Command | Description |
|---|---|
cred source add | Add a credential source |
cred source generate | Generate a new credential |
cred source keys | List keys at the source |
cred source delete | Delete a generated key |
cred source list | List configured sources |
cred source revoke | Remove source and all its keys |
Targets
| Command | Description |
|---|---|
cred target set | Configure a deployment target |
cred target list | List configured targets |
cred target revoke | Remove a target |
Configuration
| Command | Description |
|---|---|
cred config list | View all configuration |
cred config get | Get a config value |
cred config set | Set a config value |
cred config unset | Remove a config value |
Global Flags
All commands support these flags:
| Flag | Description |
|---|---|
--json | Machine-readable JSON output |
--non-interactive | Fail instead of prompting for input |
--dry-run | Preview changes without applying them |
--help | Show help for any command |
init
Initialize a cred project.
cred init
Initialize a new cred project in the current directory:
cred init
This creates:
.cred/
├── project.toml # Project configuration
└── vault.enc # Encrypted secrets vault
Run this once per project, typically at the repository root.
config
Manage global cred configuration.
Configuration is stored at ~/.config/cred/global.toml. Sensitive values (tokens) are stored in your OS keyring, not in this file.
list
View all configuration:
cred config list
get
Get a specific value:
cred config get preferences.default_target
set
Set a configuration value:
cred config set preferences.default_target github
unset
Remove a configuration value:
cred config unset preferences.default_target
Available Settings
| Key | Description |
|---|---|
preferences.default_target | Default target for push/prune commands |
doctor
Check project health and diagnose issues in machine readable JSON:
cred doctor
Output:
{
"api_version": "1",
"data": {
"cred_installed": true,
"global_config": true,
"keychain_access": true,
"project_detected": false,
"ready_for_push": false,
"targets": [],
"vault_accessible": false,
"version": "0.4.0"
},
"status": "ok"
}
This verifies:
- Vault file exists and is readable
- Configuration is valid
- Keyring access works
- Sources and targets are reachable
status
Show a hub-and-spoke overview of your project:
cred status
Output:
Vault: 3 secrets
RESEND_API_KEY [resend]
DATABASE_URL [manual]
JWT_SECRET [manual]
Sources: resend ✓
Targets: github ✓
Git: edneedham/cred
This shows:
- Number of secrets in vault
- Each secret’s source (manual or generated)
- Configured sources and targets
- Git repository (if detected)
JSON Output
For machine-readable output:
cred status --json
secret
Manage secrets in your local vault.
set
Add or update a secret:
cred secret set DATABASE_URL "postgres://user:pass@localhost:5432/db"
With a description:
cred secret set API_KEY "sk-xxx" --description "OpenAI production key"
cred secret set CERT_PEM "-----BEGIN..." -d "TLS certificate"
Format Detection
cred auto-detects the format of your secrets:
| Format | Detection | Example |
|---|---|---|
pem | Starts with -----BEGIN | Certificates, keys |
json | Valid JSON object/array | {"key": "value"} |
base64 | Single-line base64 | SGVsbG8gV29ybGQ= |
multiline | Contains newlines | Multi-line text |
raw | Default | Single-line text |
Override auto-detection:
cred secret set MY_KEY "value" --format json
get
Retrieve a secret value:
cred secret get JWT_SECRET
With full metadata:
cred secret get JWT_SECRET --json
{
"data": {
"key": "JWT_SECRET",
"value": "super-secret",
"format": "raw",
"created_at": "2025-12-11T12:00:00Z",
"updated_at": "2025-12-11T12:00:00Z",
"description": null
}
}
list
List all secrets in the vault:
cred secret list
Output:
Vault content:
API_KEY = ***** (OpenAI production key)
JWT_SECRET = *****
Descriptions are shown inline when present.
remove
Delete a secret from the local vault:
cred secret remove JWT_SECRET --yes
Output:
✓ Removed 'JWT_SECRET' from local vault (3 days old)
Note: This only removes from the local vault. To delete from a target, use
cred prune.
describe
Update a secret’s description:
cred secret describe API_KEY "Updated: rotating quarterly"
Clear a description:
cred secret describe API_KEY
import
Import KEY=VALUE pairs from a .env file:
cred import .env
Existing keys are skipped by default to keep imports non-destructive.
Overwrite existing keys:
cred import .env --overwrite
Preview without writing:
cred import .env --dry-run
export
Write vault contents to a .env file:
cred export .env.backup
Keys are sorted alphabetically. Existing files are preserved unless forced:
cred export .env --force
Preview:
cred export .env --dry-run
push
Push secrets from your vault to a target platform.
Basic Usage
Push all secrets to GitHub:
cred push github
Push specific secrets only:
cred push github DATABASE_URL JWT_SECRET
Dry Run
Preview what will change before pushing:
cred push github --dry-run
Preview specific keys:
cred push github DATABASE_URL JWT_SECRET --dry-run
Nothing is uploaded when --dry-run is used.
Options
| Flag | Description |
|---|---|
--dry-run | Preview changes without pushing |
--json | Machine-readable output |
--non-interactive | Fail instead of prompting |
Repository Detection
cred automatically detects your repository from git metadata when you’re inside a git repository.
If you’re not in a git repository, or need to push to a different repo:
cred push github --repo owner/repo
Incremental Updates
cred tracks which secrets have changed since the last push. Only modified secrets are updated remotely, making pushes efficient.
Workflow Example
# Add a secret
cred secret set DATABASE_URL "postgres://..."
# Preview the push
cred push github --dry-run
# Push when ready
cred push github
Updating Secrets
To update a secret:
# Update locally
cred secret set JWT_SECRET "new-secret-value"
# Preview changes
cred push github --dry-run
# Apply
cred push github
Only the changed secret will be updated at the target.
prune
Delete secrets from a target platform. This is a remote-only operation — it does not affect your local vault.
Basic Usage
Remove a specific key from a target:
cred prune github JWT_SECRET --yes
Remove multiple keys:
cred prune github JWT_SECRET OLD_API_KEY --yes
Dry Run
Preview what will be deleted:
cred prune github JWT_SECRET --dry-run
Prune All
Remove all known keys from a target:
cred prune github --all --yes
This removes all secrets that cred has pushed to this target.
Options
| Flag | Description |
|---|---|
--dry-run | Preview without deleting |
--yes | Confirm destructive operation |
--all | Prune all known secrets |
--json | Machine-readable output |
Safety
⚠️ Destructive operations require --yes unless using --dry-run.
This prevents accidental deletion of production secrets.
Local vs Remote
| Operation | Command |
|---|---|
| Delete from vault (local) | cred secret remove KEY --yes |
| Delete from target (remote) | cred prune github KEY --yes |
| Delete from both | Run both commands |
Workflow Example
Remove a secret completely:
# Remove from GitHub first
cred prune github OLD_SECRET --yes
# Then remove from local vault
cred secret remove OLD_SECRET --yes
Security Model
cred is designed with security as a core concern. This page explains how your secrets are protected.
Encryption at Rest
Your vault (.cred/vault.enc) is encrypted using ChaCha20-Poly1305, a modern authenticated encryption algorithm. This provides both confidentiality and integrity protection.
Token Storage
Source and target tokens (API keys, PATs) are stored in your OS credential store:
| OS | Backend |
|---|---|
| macOS | Keychain |
| Linux | Secret Service (GNOME Keyring, KWallet) |
| Windows | Credential Manager |
Tokens are never written to plaintext files like ~/.config/cred/global.toml.
What cred Protects Against
- Accidental exposure — Secrets aren’t in plaintext files that could be committed
- Disk compromise — Vault is encrypted at rest
- Clipboard history — Interactive prompts for sensitive input
- Overprivileged keys — Sources generate restricted-scope credentials
What cred Does NOT Protect Against
- Compromised machine — If an attacker has access to your running system, they can access your keyring
- Memory attacks — Secrets exist in memory during operations
- Malicious targets — cred trusts the platforms you push to
Safe Inspection
Use --dry-run to preview any operation before it executes:
cred push github --dry-run
cred prune github --all --dry-run
This is especially important for destructive operations.
Best Practices
1. Never Commit Your Vault
Added to .gitignore on cred init otherwise check and add manually if needed:
echo .cred >> .gitignore
2. Use Least Privilege
- For sources: Use master keys only for generation, deploy restricted keys
- For targets: Create fine-grained PATs with minimal permissions
3. Rotation (coming soon)
4. Keep Backups
Export your vault periodically:
cred export secrets-backup.env
Store backups securely (encrypted, offline).
5. Audit Your Secrets
Review what’s in your vault:
cred status
cred secret list
Remove secrets you no longer need.
Threat Model
cred is designed for:
- Individual developers
- Small teams with trusted members
- Projects that don’t require enterprise-grade access control
It is not designed for:
- Multi-tenant environments
- Compliance-heavy industries (healthcare, finance) without additional controls
- Scenarios requiring audit trails or approval workflows