Skip to content

Commit 830a91f

Browse files
committed
feat: add pi-enclave VM sandbox with secret protection
1 parent d03b1db commit 830a91f

24 files changed

Lines changed: 3103 additions & 3 deletions

.changeset/add-pi-enclave.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
"pi-enclave": minor
3+
---
4+
5+
Initial release of pi-enclave: VM-isolated enclave with automatic secret protection.
6+
7+
- All tools (bash, read, write, edit) execute inside a Gondolin micro-VM
8+
- Modular drop-in config files (`pi-enclave.d/`): git, jj, GitHub ship by default
9+
- Generic `[env]` section: inject host values (static, command, env var) into the VM
10+
- `setup` scripts per drop-in: raw shell, run after package install
11+
- Secrets with HTTP proxy injection (never enter the VM)
12+
- `[[git-credentials]]` for explicit git auth over HTTPS
13+
- Per-host HTTP policies with allow/deny/prompt for method+path patterns
14+
- GraphQL-aware policy: parses request bodies, checks actual field names (not spoofable operation names)
15+
- `[[mounts]]` for additional directories (e.g. jj workspace parent repos)
16+
- Cascading config: global + drop-ins + ancestor directories + project
17+
- Packages accumulate additively across all config layers
18+
- `/enclave init`, `/enclave on|off`, `/enclave add` with interactive search
19+
- Eager VM startup on session start/resume

README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,20 @@
22

33
***y**et **a**nother **p**i **p**ack*
44

