Releases: deevus/pixels
v0.6.2
v0.6.1
The headline is the MCP server. Plus base pixels, SSH host-key verification, and a batch of TrueNAS-backend reliability fixes.
Highlights
pixels mcp — the MCP code-sandbox server. A streamable-HTTP MCP server that exposes container lifecycle, exec, and file CRUD as MCP tools. Run it once on your machine, point any number of MCP clients at it, and your agent gets disposable Linux sandboxes on demand.
Base pixels. Containers that sandboxes clone from. Three ship out of the box (dev, python, node) and you can declare your own with from inheritance. With a built base, a new sandbox is up in ~5s.
SSH host-key verification (TOFU). The TrueNAS backend now pins the host key on first connect and verifies on every reconnect. On by default.
Alpha caveat for MCP. Sandboxes spawned via MCP don't get per-call egress policies yet, and base clones inherit whatever was baked in at build time. Treat an MCP-spawned sandbox like
pixels create --egress unrestricted. Egress and the rest of thepixels createhardening are coming. The server has no auth either, so keep it on loopback or behind a reverse proxy. SeeSECURITY.mdfor the full posture.
What's New
MCP server
pixels mcpruns a streamable-HTTP daemon. Default bindhttp://127.0.0.1:8765/mcp. PID file at~/.cache/pixels/mcp.pidso you can't accidentally start two.- Tools:
create_sandbox,start_sandbox,stop_sandbox,destroy_sandbox,list_sandboxes,list_bases,exec,read_file,write_file,edit_file,delete_file,list_files. create_sandboxis async. Returns immediately withstatus: "provisioning". Polllist_sandboxesuntil status flips torunningorfailed. Failed sandboxes carry anerrorfield.list_sandboxesreconciles in-memory state with the backend on a 15s cache, so a sandbox destroyed out-of-band stops appearing once the cache rolls.- Two TTLs keep things tidy.
idle_stop_after(default 1h) stops idle sandboxes.hard_destroy_after(default 24h) destroys anything older than that. - File ops own files as the
pixeluser so subsequentexeccalls can read and modify them.read_fileandedit_filecap in-memory reads (edit_filerefuses files that would truncate trailing content). [mcp]config section with sane defaults for prefix, listen address, endpoint path, lifetimes, and exec timeout ceiling.
Base pixels
- New CLI verbs:
pixels base list,pixels base build <name>.base buildcascade-builds missing deps with a spinner. - Three default bases shipped embedded in the binary:
dev— Ubuntu 24.04 + git, curl, wget, jq, vim, build-essentialpython—dev+ python3, pip, pipx, venvnode—dev+ Node 22 LTS, npm
frominheritance lets you layer bases. Cycles, missing deps, andparent_image+fromset together are rejected at config load.- Customise a base by mutating its container, then
pixels checkpoint create px-base-<name>. The next clone picks up your changes. Existing sandboxes are independent and unaffected. - Base build scripts hold a
flockon/var/lib/apt/daily_lockfor the duration of the build soapt-dailycan't fire mid-build, and retry on lists-lock contention with a single semantic message per attempt. - NodeSource GPG key fingerprint pinned in
node.sh.
A couple of gotchas worth knowing:
- Checkpoint-advances-clone-source. Future sandboxes clone from the most recent checkpoint by creation time, not by label. Always re-checkpoint after mutating.
- Mutation propagation isn't automatic. Changes to
devdo not flow intopythonornode. Destroy and rebuild dependents to propagate. Same as Docker layered images. - Setup scripts run as root with
pixels createhardening absent. Treat them like DockerRUNlines: only build a base from a script you wrote or reviewed.
mise.toml for sandbox devtools
Sandboxes get a declarative mise.toml written to /home/pixel/.config/mise/config.toml instead of a hand-rolled install script. Devtool versions are explicit and easier to override.
SSH host-key verification
TOFU: first connection trusts and pins, reconnects verify, key changes are detected and re-pinned. On by default; opt out with strict_host_keys = false.
TrueNAS backend reliability
The pixels create flow on TrueNAS gets meaningfully more reliable in this release. Each of these was a real bug that bit me running the tool day-to-day.
Readypolls for IP within the timeout instead of returning single-shot. Cloned containers boot DHCP slowly — 15 to 60 seconds in practice. PreviouslyReadyreturned before the IP appeared, so the next step ran against a container with no network and failed.- Stop timeout bumped from 30s to 120s for post-apt systemd shutdown. Any container that recently ran
apt installcouldn't stop cleanly because systemd took longer than 30s to settle.pixels stopandpixels destroywere unreliable on freshly-provisioned containers. Fixed. StopandCloneFromare now idempotent via a newStopInstanceIfRunninghelper. Retries and orchestrated flows no longer error out on already-stopped containers.zmxinstall uses$(uname -m)soarm64containers actually get a working binary. Without this, devtools provisioning and console persistence were silently broken onarm64hosts.consoleshell-escapes the zmx session name so metacharacters in-s <session>can't be interpreted by the remote shell.
Docs
pixels mcpdocumented end-to-end in the README: config options, naming rules, base pixels, async provisioning, lifetimes.SECURITY.mdgot the MCP server's security posture and known gaps.
Upgrading
mise use -g github:deevus/pixels@v0.6.1
Or go install github.com/deevus/pixels@v0.6.1.
If you're on the TrueNAS backend, this upgrade fixes a pile of reliability bugs around cloned containers, post-apt stops, and arm64 hosts. If you want to try the MCP server, start the daemon and point Claude Code at it. That's it.
Full Changelog
v0.5.0
Highlights
Native Incus backend (now the default). Connect directly to a local or remote Incus daemon — no TrueNAS required. Console and exec go through the Incus WebSocket API, eliminating the SSH dependency for the default workflow.
Extensible sandbox architecture. pixels now uses a pluggable backend interface. Container lifecycle, exec, snapshots, and network policy are all defined by the sandbox.Sandbox interface, making it straightforward to add new backends.
TrueNAS remains fully supported as an alternative backend, selected via backend = "truenas" in config or PIXELS_BACKEND=truenas.
What's New
- Sandbox interface — new
sandbox.Sandboxcomposite interface (Backend,Exec,NetworkPolicy) with a registry for self-registering backends (sandbox.Register,sandbox.Open). - Incus backend — manages containers via the native Incus Go client. Supports local unix socket and remote HTTPS connections with TLS client certs. Exec/console uses Incus WebSocket API with terminal resize handling. Snapshots use native Incus snapshot API (storage-backend agnostic). Clone uses
CopyInstance. - Backend selection —
backendconfig key (default"incus"),PIXELS_BACKENDenv var, and new[incus]config section withsocket,remote,client_cert,client_key,server_cert, andprojectkeys. - Typed Status enum —
sandbox.Statusreplaces raw strings for container state.
Breaking Changes
- Removed CLI flags
--host,--api-key, and-u/--username. These were TrueNAS-specific and are now set via config file or environment variables (PIXELS_TRUENAS_HOST,PIXELS_TRUENAS_API_KEY,PIXELS_TRUENAS_USERNAME). - Removed disk cache (
~/.cache/pixels/). The sandbox backends query live state directly. Theinternal/cachepackage has been deleted.
Bug Fixes
- Console zmx command, provisioning executor, and start IP polling.
- TrueNAS exec respects
Rootflag and re-authorizes SSH key on auth failure. - Incus exec runs as configured user with proper shell wrapping and WebSocket IO.
- Network egress setup no longer fails on dpkg conffile prompts.
- Exec runs commands in a login shell with mise activation.
Full Changelog
v0.4.0
Highlights
Persistent sessions with zmx. pixels console now uses zmx for session persistence. Disconnect and reconnect to pick up where you left off — works cleanly with TUIs. Detach with Ctrl+\ or detach.
What's New
- zmx session persistence —
pixels consolecreates a named zmx session (default"console"). Reconnecting re-attaches to the same session. Use-s <name>for named sessions or--no-persistfor a plain shell. pixels sessionscommand — lists active zmx sessions in a container.- Detach alias — type
detachinside a session as a shortcut forCtrl+\. - Detach hint — sourced from
profile.dinside zmx sessions so users know how to detach. - Login shell for zmx — zmx sessions use a login shell so
profile.dscripts are sourced.
Improvements
- Extracted
Executorinterface fromprovision.Runnerfor testability. - Extracted shared helpers from
root.gointoresolve.go. - Embedded
pixels-profile.shinstead of inlining it. - Added
WriteContainerFileandReplaceContainerRootfstests. - Added
lookupRunningIPtests. - Used
resolveRunningIPconsistently in the sessions command.
Full Changelog
v0.3.0
Merge pull request 'feat: self-contained zmx provisioning' (#7) from …
v0.2.0
refactor: replace manual env overrides with caarlos0/env
v0.1.0
ci: add release workflow with GoReleaser and git-cliff changelog gene…