Skip to content

Commit ffd8366

Browse files
committed
docs: expand MCP documentation with config options, auth warnings, naming, and base security notes
1 parent f433fa6 commit ffd8366

1 file changed

Lines changed: 70 additions & 5 deletions

File tree

README.md

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,25 @@ Create `~/.config/pixels/config.toml`:
295295
#
296296
# Session-only literal — passed to console/exec but NOT written to /etc/environment:
297297
# MY_VAR = { value = "some-value", session_only = true }
298+
299+
[mcp]
300+
# prefix = "mcp-" # name prefix for MCP-spawned sandboxes (final: px-mcp-<hex>)
301+
# base_prefix = "base-" # name prefix for bases (final: px-base-<name>)
302+
# default_image = "" # falls back to defaults.image when empty
303+
# listen_addr = "127.0.0.1:8765"
304+
# endpoint_path = "/mcp"
305+
# idle_stop_after = "1h" # stop sandboxes idle for this long
306+
# hard_destroy_after = "24h" # destroy sandboxes older than this
307+
# reap_interval = "1m" # how often the reaper checks lifetimes
308+
# exec_timeout_max = "10m" # ceiling for any single MCP exec call
309+
# state_file = "" # default: $XDG_CACHE_HOME/pixels/mcp-state.json
310+
# pid_file = "" # default: $XDG_CACHE_HOME/pixels/mcp.pid
311+
312+
# Declare extra bases (dev/python/node ship built in):
313+
# [mcp.bases.rust]
314+
# parent_image = "ubuntu/24.04"
315+
# setup_script = "~/.config/pixels/bases/rust.sh"
316+
# description = "Rust toolchain"
298317
```
299318

300319
### Priority Order
@@ -327,9 +346,28 @@ Create `~/.config/pixels/config.toml`:
327346
| `PIXELS_PROVISION_ENABLED` | `provision.enabled` |
328347
| `PIXELS_PROVISION_DEVTOOLS` | `provision.devtools` |
329348
| `PIXELS_NETWORK_EGRESS` | `network.egress` |
349+
| `PIXELS_MCP_PREFIX` | `mcp.prefix` |
350+
| `PIXELS_MCP_BASE_PREFIX` | `mcp.base_prefix` |
351+
| `PIXELS_MCP_DEFAULT_IMAGE` | `mcp.default_image` |
352+
| `PIXELS_MCP_LISTEN_ADDR` | `mcp.listen_addr` |
353+
| `PIXELS_MCP_ENDPOINT_PATH` | `mcp.endpoint_path` |
354+
| `PIXELS_MCP_IDLE_STOP_AFTER` | `mcp.idle_stop_after` |
355+
| `PIXELS_MCP_HARD_DESTROY_AFTER` | `mcp.hard_destroy_after` |
356+
| `PIXELS_MCP_REAP_INTERVAL` | `mcp.reap_interval` |
357+
| `PIXELS_MCP_EXEC_TIMEOUT_MAX` | `mcp.exec_timeout_max` |
358+
| `PIXELS_MCP_STATE_FILE` | `mcp.state_file` |
359+
| `PIXELS_MCP_PID_FILE` | `mcp.pid_file` |
330360

331361
## Using `pixels` as an MCP code-sandbox server
332362

363+
> **Alpha.** Lifecycle and the tool surface are stable enough to build
364+
> against. Sandbox security for the MCP path is not yet aligned with
365+
> `pixels create`: sandboxes don't get per-call egress policies, and
366+
> base clones inherit whatever was baked in at build time. Egress
367+
> and the rest of the `pixels create` hardening are coming soon.
368+
> For now, I'd treat an MCP-spawned sandbox as if you'd run
369+
> `pixels create --egress unrestricted`.
370+
333371
`pixels mcp` runs a streamable-HTTP MCP server that exposes container
334372
lifecycle, exec, and file CRUD as MCP tools. Run it once on your
335373
machine, then point any number of MCP clients at it.
@@ -342,6 +380,12 @@ By default it binds to `http://127.0.0.1:8765/mcp` and refuses to start
342380
if another instance is already running (PID file at
343381
`~/.cache/pixels/mcp.pid`).
344382

383+
The server has no auth. Keep it on loopback, or put it behind a
384+
reverse proxy (Caddy, nginx, Traefik) that handles auth for you.
385+
If you bind it to a non-loopback address with no proxy in front,
386+
anything that can reach the port can run `exec` in any of your
387+
sandboxes.
388+
345389
### Configure your client
346390

347391
Claude Code MCP entry:
@@ -370,6 +414,20 @@ Claude Code MCP entry:
370414
| `delete_file` | Remove a file |
371415
| `list_files` | List directory contents (optionally recursive) |
372416

417+
### Container names
418+
419+
Both backends prepend `px-` to every instance. The MCP daemon
420+
prepends its own prefix on top of that, so:
421+
422+
- MCP-spawned sandboxes land at `px-mcp-<hex>` (`mcp.prefix` default
423+
is `mcp-`).
424+
- Bases land at `px-base-<name>` (`mcp.base_prefix` default is
425+
`base-`).
426+
427+
That's why CLI examples like `pixels checkpoint create px-base-python`
428+
use the full on-disk name. `create_sandbox` returns the full name in
429+
its response.
430+
373431
### Base pixels
374432

375433
A base is a container that sandboxes clone from. Bases are declared in
@@ -402,6 +460,11 @@ description = "Rust toolchain + dev tools"
402460
Bases form a DAG via `from`. Cycle / missing-dep / both-set / neither-set
403461
are rejected at config load.
404462

463+
**Setup scripts run as root with the `pixels create` hardening absent.**
464+
I treat them like Docker `RUN` lines: only build a base from a script
465+
you wrote or reviewed. The egress, `safe-apt`, and restricted-sudoers
466+
wiring from `pixels create` isn't applied during base build.
467+
405468
Customise a base by mutating its container:
406469

407470
```bash
@@ -414,11 +477,13 @@ pixels checkpoint create px-base-python
414477
The next `create_sandbox(base="python")` call clones from the new
415478
checkpoint. Existing sandboxes are unaffected (independent containers).
416479

417-
**Checkpoint-advances-clone-source.** Any `pixels checkpoint create px-base-X`
418-
immediately advances the snapshot that future sandboxes clone from. If you take
419-
a *safety* checkpoint before mutating, new sandboxes will clone from that
420-
pre-mutation state until you take another checkpoint *after* the changes. Always
421-
re-checkpoint after mutating to ensure new clones pick up your changes.
480+
**Checkpoint-advances-clone-source.** Future sandboxes clone from the
481+
most recent checkpoint by creation time, not by label. So any
482+
`pixels checkpoint create px-base-X` immediately advances the clone
483+
source. If you take a *safety* checkpoint before mutating, new sandboxes
484+
clone from that pre-mutation state until you take another checkpoint
485+
*after* the changes. Always re-checkpoint after mutating to ensure new
486+
clones pick up your changes.
422487

423488
**Mutation-propagation gotcha.** Changes to `dev` do NOT auto-flow into
424489
`python` or `node`. Both are independent containers built when `dev` was

0 commit comments

Comments
 (0)