5-
Utilities for running [pi](https://pi.dev) agents with less babysitting: auto-review risky shell commands, compress noisy terminal output before it bloats context, and get notified when unattended work finishes.
5+
Utilities for running [pi](https://pi.dev) agents with less babysitting: sandbox tools in a VM, auto-review risky shell commands, compress noisy terminal output before it bloats context, and get notified when unattended work finishes.
66

77
## The Pack
88

99
Install the extensions together, or pick only the ones you want. Defaults are tuned for good behavior out of the box.
1010

1111
```bash
12-
pi install npm:pi-safeguard npm:pi-bash-trim npm:pi-desktop-notify npm:pi-no-soft-cursor
12+
pi install npm:pi-enclave npm:pi-safeguard npm:pi-bash-trim npm:pi-desktop-notify npm:pi-no-soft-cursor
1313
```
1414

15+
### [pi-enclave](packages/enclave/)
16+
17+
VM-isolated sandbox. All agent tools (bash, read, write, edit) execute inside a Gondolin micro-VM (QEMU/aarch64 Alpine Linux). Secrets are resolved on the host and injected via HTTP proxy; the VM never sees real credential values. Per-host policies control what the agent can do: GraphQL mutations are checked at the AST level, git pushes require approval, reads pass through. Configuration is modular: drop-in files in `pi-enclave.d/` add tool support (git, jj, GitHub) with their own packages, setup scripts, and policies. Delete a file to disable that integration.
18+
1519
### [pi-safeguard](packages/safeguard/)
1620

1721
LLM-as-judge guardrail. Every bash command the agent runs is parsed into an AST and checked against known-dangerous patterns — `rm -rf`, reading `.env` files, `curl` with secret variables, `sudo`, and more. Flagged commands go to a smaller model that evaluates them with the full parsed context — structured AST, recent tool history, and user trust directives. The tight scope makes even lightweight models very reliable here. Most dev commands pass silently; you're only interrupted when there's genuine ambiguity.

packages/enclave/README.md

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
# pi-enclave
2+
3+
> From [yapp](https://github.com/mgabor3141/yapp) · yet another pi pack
4+
5+
VM-isolated enclave for [pi](https://pi.dev). Runs all tools inside a [Gondolin](https://github.com/earendil-works/gondolin) micro-VM so secrets never enter the agent's execution environment.
6+
7+
```bash
8+
pi install npm:pi-enclave
9+
```
10+
11+
Requires QEMU: `brew install qemu` (macOS) or `sudo apt install qemu-system-aarch64` (Linux).
12+
13+
## How it works
14+
15+
pi-enclave starts an Alpine Linux micro-VM (QEMU/aarch64) and redirects all tool execution into it. Your workspace is mounted read-write at the same path inside the VM, so tools see identical paths on host and guest. File changes are bidirectional.
16+
17+
The core security property: **secrets never enter the VM**. Secrets configured in your TOML config (like `gh auth token`) are resolved on the host, and their values are replaced with random placeholders inside the VM. Gondolin's HTTP proxy substitutes real values on the wire, only for requests to configured hosts.
18+
19+
```
20+
┌──────────────────────────────────────────────────┐
21+
│ Gondolin VM (Alpine Linux) │
22+
│ │
23+
│ /home/user/project ← bidirectional mount │
24+
│ GH_TOKEN = "GONDOLIN_SECRET_a8f3..." (placeholder)│
25+
│ All pi tools execute here │
26+
└────────────────────┬─────────────────────────────┘
27+
│ HTTP
28+
29+
┌──────────────────────────────────────────────────┐
30+
│ HTTP proxy (host-side) │
31+
│ placeholder → real value (only for allowed hosts)│
32+
└──────────────────────────────────────────────────┘
33+
```
34+
35+
## Getting started
36+
37+
```
38+
/enclave init
39+
```
40+
41+
This creates:
42+
- `~/.pi/agent/extensions/pi-enclave.toml` — global config (env vars, base packages)
43+
- `~/.pi/agent/extensions/pi-enclave.d/` — drop-in files (git, jj, GitHub)
44+
- `.pi/enclave.toml` — project config with `enabled = true`
45+
46+
Once enabled, all tools (bash, read, write, edit) execute inside the VM automatically.
47+
48+
## Drop-in files
49+
50+
Service integrations live in `pi-enclave.d/` as self-contained TOML files. Each can contribute packages, setup scripts, secrets, and host policies. Delete a file to disable that integration.
51+
52+
```
53+
~/.pi/agent/extensions/
54+
├── pi-enclave.toml # base config: curl, jq, env vars
55+
└── pi-enclave.d/
56+
├── git.toml # git + user identity
57+
├── github.toml # github-cli + secrets + policies
58+
└── jj.toml # jujutsu + user identity
59+
```
60+
61+
Example drop-in (`git.toml`):
62+
63+
```toml
64+
packages = ["git"]
65+
setup = """
66+
git config --global safe.directory '*'
67+
git config --global user.name "$USER_NAME"
68+
git config --global user.email "$USER_EMAIL"
69+
"""
70+
```
71+
72+
`USER_NAME` and `USER_EMAIL` are defined in the main config as env vars resolved from the host:
73+
74+
```toml
75+
[env]
76+
USER_NAME = { command = "git config --global user.name" }
77+
USER_EMAIL = { command = "git config --global user.email" }
78+
```
79+
80+
## Configuration
81+
82+
### Env vars
83+
84+
Non-secret values available in the VM and setup scripts. Three source types:
85+
86+
```toml
87+
[env]
88+
EDITOR = "vim" # static
89+
USER_NAME = { command = "git config user.name" } # host command
90+
GOPATH = { env = "GOPATH" } # host env var
91+
```
92+
93+
### Secrets
94+
95+
Like env vars, but values never enter the VM. The HTTP proxy injects them on the wire.
96+
97+
```toml
98+
[secrets.GH_TOKEN]
99+
command = "gh auth token"
100+
hosts = ["api.github.com", "github.com", "*.githubusercontent.com"]
101+
```
102+
103+
### Git credentials
104+
105+
Configures git credential helpers using secret placeholders:
106+
107+
```toml
108+
[[git-credentials]]
109+
host = "github.com"
110+
username = "x-access-token"
111+
secret = "GH_TOKEN"
112+
```
113+
114+
### Host policies
115+
116+
Access control per host. `unmatched` determines what happens to requests that don't match any allow/deny rule.
117+
118+
```toml
119+
[hosts."api.github.com"]
120+
unmatched = "prompt"
121+
allow.GET = ["/**"]
122+
123+
[hosts."api.github.com".graphql]
124+
endpoint = "/graphql"
125+
allow.query = ["*"]
126+
allow.mutation = ["createPullRequest", "createIssue", "addComment"]
127+
```
128+
129+
GraphQL policy parses the request body and checks actual field names (not the spoofable operation name).
130+
131+
### Mounts
132+
133+
Additional directories to mount in the VM (e.g. for jj workspaces):
134+
135+
```toml
136+
[[mounts]]
137+
path = "~/dev/myproject/.jj"
138+
```
139+
140+
### Project overrides
141+
142+
Project configs override global. Packages accumulate; secrets, hosts, and env merge by key (later wins).
143+
144+
```toml
145+
# .pi/enclave.toml — allow all GitHub operations in this project
146+
enabled = true
147+
148+
[hosts."api.github.com"]
149+
unmatched = "allow"
150+
```
151+
152+
## Commands
153+
154+
| Command | Description |
155+
|---------|-------------|
156+
| `/enclave` or `/enclave status` | Show VM state, packages, secrets |
157+
| `/enclave init` | Create project and global config files, enable enclave |
158+
| `/enclave on` | Enable VM isolation for this session |
159+
| `/enclave off` | Disable VM isolation for this session (shuts down VM) |
160+
| `/enclave restart` | Restart VM on next tool use |
161+
| `/enclave add <package>` | Search for and install an Alpine package |

packages/enclave/package.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"name": "pi-enclave",
3+
"version": "0.0.1",
4+
"description": "VM-isolated sandbox for pi with automatic secret protection · from yapp",
5+
"author": "mgabor3141",
6+
"license": "MIT",
7+
"repository": {
8+
"url": "git+https://github.com/mgabor3141/yapp.git",
9+
"directory": "packages/enclave"
10+
},
11+
"keywords": [
12+
"pi-package",
13+
"pi-extension",
14+
"yapp"
15+
],
16+
"type": "module",
17+
"main": "dist/index.js",
18+
"types": "dist/index.d.ts",
19+
"exports": {
20+
".": {
21+
"import": "./dist/index.js",
22+
"types": "./dist/index.d.ts"
23+
}
24+
},
25+
"files": [
26+
"dist",
27+
"templates",
28+
"README.md"
29+
],
30+
"scripts": {
31+
"build": "tsup"
32+
},
33+
"pi": {
34+
"extensions": [
35+
"dist/index.js"
36+
]
37+
},
38+
"dependencies": {
39+
"@earendil-works/gondolin": "^0.6.0",
40+
"graphql": "^16.13.1",
41+
"smol-toml": "^1.6.0",
42+
"valibot": "^1.2.0"
43+
},
44+
"peerDependencies": {
45+
"@mariozechner/pi-coding-agent": "*"
46+
},
47+
"devDependencies": {
48+
"@mariozechner/pi-coding-agent": "^0.57.0",
49+
"@types/node": "^25.3.5",
50+
"tsup": "^8.5.1",
51+
"typescript": "^5.9.3"
52+
}
53+
}

0 commit comments

Comments
 (0)