@sidshaytay/pi-secret-safe0.2.1
Pi extension that keeps secrets out of model context, provider requests, tool output, and session history.
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
@sidshaytay/pi-secret-safe
A pi extension that keeps secret values out of model context, the outbound provider request, tool output, and session history — while still letting the agent use those secrets in bash commands.
What it does
pi-secret-safe discovers secret values from environment variables and dotenv files, replaces them with placeholders the model can see, and exports the real values into the shell environment. The model reads and writes the placeholder; when a command runs, the shell substitutes the real value, so the secret never enters the conversation.
The model sees a placeholder like this:
«Redacted — read as environment variable $NAME via script»
NAME is the secret's variable name — OPENAI_API_KEY, DB_PASSWORD, or a
generated SECRET_* name for values matched by a pattern. The model references
$OPENAI_API_KEY in a bash command, and the shell expands it at run time.
The placeholder avoids quotes, backslashes, colons, and equals signs, so it
stays valid when it lands inside a JSON, YAML, or TOML string value.
Short values found in URL credentials are hidden in URLs, but not in unrelated
words that happen to contain the same letters.
Quick start
Install the package:
pi install npm:@sidshaytay/pi-secret-safe
Load it from a local checkout instead:
pi -e ./packages/pi-secret-safe/extensions/index.ts
If you edit the extension source while pi is running, run /reload in pi to
pick up the changes.
Put the secrets you want protected in a dotenv file in your project, or in the environment before you start pi:
# .env
OPENAI_API_KEY=sk-...
DATABASE_URL=postgres://user:password@host/db
Then check what it found:
/secret-safe
Usage
Modes
| Mode | Behavior |
|---|---|
redact (default) |
Redact secrets everywhere. Block nothing. |
strict |
Redact, and additionally block reads, writes, edits, and greps of sensitive paths, sensitive bash commands, and bash commands or write/edit content carrying secret literals. When a UI is available and promptOnBlock is true, you are asked to allow once or deny. |
off |
The extension does nothing for the session. |
Switch modes with a command, or set the default in a config file.
Commands
| Command | Does |
|---|---|
/secret-safe |
Same as /secret-safe status |
/secret-safe status |
Show mode, protected secret count, placeholder count, redaction and block counters, the loaded config files, and any config errors |
/secret-safe strict |
Switch to strict mode |
/secret-safe redact |
Switch to redact mode |
/secret-safe off |
Disable for this session |
/secret-safe rescan |
Re-read dotenv files and environment |
Configuration
pi-secret-safe reads JSON from two files, lowest precedence first:
~/.pi/agent/secret-safe.json<project>/.pi/secret-safe.json
The array keys patterns, allowPaths, blockPaths, and extraBlockCommands
accumulate across both files. Every other key, including dotenvFiles,
overrides: the project file wins over the user file. Invalid values fall back to
their defaults and are reported by /secret-safe status.
| Key | Default | Meaning |
|---|---|---|
mode |
"redact" |
"redact", "strict", or "off" |
dotenvFiles |
[".env", ".env.local", ".env.development", ".env.development.local"] |
Dotenv filenames to read, relative to the session directory |
patterns |
[] |
Extra regex rules for secret formats the built-in table does not cover |
allowPaths |
[] |
Strict mode: paths that are always allowed, overriding built-in rules |
blockPaths |
[] |
Strict mode: paths that are always blocked, overriding built-in rules |
extraBlockCommands |
[] |
Strict mode: extra regex sources that make a bash command blockable |
scanEnv |
true |
Discover secrets from the process environment |
exportToShell |
true |
Export discovered secrets into the shell environment so bash can reference them by name. When false, secrets are still redacted, but the placeholder's $NAME form will not expand in bash, so the agent cannot use those values |
minValueLength |
8 |
Minimum value length for a name-matched env or dotenv value to count as a secret |
promptOnBlock |
true |
In strict mode, ask before denying when a UI is available |
Each patterns item is an object with name, regex, and an optional flags:
{
"patterns": [
{ "name": "INTERNAL_TOKEN", "regex": "tok_[a-z0-9]{24}" }
]
}
Example project config:
{
"mode": "strict",
"dotenvFiles": [".env", ".env.local"],
"allowPaths": ["docs/fixtures/**"],
"extraBlockCommands": ["vault\\s+read"]
}
Troubleshooting
The agent can't use a secret in bash.
The value is exported into the shell environment by its variable name, not
inlined into the command. If exportToShell is false, set it back to true
in secret-safe.json and run /secret-safe rescan. The agent must reference
$OPENAI_API_KEY, not the literal value — a literal value in a command cannot
be expanded, and in strict mode it is blocked as a secret literal.
Strict mode blocks something it shouldn't.
Turn the extension off for the session with /secret-safe off. To keep strict
mode, add the path to allowPaths, or refine detection with a patterns entry.
Config isn't loading.
Run /secret-safe and check the path it prints. Confirm the file is at
~/.pi/agent/secret-safe.json or <project>/.pi/secret-safe.json, that its
JSON parses, and that each key uses the type in the table above. Invalid values
fall back to defaults and appear in the /secret-safe output.
What it does not protect
- Detection is heuristic. It matches exact values plus a pattern table. An unusual secret format that is neither in the environment or dotenv files nor matched by a pattern can pass through.
- Image content is never redacted.
- Files on disk are unchanged. Secrets written into files the agent reads are redacted at read time, but the file still holds the real value. Rotating a leaked credential is the only real remedy.
- Nothing outside the session. It does not manage pi debug logs, terminal scrollback, or shell history.
- Blocking is a guard, not proof. Strict mode blocks operations and redact mode rewrites content. Neither proves a secret never existed.
- A user-run command is stored as typed. A
!or!!command is recorded in session history exactly as entered, so a secret typed literally into the command itself is stored, while its output is redacted as it is produced. - User command output redaction is line-buffered. A secret that spans multiple lines and is not recognised as a private-key block can partially survive in the session file.
- Other extensions can write ahead of us. A message injected by another
extension through pi's
sendMessageAPI is written to the session file before this extension can inspect it. - A thrown redaction hook fails open. pi logs the error and the original content continues unredacted.
- Generic assignment rules stop at punctuation. They end at
#,,,;,}, and], so when a value is not already known from the environment or a dotenv file, a tail after one of those characters can stay visible.
How it hooks into pi
pi-secret-safe registers these hooks:
| Hook | Runs when | Does |
|---|---|---|
session_start |
A session starts | Load config, discover secrets, refresh the redactor, export secrets to the shell environment |
before_agent_start |
Before the agent starts | Append system-prompt guidance explaining placeholders and safe $VAR usage |
input |
User input arrives | Redact secret values before they enter the conversation |
context |
Before each model request | Redact every conversation message |
before_provider_request |
Before the request leaves | Redact the final provider payload as a last line of defence |
tool_result |
A tool returns | Redact tool output, including SSH config metadata and private keys that arrive in chunks |
message_end |
A message is finalized | Redact the message so session history on disk is scrubbed |
tool_call |
Before a tool runs | In strict mode, block reads, writes, edits, and greps of sensitive paths and calls whose command or content carries a secret literal |
user_bash |
Before a user bash command runs | In strict mode, block commands that would expose secrets; otherwise redact the command's output as it streams, before pi records it |
Credits
pi-secret-safe merges ideas and code adapted from several MIT-licensed projects. See CREDITS.md.