Skip to main content

Handy secret management system with a convenient CLI and readable storage format.

Project description

Pocket Protector 🔏

Pocket Protector provides a cryptographically-strong, serverless secret management infrastructure. Pocket Protector enables key management as code, securely storing secrets in a versionable format, right alongside the corresponding application code.

Note: The canonical repository is now github.com/mahmoud/pocket_protector. The original home was github.com/SimpleLegal/pocket_protector.

See the debut meetup talk for an introduction.

Pocket Protector's approach lets you:

  • Leverage existing user, versioning, and backup systems, with no infrastructure to set up
  • Support multiple environments
  • Integrate easily with existing key management systems (AWS/Heroku/GitHub Actions)

Pocket Protector also:

  • Minimizes the number of passphrases and keys your team has to remember and secure
  • Beats the heck out of hardcoded plaintext secrets!

Installation

Right now the easiest way to install Pocket Protector across all platforms is with pip:

pip install pocket_protector

This will install the command-line application pocket_protector, conveniently shortened to pprotect, which you can use to test your installation:

$ pprotect version
pocket_protector version 26.0.0dev

Once the above is working, we're ready to start using Pocket Protector!

Usage

Pocket Protector aims to be as easy to use as a secret management system can get. That said, understanding security takes time, so be sure to go beyond the quick start and reference below, and read our User Guide as well.

Quick start

Pocket Protector's CLI is its primary interface. It presents a compact set of commands, each representing one action you might want to take on a secret store. Basic usage starts on your laptop, inside your checked out code repository:

# create a new protected file
pprotect init

# add a key domain
pprotect add-domain

# add a secret to the new key domain
pprotect add-secret

# decrypt and read out the secret
pprotect decrypt-domain

Each of these will prompt the user for credentials when necessary. See the section below on passing credentials.

When you're done updating the secret store, simply git commit (or equivalent) to save your changes. Should you make any mistakes, use your VCS to revert the changes.

Passing credentials

By default, the pocket_protector command prompts you for credentials when necessary. But convenience and automation both demand more options, highlighted here:

  • Command-line Flags

    • -u / --user USER_EMAIL - specifies the user email for subcommands which require it
    • --passphrase-file PATH - specifies a path to a readable file which contains the passphrase (useful for mount-based key management, like Docker)
    • --domain DOMAIN - specifies the name of the domain
    • --non-interactive - causes the command to fail when credentials cannot be gotten by other means
    • --env-prefix PREFIX - sets the env var prefix for credential lookup (default: PPROTECT). When set, credentials are read from PREFIX_USER and PREFIX_PASSPHRASE instead of the defaults
  • Environment variables

    • PPROTECT_USER - environment variable which contains the user email
    • PPROTECT_PASSPHRASE - environment variable which contains the passphrase (useful for environment variable-based key management, used by AWS/Heroku/many CI systems)

In all cases, flags take precedence over environment variables, and both take precedence over and bypass interactive prompts. In the event an incorrect credential is passed, pocket_protector does not automatically check other sources.

Custom env var prefix

In environments where multiple pocket_protector-managed projects coexist, use --env-prefix to namespace credential env vars per project:

# Project A
export PROJECTA_USER=alice@example.com
export PROJECTA_PASSPHRASE=secret_a
pprotect decrypt-domain prod --env-prefix PROJECTA

# Project B (simultaneously)
export PROJECTB_USER=bob@example.com
export PROJECTB_PASSPHRASE=secret_b
pprotect decrypt-domain staging --env-prefix PROJECTB

The default prefix remains PPROTECT, so existing workflows are unaffected. When using pprotect exec with a custom prefix, both the custom prefix vars and the default PPROTECT_* vars are scrubbed from the child process.

See our User Guide for more usage tips.

Command summary

Here is a summary of all commands:

usage: pprotect [COMMANDS]

