# secret - Local Secret Manager secret is a command-line local secret manager that implements a hierarchical key architecture for storing and managing sensitive data. It supports multiple vaults, various unlock mechanisms, and provides secure storage using the `age` encryption library. It could be used as password manager, but was not designed as such. I created it to scratch an itch for a secure key/value store for replacing a bunch of pgp-encrypted files in a directory structure. ## Core Architecture ### Three-Layer Key Hierarchy Secret implements a three-layer key architecture: 1. **Long-term Keys**: Derived from BIP39 mnemonic phrases, these provide the foundation for all encryption 2. **Unlockers**: Short-term keys that encrypt the long-term keys, supporting multiple authentication methods 3. **Version-specific Keys**: Per-version keys that encrypt individual secret values ### Version Management Each secret maintains a history of versions, with each version having: - Its own encryption key pair - Metadata (unencrypted) including creation time and validity period - Immutable value storage - Atomic version switching via symlink updates ### Vault System Vaults provide logical separation of secrets, each with its own long-term key and unlocker set. This allows for complete isolation between different contexts (work, personal, projects). ## Installation Build from source: ```bash git clone cd secret make build ``` ## Quick Start 1. **Initialize the secret manager**: ```bash secret init ``` This creates the default vault and prompts for a BIP39 mnemonic phrase. 2. **Generate a mnemonic** (if needed): ```bash secret generate mnemonic ``` 3. **Add a secret**: ```bash echo "my-password" | secret add myservice/password ``` 4. **Retrieve a secret**: ```bash secret get myservice/password ``` ## Commands Reference ### Initialization #### `secret init` Initializes the secret manager with a default vault. Prompts for a BIP39 mnemonic phrase and creates the initial directory structure. **Environment Variables:** - `SB_SECRET_MNEMONIC`: Pre-set mnemonic phrase - `SB_UNLOCK_PASSPHRASE`: Pre-set unlock passphrase ### Vault Management #### `secret vault list [--json]` / `secret vault ls` Lists all available vaults. The current vault is marked. #### `secret vault create ` Creates a new vault with the specified name. **Vault Name Format:** only lowercase ASCII letters, digits, `.`, `-` and `_` are allowed, and a name must not be empty, `.` or `..`. #### `secret vault select ` Switches to the specified vault for subsequent operations. #### `secret vault remove [--force]` / `secret vault rm` ⚠️ 🛑 **DANGER**: Permanently removes a vault and all its secrets. Like Unix `rm`, this command does not ask for confirmation. Requires --force if the vault contains secrets. With --force, will automatically switch to another vault if removing the current one. - `--force, -f`: Force removal even if vault contains secrets - **NO RECOVERY**: All secrets in the vault will be permanently deleted ### Secret Management #### `secret add [--force]` Adds a secret to the current vault. Reads the secret value from stdin. - `--force, -f`: Overwrite existing secret **Secret Name Format:** only ASCII letters, digits, `.`, `-`, `_` and `/` are allowed, and a name must not be empty, start with `.` or `/`, end with `/`, contain `//`, or have `..` as a path segment. - Forward slashes (`/`) are converted to percent signs (`%`) for storage - Examples: `database/password`, `api.key`, `ssh_private_key` #### `secret get [--version ]` Retrieves and outputs a secret value to stdout. - `--version, -v`: Get a specific version (default: current) #### `secret list [filter] [--json]` / `secret ls` Lists all secrets in the current vault. Optional filter for substring matching. #### `secret remove ` / `secret rm` ⚠️ 🛑 **DANGER**: Permanently removes a secret and ALL its versions. Like Unix `rm`, this command does not ask for confirmation. - **NO RECOVERY**: Once removed, the secret cannot be recovered - **ALL VERSIONS DELETED**: Every version of the secret will be permanently deleted #### `secret move ` / `secret mv` / `secret rename` Moves or renames a secret within the current vault. - Fails if the destination already exists - Fails if the destination is the source under another name, such as `foo` for `Foo` on a case-insensitive filesystem (the macOS default); there, to change only the case of a name, move the secret to a third name first - Preserves all versions and metadata ### Version Management #### `secret version list ` / `secret version ls` Lists all versions of a secret showing creation time, status, and validity period. #### `secret version promote ` Promotes a specific version to current by updating the symlink. Does not modify any timestamps, allowing for rollback scenarios. #### `secret version remove ` / `secret version rm` ⚠️ 🛑 **DANGER**: Permanently removes a specific version of a secret. Like Unix `rm`, this command does not ask for confirmation. - **NO RECOVERY**: Once removed, this version cannot be recovered - Cannot remove the current version (must promote another version first) ### Key Generation #### `secret generate mnemonic` Generates a cryptographically secure BIP39 mnemonic phrase. #### `secret generate secret [--length=16] [--type=base58] [--force]` Generates and stores a random secret. - `--length, -l`: Length of generated secret (default: 16) - `--type, -t`: Type of secret (`base58`, `alnum`) - `--force, -f`: Overwrite existing secret ### Unlocker Management #### `secret unlocker list [--json]` / `secret unlocker ls` Lists all unlockers in the current vault with their metadata. #### `secret unlocker add [options]` Creates a new unlocker of the specified type: **Types:** - `passphrase`: Traditional passphrase-protected unlocker - `pgp`: Uses an existing GPG key for encryption/decryption - `keychain`: macOS Keychain integration (macOS only) - `secure-enclave`: Hardware-backed Secure Enclave protection (macOS only) **Options:** - `--keyid `: GPG key ID (optional for PGP type, uses default key if not specified) A vault has one passphrase unlocker: adding one replaces the one the vault has, which is removed only once the new one is the current unlocker. #### `secret unlocker remove [--force]` / `secret unlocker rm` ⚠️ 🛑 **DANGER**: Permanently removes an unlocker. Like Unix `rm`, this command does not ask for confirmation. Cannot remove the last unlocker if the vault has secrets unless --force is used. An unlocker directory that `secret unlocker list` skips with a warning, because its metadata cannot be read or parsed, is removed by the directory name the warning gives. - `--force, -f`: Force removal of last unlocker even if vault has secrets - **CRITICAL WARNING**: Without unlockers and without your mnemonic phrase, vault data will be PERMANENTLY INACCESSIBLE - **NO RECOVERY**: Removing all unlockers without having your mnemonic means losing access to all secrets forever #### `secret unlocker select ` Selects an unlocker as the current default for operations. ### Import Operations #### `secret import --source ` Imports a secret from a file and stores it in the current vault under the given name. #### `secret vault import [vault-name]` Imports a mnemonic phrase into the specified vault (defaults to "default"). ### Encryption Operations #### `secret encrypt [--input=file] [--output=file]` Encrypts data using an Age key stored as a secret. If the secret doesn't exist, generates a new Age key. #### `secret decrypt [--input=file] [--output=file]` Decrypts data using an Age key stored as a secret. ## Storage Architecture ### Directory Structure ``` ~/.local/share/secret/ ├── vaults.d/ │ ├── default/ │ │ ├── unlockers.d/ │ │ │ ├── passphrase-