# szecret Secret management using [age](https://github.com/FiloSottile/age) encryption. ## Quick Start ```bash # Store a secret echo "my-api-key" | szecret put api-key # Retrieve a secret szecret get api-key # List all secrets szecret list ``` ## Commands | Command | Description | |---------|-------------| | `get ` | Retrieve and decrypt a secret | | `put ` | Store a secret (reads from stdin) | | `list` | List all secrets in the store | | `ready` | Check if private key is configured | | `ensure` | Ensure private key is configured (interactive) | | `rotate` | Generate a new key and re-encrypt the global store | | `create-local ` | Create a localized secret store | | `put-local ` | Encrypt to a local store | | `get-local ` | Decrypt from a local store | ## Local Stores Local stores allow you to keep encrypted secrets outside the global store, useful for project-specific secrets that live alongside the code. ### Creating a Local Store ```bash # Initialize a local store in a directory szecret create-local myproject /path/to/project/secrets/ # This creates: # - A key in the global store (local-key-myproject) # - A metadata file at /path/to/project/secrets/.szecret containing: # store=myproject # pubkey=age1... ``` The metadata includes the public key for verification, preventing accidental use of the wrong store. ### Using a Local Store ```bash # Encrypt a secret echo "db-password" | szecret put-local /path/to/project/secrets/db.age # Decrypt a secret szecret get-local /path/to/project/secrets/db.age ``` The commands automatically read the store name from the `.szecret` metadata file in the directory. ## Setup ### Where the store lives These scripts carry no secrets. The global store is a directory of `.age` files plus `id.pub`, the public key they are encrypted to, kept in a checkout of its own. szecret looks for it in this order: 1. `$SZECRET_STORE` and `$SZECRET_PUBLIC_KEY`, if set. 2. `~/.local/share/szecret/store` and `~/.local/share/szecret/id.pub`, beside the private keys. Normally symlinks into the checkout that holds the store. With neither, every command stops with setup instructions, and `ready` fails silently. ### Initial Setup ```bash # Generate a new keypair (file must be named after its public key) age-keygen -o private pubkey="$(age-keygen -y private)" mkdir -p ~/.local/share/szecret/store echo "${pubkey}" > ~/.local/share/szecret/id.pub mv private "${HOME}/.local/share/szecret/${pubkey}" ``` ### On a New Machine ```bash # The ensure command will prompt for your private key szecret ensure ``` ### Rotating the Global Key ```bash # Re-encrypt all secrets with a new age key szecret rotate ``` This updates `id.pub` and every file in the store. When complete, commit those changes in the checkout that holds the store, and update the private key backup in 1Password. ## Migration Guide ### Migrating Local Stores (pre-2026) If you created local stores before the metadata system was introduced, run `create-local` again with the directory - it will detect the existing key and just add the metadata: ```bash szecret create-local myapp /path/to/secrets/ # Output: Added metadata for existing store 'myapp' ``` **Before (old syntax):** ```bash szecret create-local myapp echo "secret" | szecret put-local myapp /path/to/secrets/password.age szecret get-local myapp /path/to/secrets/password.age ``` **After (new syntax):** ```bash # One-time migration szecret create-local myapp /path/to/secrets/ # Now use simplified commands echo "secret" | szecret put-local /path/to/secrets/password.age szecret get-local /path/to/secrets/password.age ``` **For automated scripts:** The old `get-local ` syntax still works but emits a deprecation warning suggesting the migration command. ## Environment Variables For testing or custom setups, these environment variables override default paths: | Variable | Description | Default | |----------|-------------|---------| | `SZECRET_STORE` | Global store directory | `~/.local/share/szecret/store` | | `SZECRET_PUBLIC_KEY` | Public key path | `~/.local/share/szecret/id.pub` | | `SZECRET_PRIVATE_KEY` | Single private key file (full override) | (none) | | `SZECRET_PRIVATE_KEY_DIR` | Directory containing private keys (named by their public key) | `~/.local/share/szecret` | * Private keys are stored in `~/.local/share/szecret/` (or `$SZECRET_PRIVATE_KEY_DIR`), with each file named after its public key (e.g. `age1...`). * `id.pub` indicates which public key is current. ## Testing ```bash # Run all tests ./test/run.sh # Run specific tests ./test/run.sh local create-local ``` See [test/README.md](test/README.md) for more details.