diff options
| author | Igor Soarez <igor@soarez.org> | 2026-09-17 19:45:23 +0100 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-09-17 19:45:23 +0100 |
| commit | 13cc065ec014b86369dfe4ad60d1a43fa06fd251 (patch) | |
| tree | 684690c61b7b59758c2aac132c65b81c86de0608 /secrets/README.md | |
dotfiles
Shell, editor and terminal configuration, the tools that go with them, and
szecret, an age-based secret manager whose store lives elsewhere.
History restarts here. What came before was not written to be read by
anyone else, and carried machine names, addresses and an encrypted secret
store; it is kept privately instead. What is left is meant to be safe on
any machine, including one whose owner is not me: see CLAUDE.md for the
rule it is written to.
Diffstat (limited to 'secrets/README.md')
| -rw-r--r-- | secrets/README.md | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/secrets/README.md b/secrets/README.md new file mode 100644 index 0000000..293b747 --- /dev/null +++ b/secrets/README.md @@ -0,0 +1,163 @@ +# 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 <name>` | Retrieve and decrypt a secret | +| `put <name>` | 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 <name> <dir>` | Create a localized secret store | +| `put-local <file>` | Encrypt to a local store | +| `get-local <file>` | 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 <store-name> <file>` 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. |
