Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ nosetests.xml
*.sw[op]

.cache/
.venv/
85 changes: 85 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,94 @@ Commands:
set-key-custodian-passphrase
change a key custodian passphrase
update-secret update an existing secret in a specified domain
exec run a command with decrypted secrets injected as
environment variables
```


## 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.

```sh
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.

```sh
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`).

```sh
# 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
Expand Down
2 changes: 2 additions & 0 deletions pocket_protector/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from ._version import __version__
from .file_keys import KeyFile, Creds, PPError, KDF_SENSITIVE, KDF_INTERACTIVE
14 changes: 10 additions & 4 deletions pocket_protector/__main__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import os
import sys

from .cli import main
from .cli import main as _main

if __name__ == '__main__': # pragma: no cover

def main():
try:
sys.exit(main() or 0)
sys.exit(_main() or 0)
except Exception:
if os.getenv('PPROTECT_ENABLE_DEBUG'):
import pdb;pdb.post_mortem()
import pdb
pdb.post_mortem()
raise


if __name__ == '__main__':
main()
7 changes: 1 addition & 6 deletions pocket_protector/_version.py
Original file line number Diff line number Diff line change
@@ -1,6 +1 @@

# The version of PocketProtector, used in the version subcommand, as
# well as in setup.py. For full release directions, see the bottom of
# setup.py.

__version__ = '20.0.2dev'
__version__ = '24.0.0dev'
Loading