Files
pi-policy-gate/README.md
T

237 lines
7.7 KiB
Markdown

# @enne2/pi-policy-gate
A standalone **pi** extension package that applies a practical policy layer to risky tool calls.
It classifies operations into four outcomes:
- **allow** — execute immediately
- **confirm** — require human approval
- **deny** — block outright
- **refine** — block and tell the model how to reformulate the command safely
The default policy is opinionated but usable, and it is now **baked into the code** even if you provide no config file.
Built-in defaults:
- `sudo` and `ssh` are **not denied by default**
- risky commands are generally **confirmed**
- only objectively dangerous or ambiguous forms are **denied/refined**
- destructive deletes should use an **explicit absolute path**
- `curl` / `wget` / `nc` / `socat` / `telnet` are **confirmed**
- `env` / `printenv` are **confirmed**
- bash-based writes to sensitive files such as `AGENTS.md`, `.env`, shell dotfiles, SSH config, and local policy files are **confirmed**
- all `bash` / `read` / `write` / `edit` calls are analyzed by the active Pi model unless deterministic policy blocks/refines them first
- the default minimum LLM risk requiring user confirmation is `ALTO`; configure it interactively with `/policy-gate`
- programmatically generated local **alert sounds** play on confirmation requests and blocks/refinements by default
## What it protects against
Examples of behavior it catches by default:
- `rm -rf *`
- `rm -rf .`
- `rm -rf ..`
- `rm -rf /`
- `find . -delete`
- `sudo ...` (confirmation)
- `ssh ...` / `scp` / `rsync` / `sftp` (confirmation)
- `curl ...`, `wget ...`, `nc ...`, `socat ...` (confirmation)
- `env`, `printenv` (confirmation)
- `git reset --hard`, `git clean -fdx`, `git push` (confirmation)
- `sed -i ~/.bashrc ...` or `tee AGENTS.md` (confirmation)
- writes outside the workspace (confirmation)
- reads/writes to sensitive paths such as `~/.ssh`, `AGENTS.md`, or `.env` (confirmation)
## Install
### Directly in pi from git
Private Gitea/GitHub style install:
```bash
pi install 'ssh://git@git.enne2.net:222/enne2/pi-policy-gate.git'
```
Or with HTTPS:
```bash
pi install https://git.enne2.net/enne2/pi-policy-gate.git
```
### With npm from the git repository
```bash
npm install git+ssh://git@git.enne2.net:222/enne2/pi-policy-gate.git
```
Then load it from local `node_modules` or publish it to your npm registry later.
## Sound alerts
The package ships with three pre-generated WAV files and the extension only plays them at runtime:
- `confirm.wav` — soft ascending alert for human confirmation
- `block.wav` — lower, harsher alert for denied actions
- `refine.wav` — distinct mid-tone alert for “blocked, but reformulate safely” cases
By default it plays the bundled files from the package `assets/` directory, so it does **not** write audio files into your agent home at startup.
It automatically uses the first available local player among:
- `paplay`
- `pw-play`
- `aplay`
- `ffplay`
- `play`
You can also test them manually inside pi:
- `/policy-gate-sound`
- `/policy-gate-sound block`
- `/policy-gate-sound refine`
## Config files
The extension looks for config files in this order:
1. built-in defaults
2. `~/.pi/agent/policy-gate.json`
3. extra file passed with `--policy-gate-config /path/to/file.json`
4. `.pi/policy-gate.json` in the current project
Project config overrides global config.
## Example config
Copy and adjust:
```bash
cp policy-gate.example.json ~/.pi/agent/policy-gate.json
```
Example:
```json
{
"minimumRiskToConfirm": "ALTO",
"workspaceRoots": ["."],
"requireAbsolutePathForRecursiveDelete": true,
"soundEnabled": true,
"soundConfirmEnabled": true,
"soundBlockEnabled": true,
"soundRefineEnabled": true,
"soundPlayer": "auto",
"overrides": [
{
"tool": "bash",
"commandRegex": "^sudo systemctl restart my-safe-service$",
"action": "allow"
}
]
}
```
## Config reference
```json
{
"minimumRiskToConfirm": "ALTO",
"workspaceRoots": ["."],
"requireAbsolutePathForRecursiveDelete": true,
"requireAbsolutePathForFindDelete": true,
"confirmSensitiveReads": true,
"confirmSensitiveWrites": true,
"confirmWritesOutsideWorkspace": true,
"soundEnabled": true,
"soundConfirmEnabled": true,
"soundBlockEnabled": true,
"soundRefineEnabled": true,
"soundPlayer": "auto",
"soundDirectory": "/absolute/path/to/custom/policy-gate-sounds",
"sensitivePathGlobs": ["~/.ssh/**", "**/.env"],
"overrides": [
{
"id": "optional label",
"tool": "bash",
"commandRegex": "^ssh deploy@staging\\b",
"pathGlob": "~/.ssh/**",
"action": "allow | confirm | deny | refine",
"reason": "Shown to the user / model",
"suggest": "Only used with refine/deny to explain the safer alternative"
}
]
}
```
### Notes
- `soundPlayer: "auto"` picks the first available local player.
- Set `soundEnabled: false` to disable all alert sounds.
- `soundConfirmEnabled`, `soundBlockEnabled`, and `soundRefineEnabled` let you mute just one class of event.
- `soundDirectory` is optional: by default the extension uses the package's bundled `assets/` directory. If you set it, it should point to an existing directory containing `confirm.wav`, `block.wav`, and `refine.wav`.
- `workspaceRoots` can be absolute paths, `~` paths, or paths relative to the current project cwd.
- `commandRegex` is evaluated against the raw bash command string.
- `pathGlob` is matched against both the absolute path and the path relative to the current cwd.
- last matching override wins.
## Behavior model
### LLM risk threshold and confirmation
Every `bash`, `read`, `write`, and `edit` call that is not already denied or refined is analyzed by the active Pi model. The default minimum risk requiring confirmation is `ALTO`; any LLM risk at or above the selected threshold triggers a modal. Existing deterministic confirmation rules still apply regardless of the threshold.
The modal offers **Allow once / Block** and shows a short description, the model's rationale, and a one-word risk label (`BASSO`, `MEDIO`, `ALTO`, or `CRITICO`). If no model is active or analysis fails, the risk is `SCONOSCIUTO` and the operation requires manual confirmation (fail-safe). The threshold is changed from the `/policy-gate` TUI menu and saved to `~/.pi/agent/policy-gate.json`.
Before analysis, the active model receives the proposed command/path and up to three recent, related tool calls from the session. Common inline credentials and URL user-info are redacted. This data is sent to the configured current-model provider; do not use a provider you do not trust for it.
### Confirm
Potentially dangerous but legitimate actions open the same confirmation modal even when the LLM risk is below the selected threshold.
Examples:
- `sudo systemctl restart nginx`
- `ssh deploy@staging 'systemctl status api'`
- `rm -rf /full/path/to/some/build-cache`
### Deny / Refine
Only clearly unsafe or ambiguous forms are blocked.
Examples:
- `rm -rf *`
- `rm -rf .`
- `find . -delete`
The extension tells the model to switch to a safer form such as using an explicit absolute path.
## Useful commands
Inside pi:
- `/policy-gate` — open the TUI menu to set the minimum LLM risk requiring confirmation or view the policy summary
- `/policy-gate-sound` — play the confirmation sound
- `/policy-gate-sound block` — play the deny sound
- `/policy-gate-sound refine` — play the refine sound
## Development
Install deps, regenerate the packaged WAV assets if needed, and verify the tarball:
```bash
npm install
npm run generate:sounds
npm run pack:check
```
Try it without installing globally:
```bash
pi -e /absolute/path/to/pi-policy-gate --list-models
```
## Packaging notes
This package is intentionally TypeScript-only and relies on pi's built-in runtime loader.
No build step is required.