|
| 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**. 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 | +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 (GitHub policy, secrets) |
| 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 | +## Default behavior |
| 50 | + |
| 51 | +With `/enclave init`, pi-enclave creates a global config with sensible defaults: |
| 52 | + |
| 53 | +1. Starts a VM, mounts your project directory |
| 54 | +2. Installs `git`, `curl`, `jq`, `github-cli` |
| 55 | +3. Resolves `GH_TOKEN` via `gh auth token` (skipped if `gh` is not installed) |
| 56 | +4. GitHub API policy: GET and GraphQL queries are allowed, mutations and other writes prompt for approval |
| 57 | +5. GraphQL policy parses the request body and checks actual field names (not the spoofable operation name) |
| 58 | +6. Redirects all tool execution to the VM |
| 59 | + |
| 60 | +## Configuration |
| 61 | + |
| 62 | +Place `.pi/enclave.toml` in your project (or any ancestor directory). |
| 63 | + |
| 64 | +```toml |
| 65 | +# Enable VM isolation for this project |
| 66 | +enabled = true |
| 67 | + |
| 68 | +# Alpine packages to install |
| 69 | +# packages = ["git", "curl", "jq", "ripgrep", "github-cli"] |
| 70 | + |
| 71 | +# Secrets: resolved on the host, injected via HTTP proxy |
| 72 | +[secrets.GH_TOKEN] |
| 73 | +command = "gh auth token" |
| 74 | +hosts = ["api.github.com", "github.com", "*.githubusercontent.com"] |
| 75 | + |
| 76 | +# [secrets.OPENAI_API_KEY] |
| 77 | +# env = "OPENAI_API_KEY" |
| 78 | +# hosts = ["api.openai.com"] |
| 79 | + |
| 80 | +# Disable a secret inherited from global config |
| 81 | +# [secrets.GH_TOKEN] |
| 82 | +# false |
| 83 | + |
| 84 | +# Host policies: access control for specific hosts |
| 85 | +[hosts."api.github.com"] |
| 86 | +unmatched = "prompt" |
| 87 | +allow.GET = ["/**"] |
| 88 | + |
| 89 | +# GraphQL: parses request body, checks actual field names |
| 90 | +[hosts."api.github.com".graphql] |
| 91 | +endpoint = "/graphql" |
| 92 | +allow.query = ["*"] |
| 93 | +allow.mutation = [ |
| 94 | + "createPullRequest", |
| 95 | + "createIssue", |
| 96 | + "addComment", |
| 97 | +] |
| 98 | +``` |
| 99 | + |
| 100 | +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`. |
| 101 | + |
| 102 | +### Project overrides |
| 103 | + |
| 104 | +A project can loosen restrictions from the global config: |
| 105 | + |
| 106 | +```toml |
| 107 | +# .pi/enclave.toml |
| 108 | +enabled = true |
| 109 | + |
| 110 | +# Allow all GitHub API operations without prompts |
| 111 | +[hosts."api.github.com"] |
| 112 | +unmatched = "allow" |
| 113 | +``` |
| 114 | + |
| 115 | +## Commands |
| 116 | + |
| 117 | +| Command | Description | |
| 118 | +|---------|-------------| |
| 119 | +| `/enclave` or `/enclave status` | Show VM state, packages, secrets | |
| 120 | +| `/enclave init` | Create project and global config files, enable enclave | |
| 121 | +| `/enclave on` | Enable VM isolation for this session | |
| 122 | +| `/enclave off` | Disable VM isolation for this session (shuts down VM) | |
| 123 | +| `/enclave restart` | Restart VM on next tool use | |
| 124 | +| `/enclave add <package>` | Search for and install an Alpine package | |
0 commit comments