Keeping script secrets in Proton Pass
Using pass-cli references, process injection, and an optional Linux keyring cache instead of committing credentials.
I keep configuration and wrapper scripts in Git, but credentials do not belong there. The usual compromise is an ignored .env file, which avoids a public commit but still leaves long-lived plaintext secrets scattered across machines.
The Proton Pass command-line client offers a better boundary: scripts contain references, and the real values are resolved from the vault only when they are needed.
This note reflects the CLI as documented in July 2026. Secret-management tools evolve, so check the official pass-cli documentation before building automation around it.
References instead of values
Proton Pass uses references in this form:
pass://vault/item/field
For example:
pass://Development/Example API/username
pass://Development/Example API/token
Vault, item, and field names are case-sensitive. IDs can be used instead when names are ambiguous.
To retrieve one field directly:
pass-cli item view "pass://Development/Example API/token"
That is useful interactively, but manually exporting the result puts more responsibility on the surrounding shell. The built-in run command is a cleaner fit for applications.
Injecting an environment at launch
Create a template such as app.env containing references rather than secrets:
API_USER=pass://Development/Example API/username
API_TOKEN=pass://Development/Example API/token
Then launch the program through Proton Pass:
pass-cli run --env-file app.env -- ./my-app
pass-cli resolves the references, supplies the resulting environment variables to the child process, forwards signals and standard I/O, and masks resolved secrets in output by default. The calling shell does not retain those injected values after the child exits.
The template is safe to commit provided the vault and item names themselves reveal nothing sensitive. It is still worth considering whether those names expose internal systems or customer relationships.
Headless use
Interactive login works well on a workstation:
pass-cli login
For a server or scheduled job, Proton supports expiring personal access tokens with explicit grants. A token begins with no resource access; a vault or individual item must be granted to it with a viewer, editor, or manager role.
That makes it possible to give one job read access to one credential instead of giving a server the same vault access as a human account. Supply the token through the environment when logging in rather than placing it directly in shell history.
Optional caching on Linux
Vault access adds latency. For a frequently launched local tool, the Linux user keyring can act as a short-lived memory-only cache:
get_cached() {
local id
id=$(keyctl search @u user "$1" 2>/dev/null) || return 0
keyctl pipe "$id" 2>/dev/null
}
cache_value() {
local id
id=$(keyctl add user "$1" "$2" @u)
keyctl timeout "$id" 3600
}
The wrapper can check the keyring first, retrieve from Proton Pass on a miss, and apply a short expiry. This avoids writing a cache file, but it changes the security model: for the duration of the timeout, another process running as the same user may be able to read the key. I use this only where the launch delay justifies that trade-off.
The remaining sharp edges
Moving a secret out of a file does not make it disappear:
- The launched process receives the value and can log or leak it.
- Environment variables may be observable through debugging and process-inspection facilities, depending on the operating system.
- Generated configuration files must be protected and removed when no longer needed.
- Vault and item names can themselves disclose context.
- A personal access token is also a secret and needs its own secure delivery path.
The useful improvement is narrower custody. Git holds the repeatable procedure, Proton Pass holds the durable credential, and the application sees the value only while it is doing the work.