cred

## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# 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 Silicon
- `cred-vX.Y.Z-x86_64-apple-darwin` - macOS Intel
- `cred-vX.Y.Z-x86_64-unknown-linux-gnu` - Linux x86_64
- `cred-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.

Learn more about the Vault →

## 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

Learn more about Sources →

## 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

Learn more about Targets →

## How They Work Together

    ┌─────────────┐     generate      ┌─────────────┐      push       ┌─────────────┐
    │   Source    │ ───────────────►  │    Vault    │ ──────────────► │   Target    │
    │  (Resend)   │                   │ (encrypted) │                 │  (GitHub)   │
    └─────────────┘                   └─────────────┘                 └─────────────┘
                                            ▲
                                            │ manual set
                                            │
                                      ┌─────────────┐
                                      │     You     │
                                      └─────────────┘

1. Sources generate credentials and store them in your vault
2. You can also manually add secrets to the vault
3. 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:

1. Delete all API keys generated from this source at Resend
2. Remove them from the local vault
3. 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

1. Create a fine-grained Personal Access Token at github.com/settings/tokens
2. Select only the repository you want to manage
3. Grant only the Actions secrets permission (read and write)
4. 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

1. Add `.cred/` to `.gitignore` — Never commit your vault
2. Use descriptions — Document what each secret is for
3. Keep backups — Export periodically with `cred export`
4. 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