secret/README.md
2025-05-28 14:06:29 -07:00

6.4 KiB

architecture

  • secret vault allows you to change 'vaults'. vaults are just a universe of secrets (and a single associated long-term key). you can have multiple vaults, each with its own long-term key and secrets. this is useful for separating work and personal secrets, or for separating different projects with different long-term keys.

    • secret vault list
    • secret vault create <name> creates a new profile
    • secret vault select <name> selects (switches to) a profile

    the first and initial vault is titled default.

  • secret init initializes a new vault and imports a user-provided BIP39 mnemonic phrase. The user must provide their own mnemonic phrase. The long-term keypair is derived from this mnemonic. The long-term keypair is used to encrypt and decrypt secrets. The long-term keypair is stored in the vault. The private key for the vault is encrypted to a short-term keypair. The short-term keypair private key is encrypted to a passphrase.

    Use secret generate mnemonic to create a new BIP39 mnemonic phrase if you need one.

    if there is already a vault, secret init exits with an error.

  • secret import [vaultname] will derive a long-term key pair from a bip32 seed phrase and import it into the named vault. if no vault name is specified, default is used. if the named vault already exists, it exits with an error.

    first:

    • the long term key pair will be derived in memory
    • a random short term (unlock) key pair will be derived in memory

    then:

    • the long term key pair public key will be written to disk
    • the short term key pair public key will be written to disk
    • the long term key pair private key will be encrypted to the short term public key and written to disk
    • the short term key pair private key will be encrypted to a passphrase and written to disk
  • secret enroll sep creates a short-term keypair inside the secure enclave of a macOS device. it will then use one of your existing short-term keypairs to decrypt the long-term keypair, re-encrypt it to the secure enclave short-term keypair, and write it to disk. it requires an existing vault, and errors otherwise.

  • short-term keypairs are called 'unlock keys'.

  • secret add <secret> adds a secret to the vault. this will generate a keypair (secret-specific key) and encrypt the private portion of the secret-specific key (called an 'unlock key') to the long-term keypair and write it to disk. if the secret already exists it will not overwrite, but will exit with an error, unless --force/-f is used. the secret identifier is [a-z0-9.-_/]+ and is used as a storage directory name. slashes are converted to % signs.

    in a future version, overwriting a secret will cause the current secret to get moved to a timestamped history archive.

  • secret get <secret> retrieves a secret from the vault. this will use an unlock keypair to decrypt the long-term keypair in memory, then use the long-term keypair to decrypt the secret-specific keypair, which is then used to decrypt the secret. the secret is then returned in plaintext on stdout.

  • secret keys list lists the short-term keypairs in the current vault. this will show the public keys of the short-term keypairs and their creation dates, as well as any flags (such as hsm). their identifiers are a metahash of the public key data using the sha256 algorithm.

  • secret keys rm <keyid> removes a short-term keypair from the vault. this will remove the short-term keypair from the vault, and remove the long-term keypair from the short-term keypair.

  • secret keys add pgp <pgp keyid> adds a new short-term keypair to the vault. this will generate a new short-term keypair and encrypt it to a given gpg key, to allow unlocking a vault with an existing gpg key, for people who use yubikeys or other gpg keys with an agent. the new short-term keypair is randomly generated, the public key stored, and the private key encrypted to the gpg key and stored.

  • secret key select <keyid> selects a short-term keypair to use for secret get operations.

file layout

$BASE = ~/.config/berlin.sneak.pkg.secret (on linux per XDG) $BASE = ~/Library/Application Support/berlin.sneak.pkg.secret (on macOS)

$BASE/configuration.json
$BASE/currentvault -> $BASE/vaults.d/default (symlink)
$BASE/vaults.d/default/
$BASE/vaults.d/default/vault-metadata.json
$BASE/vaults.d/default/pub.age
$BASE/vaults.d/default/current-unlock-key -> $BASE/vaults.d/default/unlock.d/passphrase (symlink)
$BASE/vaults.d/default/unlock.d/passphrase/unlock-metadata.json
$BASE/vaults.d/default/unlock.d/passphrase/pub.age
$BASE/vaults.d/default/unlock.d/passphrase/priv.age
$BASE/vaults.d/default/unlock.d/passphrase/longterm.age # long-term keypair, encrypted to this short-term keypair
$BASE/vaults.d/default/unlock.d/sep/unlock-metadata.json
$BASE/vaults.d/default/unlock.d/sep/pub.age
$BASE/vaults.d/default/unlock.d/sep/priv.age
$BASE/vaults.d/default/unlock.d/sep/longterm.age # long-term keypair, encrypted to this short-term keypair
$BASE/vaults.d/default/unlock.d/pgp/unlock-metadata.json
$BASE/vaults.d/default/unlock.d/pgp/pub.age
$BASE/vaults.d/default/unlock.d/pgp/priv.asc
$BASE/vaults.d/default/unlock.d/pgp/longterm.age # long-term keypair, encrypted to this short-term keypair
$BASE/vaults.d/default/secrets.d/my-tinder-password/value.age
$BASE/vaults.d/default/secrets.d/my-tinder-password/pub.age
$BASE/vaults.d/default/secrets.d/my-tinder-password/priv.age # secret-specific key, encrypted to long-term key
$BASE/vaults.d/default/secrets.d/my-tinder-password/secret-metadata.json
$BASE/vaults.d/default/secrets.d/mail%berlin.sneak.secrets.imaplogin/value.age
$BASE/vaults.d/default/secrets.d/mail%berlin.sneak.secrets.imaplogin/pub.age
$BASE/vaults.d/default/secrets.d/mail%berlin.sneak.secrets.imaplogin/priv.age
$BASE/vaults.d/default/secrets.d/mail%berlin.sneak.secrets.imaplogin/secret-metadata.json

example configuration.json

json { "$id": "https://berlin.sneak.pkg.secret/configuration.json", "$schema": "https://berlin.sneak.pkg.secret/configuration.schema.json", "version": 1, "configuration": { "createdAt": "2025-01-01T00:00:00Z", "requireAuth": false, "pubKey": "<public key of the long-term keypair>", "pubKeyFingerprint": "<fingerprint of the long-term keypair>" } }