docs: multi-recipient age encryption (recipients/reencrypt) + bump 0.6.2.0 (EGB-283)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b09f94c92f
commit
f7576a3eae
5 changed files with 125 additions and 7 deletions
70
README.md
70
README.md
|
|
@ -129,13 +129,15 @@ secrets pull
|
|||
|
||||
### Sharing with teammates
|
||||
|
||||
To share secrets with a teammate, they need:
|
||||
**Simple approach (shared key):** To share secrets with a teammate, they need:
|
||||
|
||||
1. Access to your private `my-secrets` GitHub repo (add them as a collaborator)
|
||||
2. A copy of `key.txt` (send it to them directly — AirDrop, USB, or in-person)
|
||||
|
||||
Everyone on the team uses the same key. When anyone runs `secrets push`, the encrypted files are updated and everyone else can `secrets pull` to get the latest version.
|
||||
|
||||
**Per-teammate keys (recommended for teams):** Use `secrets recipients add` so each person keeps their own private key — no key sharing needed. See [Onboarding and offboarding teammates](#onboarding-and-offboarding-teammates) below.
|
||||
|
||||
## Usage
|
||||
|
||||
### Daily workflow
|
||||
|
|
@ -168,12 +170,16 @@ secrets clear
|
|||
| `secrets run <command>` | Pull secrets, run a command, then clear secrets when it exits |
|
||||
| `secrets list` | Show all projects that have stored secrets |
|
||||
| `secrets rm <project>` | Delete a project's secrets from the store |
|
||||
| `secrets rekey` | Generate a new encryption key and re-encrypt everything |
|
||||
| `secrets rekey` | Generate a new encryption key and re-encrypt everything (single-key store) or re-encrypt to the current recipients without changing keys (multi-recipient store) |
|
||||
| `secrets verify [project]` | Check the current project's `.secrets.json` against the store (missing/orphaned blobs) and decrypt every blob. `[project]` overrides the store directory name; the manifest is still read from the current directory |
|
||||
| `secrets verify --all` | Decrypt-test every blob in every project — a store-wide integrity sweep |
|
||||
| `secrets migrate [--dry-run]` | Copy-forward this project's encrypted blobs to store format v2 (non-destructive; manifest-free; `--dry-run` previews) |
|
||||
| `secrets migrate --status` | Survey every project's v2 readiness; exits non-zero until the whole store is finalize-ready |
|
||||
| `secrets migrate --finalize` | Drop the old v1 blobs and mark the store v2 — runs once, store-wide, after `verify` is green and every machine is upgraded |
|
||||
| `secrets recipients list` | List the store's recipient public keys (and names if set) |
|
||||
| `secrets recipients add <age1…> [--name N]` | Add a recipient key to the store and immediately re-encrypt every blob to the new set |
|
||||
| `secrets recipients rm <key\|name> [--yes]` | Remove a recipient and re-encrypt the store; `--yes` required when removing your own key |
|
||||
| `secrets reencrypt` | Re-encrypt every blob to the current recipients (idempotent — useful after a manual edit or partial failure) |
|
||||
|
||||
### Automatic project detection
|
||||
|
||||
|
|
@ -448,6 +454,64 @@ Some external secrets are whole binary files — an Android upload keystore, a c
|
|||
|
||||
On `secrets push` the file is encrypted into `<project>/external/`. On `secrets pull` it is restored to the same path with mode `600`; if a different version already exists there, it is backed up to `<name>.secrets-bak` first. The same path rules apply (inside `$HOME`, no `..`, no symlinks). Like merged Gradle keys, restored files are permanent plaintext on disk — `secrets clear` does not remove them.
|
||||
|
||||
### Onboarding and offboarding teammates
|
||||
|
||||
By default every team member uses the **same** `key.txt` (one shared private key). The multi-recipient feature lets each teammate have their **own** keypair while still sharing one store — so you never hand out a secret key to a new hire, and removing an ex-teammate's access is one command.
|
||||
|
||||
#### Onboarding a teammate
|
||||
|
||||
```bash
|
||||
# 1. Teammate generates their own keypair on their machine (never shares the private key)
|
||||
age-keygen -o ~/.secrets/key.txt # writes key.txt; prints the public key
|
||||
|
||||
# 2. Teammate sends you their PUBLIC key (printed by age-keygen, starts with age1…)
|
||||
# — over Slack, email, whatever. Public keys are not secret.
|
||||
|
||||
# 3. An existing member adds the public key to the store
|
||||
secrets recipients add age1theirpublickey --name alice
|
||||
# => Adds alice to recipients.txt, re-encrypts every blob to the full set, pushes.
|
||||
|
||||
# 4. Teammate clones the store repo and drops their key.txt in place
|
||||
git clone git@github.com:<you>/my-secrets.git ~/.secrets
|
||||
# (key.txt already generated in step 1 — nothing to copy)
|
||||
|
||||
# 5. Teammate pulls into any project
|
||||
cd ~/myapp
|
||||
secrets pull
|
||||
# => Their key matches one recipient stanza in every blob — it just works.
|
||||
```
|
||||
|
||||
Run `secrets recipients list` to confirm who has access:
|
||||
|
||||
```
|
||||
alice age1theirpublickey…
|
||||
you age1yourpublickey…
|
||||
```
|
||||
|
||||
#### Offboarding a teammate
|
||||
|
||||
```bash
|
||||
# Remove the recipient by name (or public key) and re-encrypt the store
|
||||
secrets recipients rm alice
|
||||
# => Removes alice from recipients.txt, re-encrypts every blob, pushes.
|
||||
# New blobs are no longer readable by alice's key.
|
||||
```
|
||||
|
||||
> **Important:** git history can't be un-shared. If alice had access during a period when genuinely sensitive values were stored, rotate those values now (update them in the external system and run `secrets push`). The re-encrypt prevents future access; history is permanent.
|
||||
|
||||
#### Managing recipients
|
||||
|
||||
```bash
|
||||
secrets recipients list # show all recipient keys and names
|
||||
secrets recipients add age1… # add a key (bootstraps recipients.txt on a legacy store)
|
||||
secrets recipients add age1… --name bob # attach a human-readable label
|
||||
secrets recipients rm bob # remove by name
|
||||
secrets recipients rm age1… # remove by public key
|
||||
secrets reencrypt # re-encrypt to current recipients (idempotent heal)
|
||||
```
|
||||
|
||||
`secrets which` shows a `recipients: N (alice, bob, …)` line so you can always confirm the active set from any project directory.
|
||||
|
||||
## Safety features
|
||||
|
||||
- **`secrets run` auto-clears** — plaintext files are deleted when the command exits, errors, or is interrupted with Ctrl-C
|
||||
|
|
@ -501,7 +565,7 @@ For complete rotation with no historical exposure, create a fresh `~/.secrets/`
|
|||
## Development
|
||||
|
||||
```bash
|
||||
# Run the test suite (237 tests across three files)
|
||||
# Run the test suite (272 tests across four files)
|
||||
brew install bats-core
|
||||
bats test/
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue