# 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 ` creates a new profile * `secret vault select ` selects (switches to) a profile the first and initial vault is titled `default`. * `secret init` initializes a new vault. this will create a new profile and generate a new long-term keypair. 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. to generate the long-term keypair, a random bip32 seed phrase is generated, then the process proceeds exactly as `secret import private`. the randomly generated bip32 seed phrase is shown to the user. 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 ` 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 ` 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 ` 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 ` 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 ` 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": "", "pubKeyFingerprint": "" } } `