Commands:
  add-domain            add a new domain to the protected
  add-key-custodian     add a new key custodian to the protected
  add-owner             add a key custodian as owner of a domain
  add-secret            add a secret to a specified domain
  decrypt-domain        decrypt and display JSON-formatted cleartext for a
                        domain
  exec                  run a command with decrypted secrets injected as
                        environment variables
  init                  create a new pocket-protected file
  list-all-secrets      display all secrets, with a list of domains the key is
                        present in
  list-audit-log        display a chronological list of audit log entries
                        representing file activity
  list-domain-secrets   display a list of secrets under a specific domain
  list-domains          display a list of available domains
  list-user-secrets     similar to list-all-secrets, but filtered by a given
                        user
  migrate-owner         migrate all domain ownerships from one custodian to
                        another
  rekey-custodian       re-encrypt a custodian's key with a new passphrase
                        and/or KDF
  rm-domain             remove a domain from the protected
  rm-owner              remove an owner's privileges on a specified domain
  rm-secret             remove a secret from a specified domain
  rotate-domain-keys    rotate the internal keys for a particular domain (must
                        be owner)
  set-key-custodian-passphrase
                        change a key custodian passphrase
  update-secret         update an existing secret in a specified domain
  version               display the current version

Agent & Automation Security

Pocket Protector is commonly used in CI/CD pipelines and increasingly alongside AI coding agents. In these contexts, secret hygiene matters more than usual. Any process with shell access can read environment variables, cat .env, or inspect /proc/*/environ.

Credential injection: from safest to weakest

  1. pprotect exec (safest): decrypts a domain and injects secrets as env vars into a child process. The custodian passphrase is scrubbed from the child environment. Secrets exist only in the child process memory, never on disk or in the parent env.

    pprotect exec --domain prod -- ./myapp --flag arg
    
  2. --passphrase-file from a restricted mount: store the passphrase on a tmpfs or Docker secret mount with 0400 permissions. Keeps the passphrase off the command line and out of the process environment.

    pprotect decrypt-domain prod --passphrase-file /run/secrets/pp_pass
    
  3. PPROTECT_PASSPHRASE env var (simplest): the classic option but not the safest. Readable by any subprocess, including AI agents, build scripts, and debug tooling. Use only when other options are not available.

Output formats for decrypt-domain

decrypt-domain supports --output-format json (default), --output-format env (dotenv-style), and --output-format shell (eval-able exports). Use --secret SECRET_NAME to extract a single value.

Secret names are case-sensitive and stored exactly as provided. The validation rule is: start with a letter, then ASCII letters, digits, hyphens, or underscores (e.g. db-pass, API_KEY, tls-cert).

# JSON (default)
pprotect decrypt-domain prod

# .env format
pprotect decrypt-domain prod --output-format env

# Shell export format
eval $(pprotect decrypt-domain prod --output-format shell)

# Single secret, raw value (name must match exactly)
db_pass=$(pprotect decrypt-domain prod --secret db-pass)

What Pocket Protector is not

Pocket Protector manages static deploy-time secrets -- database passwords, API keys, TLS certificates. It is not a runtime credential manager. For dynamic credentials (OAuth tokens, short-lived sessions, PKCE flows), use a runtime credential manager alongside Pocket Protector.

Other explicit non-goals:

  • Network daemon / SaaS mode -- serverless is the value prop
  • Time-limited credentials -- no clock-based expiry; use pprotect exec to limit secret lifetime to a process
  • Per-secret access control -- domains are the access boundary
  • MCP server mode -- use pprotect exec to inject secrets into MCP server processes at startup

Security note on pprotect exec

An agent or process that can run arbitrary commands could call pprotect decrypt-domain directly. exec reduces accidental exposure (logged output, env dumps, process listings), not adversarial exfiltration by a fully compromised agent. Defense in depth still applies: restrict filesystem access, use scoped custodians, and audit the protected.yaml change log.

Design

The theory of operation is that the protected.yaml file consists of "key domains" at the root level. Each domain stores data encrypted by a keypair. The public key of the keypair is stored in plaintext, so that anyone may encrypt and add a new secret. The private key is encrypted with the owner's passphrase. The owners are known as "key custodians", and their private keys are protected by passphrases.

Secrets are broken up into domains for the purposes of granting security differently. For example, prod, dev, and stage may all be different domains. Protected stores may have as few or as many domains as the team and application require.

To allow secrets to be accessed in a certain environment, Pocket Protector must be invoked with a user and passphrase. As long as the credentials are correct and the user has permissions to a domain, all secrets within that domain are unlocked.

Passphrase security will depend on the domain. For instance, a domain used for local development may set the passphrase as an environment variable, or hardcode it in a configuration file.

On the other hand, a production domain would likely require manual entry of an authorized release engineer, or use AWS/GCP/Heroku key management solutions to inject the passphrase.

for prod domains, use AWS / heroku key management to store the passphrase

An application / script wants to get its secrets:

# at initialization
secrets = KeyFile.decrypt_domain(domain_name, Creds(name, passphrase))
# ... later to access a secret
secrets[secret_name]

An application / script that wants to add / overwrite a secret:

KeyFile.from_file(path).with_secret(
    domain_name, secret_name, value).write()

Note -- the secure environment key is needed to read secrets, but not write them. Change management on secrets is intended to follow normal source-code management.

File structure:

[key-domain]:
  meta:
    owners:
      [name]: [encrypted-private-key]
    public_key: [b64-bytes]
    private_key: [b64-bytes]
  secret-[name]: [b64-bytes]
key-custodians:
  [name]:
    public-key: [b64-bytes]
    encrypted-private-key: [b64-bytes]

Threat model

An attacker is presumed to be able to read but not write the contents of protected.yaml. This could happen because a developer's laptop is compromised, GitHub credentials are compromised, or (most likely) Git history is accidentally pushed to a publicly acessible repo.

With read access, an attacker gets environment and secret names, and which secrets are used in which environments.

Neither the file as a whole nor individual entries are signed, since the security model assumes an attacker does not have write access.

Notes

Pocket Protector is a streamlined, people-centric secret management system, custom built to work with distributed version control systems.

  • Pocket Protector is a data protection tool, not a change management tool. While it has convenient affordances like an informal audit_log, Pocket Protector is meant to be used in conjunction with your version management tool. Signed commits are a particularly good complement.
  • Pocket Protector is designed for single-user usage. This is not a scaling limitation as much as it is a scaling feature. Single-user means that every pprotect command needs at most one credentialed user present. No sideband communication is required, minimizing leakage, while maintaining a system as distributed as your version management.

FAQ

Securing Write Access

Pocket Protector does not provide any security against unauthorized writes to the protected.yaml file, by design. Firstly, without any Public Key Infrastructure, Pocket Protector is not a good basis for cryptographic signatures. (An attacker that modifies the file could also replace the signing keypair with their own; the only way to detect this would be to have a data-store outside of the file.)

Secondly -- and more importantly -- the Git or Mercurial repository already has good controls around write access. All changes are auditable, authenticated with ssh keypairs or user passphrases. For futher security, consider using signed commits:

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pocket_protector-26.0.0.tar.gz (43.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pocket_protector-26.0.0-py3-none-any.whl (36.0 kB view details)

Uploaded Python 3

File details

Details for the file pocket_protector-26.0.0.tar.gz.

File metadata

  • Download URL: pocket_protector-26.0.0.tar.gz
  • Upload date:
  • Size: 43.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pocket_protector-26.0.0.tar.gz
Algorithm Hash digest
SHA256 2161a2728ac59c049318649df110d07d16b2fecb46200788770b9417cd3ba57a
MD5 7c07ee7b95c2c1f3fac75c23cf2bebdc
BLAKE2b-256 8a88961ccd61f60f11944a406f2d05eb4d2b898c07f6a3ce30c3422c5270e83f

See more details on using hashes here.

Provenance

The following attestation bundles were made for pocket_protector-26.0.0.tar.gz:

Publisher: publish.yml on mahmoud/pocket_protector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pocket_protector-26.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pocket_protector-26.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a4ba05ef8f68d90d4296694ec39ce055053c089e3086b0067115897fb7eed3cb
MD5 2a9c6422668387a9bd8c67ebaa545816
BLAKE2b-256 3bcaa26f3f9ee7fe3c2863b75f5b14f338355803598313533ec67d795eff0003

See more details on using hashes here.

Provenance

The following attestation bundles were made for pocket_protector-26.0.0-py3-none-any.whl:

Publisher: publish.yml on mahmoud/pocket_protector

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page