all repos — dot @ master

Shell, editor and terminal configuration, and the tools that go with them

secrets/README.md (view raw)

 1
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 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.