Skip to content

Commit 0f87f2c

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

15 files changed

Lines changed: 2102 additions & 1 deletion

File tree

.changeset/add-pi-enclave.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"pi-enclave": minor
3+
---
4+
5+
Initial release of pi-enclave: VM-isolated sandbox with automatic secret protection.
6+
7+
- All tools (bash, read, write, edit) execute inside a Gondolin micro-VM
8+
- Auto-detects `gh auth token` and provisions secrets with proxy-based injection
9+
- Cascading TOML config with `enabled` flag, per-host HTTP method+path policies
10+
- `/enclave init` creates project and global config, `/enclave on|off` for session toggle
11+
- `/enclave add` with interactive package search and config persistence
12+
- Zero-config: just `enabled = true` and secrets are protected immediately

packages/enclave/README.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# pi-enclave
2+
3+
> From [yapp](https://github.com/mgabor3141/yapp) · yet another pi pack
4+
5+
VM-isolated sandbox 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**. Credential tools on the host (like `gh auth token`) are auto-detected, 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+
After installing, run `/enclave init` in any project to enable it:
38+
39+
```
40+
/enclave init
41+
```
42+
43+
This creates two files:
44+
- `~/.pi/agent/extensions/pi-enclave.toml` — global defaults (commented out template)
45+
- `.pi/enclave.toml` — project config with `enabled = true`
46+
47+
Once enabled, all tools (bash, read, write, edit) execute inside the VM automatically.
48+
49+
## Zero-config behavior
50+
51+
With `enabled = true` and no other configuration, pi-enclave:
52+
53+
1. Starts a VM, mounts your project directory
54+
2. Installs `git`, `curl`, `jq`
55+
3. Auto-detects `gh auth token` and provisions `GH_TOKEN` scoped to GitHub hosts
56+
4. Allows all outbound HTTP traffic
57+
5. Redirects all tool execution to the VM
58+
59+
## Configuration
60+
61+
Place `.pi/enclave.toml` in your project (or any ancestor directory).
62+
63+
```toml
64+
# Enable VM isolation for this project
65+
enabled = true
66+
67+
# Stop walking up the directory tree
68+
# root = true
69+
70+
# Alpine packages to install
71+
# packages = ["git", "curl", "jq", "ripgrep", "github-cli"]
72+
73+
[secrets]
74+
# Source from a host command
75+
# GH_TOKEN = { command = "gh auth token", hosts = ["api.github.com", "github.com"] }
76+
77+
# Source from a host environment variable
78+
# OPENAI_API_KEY = { env = "OPENAI_API_KEY", hosts = ["api.openai.com"] }
79+
80+
# Disable an auto-detected provider
81+
# GH_TOKEN = false
82+
83+
[network]
84+
# Restrict outbound access (unset = allow all)
85+
# allow-hosts = ["api.github.com", "github.com"]
86+
87+
[policy.api-github-com]
88+
# Per-host HTTP method + path rules (section name = hostname, dots → dashes)
89+
# GET = ["*"]
90+
# POST = ["/graphql", "/repos/*/*/pulls"]
91+
# deny = ["/repos/*/*/pulls/*/merge"]
92+
# unmatched = "prompt" # "prompt" (default) | "deny" | "allow"
93+
```
94+
95+
Config files cascade: global (`~/.pi/agent/extensions/pi-enclave.toml`) is loaded first, then `.pi/enclave.toml` files from ancestor directories (closest wins). Config inside the workspace is protected from modification by the VM via Gondolin's `ShadowProvider`.
96+
97+
## Commands
98+
99+
| Command | Description |
100+
|---------|-------------|
101+
| `/enclave` or `/enclave status` | Show VM state, packages, secrets, network policy |
102+
| `/enclave init` | Create project and global config files, enable enclave |
103+
| `/enclave on` | Enable VM isolation for this session |
104+
| `/enclave off` | Disable VM isolation for this session (shuts down VM) |
105+
| `/enclave restart` | Restart VM on next tool use |
106+
| `/enclave add <package>` | Search for and install an Alpine package |

packages/enclave/package.json

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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+
"README.md"
28+
],
29+
"scripts": {
30+
"build": "tsup"
31+
},
32+
"pi": {
33+
"extensions": [
34+
"dist/index.js"
35+
]
36+
},
37+
"dependencies": {
38+
"@earendil-works/gondolin": "^0.6.0",
39+
"smol-toml": "^1.6.0",
40+
"valibot": "^1.2.0"
41+
},
42+
"peerDependencies": {
43+
"@mariozechner/pi-coding-agent": "*"
44+
},
45+
"devDependencies": {
46+
"@mariozechner/pi-coding-agent": "^0.57.0",
47+
"@types/node": "^25.3.5",
48+
"tsup": "^8.5.1",
49+
"typescript": "^5.9.3"
50+
}
51+
}

0 commit comments

Comments
 (0)