231 lines
7.0 KiB
Markdown
231 lines
7.0 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**
|
|
- 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
|
|
{
|
|
"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
|
|
{
|
|
"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
|
|
|
|
### Allow
|
|
Low-risk reads and normal project-local writes are allowed.
|
|
|
|
### Confirm
|
|
Potentially dangerous but legitimate actions open a modal with **Allow once / Block**. Before showing it, the currently selected Pi model receives the proposed command and up to three recent, related tool commands from the active session. The dialog displays a short description, the model's rationale, and a one-word risk label (`BASSO`, `MEDIO`, `ALTO`, or `CRITICO`). Common inline credentials and URL user-info are redacted from the analysis request. If no model is active or analysis fails, the dialog says so and labels risk `SCONOSCIUTO`; it still requires an explicit user choice.
|
|
|
|
The command and selected prior commands are sent to the configured current-model provider for analysis. Do not use this feature with a provider you do not trust for that data.
|
|
|
|
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` — show current 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.
|