Add cell start/stop, Windows VM engine, WireGuard tunnels, and declarative packages - #45
Merged
Conversation
…oots redesigned to prevent cross-container reaping, and navigator.platform CDP override added for stealth - fix(runner): remove namespace-blind auto-root cleanup from SafeNixGCScript — containers no longer reap each other's indirect GC roots when pruning from a different mount namespace (CELL-330) - fix(runner): key GC roots by nix store path hash instead of project+arch — identical configs dedupe into one root, different configs never overwrite each other (CELL-331) - feat(nix): stamp GC roots at container start via entrypoint fragment — every running container is now protected regardless of whether it triggered a build (CELL-332) - fix(runner): run safe nix GC inside a container with the nix volume mounted instead of via `sudo sh -c` on the host — prune now operates in the correct mount namespace (CELL-333) - feat(runner): add GC root report preflight with drift detection and stale root cleanup — `cell build prune --pure` now shows root inventory and warns when multiple nixpkgs revisions are anchored on disk (CELL-334) - fix(scraping): patch patchright-core's _updateUserAgent to inject navigator.platform via CDP Emulation.setUserAgentOverride — isolated V8 worlds now see the spoofed platform instead of the real OS value (CELL-329) - feat(cli): improve `cell chrome` cookie flow UX — show storage-state path, MCP restart count, and container profile path after extraction - chore(nix): bump nixpkgs-edge flake input
…[gui] wm = "icewm"` or `"fluxbox"` in TOML - feat(cfg): add top-level `[gui]` TOML section with `enabled` and `wm` fields — users can now select icewm or fluxbox per-project without touching nix; legacy `[cell] gui` auto-migrates - feat(runner): pass `DEVCELL_WM` env var into container from `[gui] wm` — window manager selection flows from TOML through CLI to entrypoint without rebuilding the image - feat(desktop): add IceWM as a selectable window manager with Nord theme (gnome-look.org/p/1952840, GPL-2) — IceWM provides built-in systray and EWMH 1.5 for Wine/tray-icon apps that Fluxbox couldn't support - refactor(entrypoint): restructure 50-gui.sh WM startup into icewm/fluxbox case dispatch with fallback — no user-facing impact beyond enabling the `[gui] wm` switch - feat(nix): make desktop module conditionally deploy Fluxbox or IceWM config files based on `windowManager` option — only the selected WM's theme, keys, menu, and wallpaper are included in the closure - test(cfg): add [gui] section tests — enabled/disabled, default WM, legacy migration, merge precedence - test(gui): make pixel assertion and WM detection tests work with both icewm (Nord) and fluxbox (devcell-ocean) — tests no longer hardcode fluxbox process names or devcell-ocean colors
…SNI tray icon support for GUI stack - feat(cfg): add `[gui] scale` and `resolution` TOML fields with DPI/framebuffer resolvers — containers can now match any display (e.g. `resolution = "1800x1169x24"` for 1:1 MBP16, `scale = 2` for Retina) - feat(gui): HiDPI scaling pipeline driven by a single `scale` integer — Xvfb DPI, IceWM/Fluxbox pixel overrides, GTK/Qt scale factors, Xresources, and xrdp.ini all computed automatically - feat(desktop): D-Bus session bus + snixembed SNI→XEmbed proxy baked into entrypoint — modern tray icons (Wails 3, Electron, GTK3+) now work in IceWM without manual setup - refactor(desktop): Mesa env vars (`LIBGL_DRIVERS_PATH`, `__EGL_VENDOR_LIBRARY_DIRS`, `VK_ICD_FILENAMES`) routed through nix profile symlink instead of hardcoded store hashes — no more stale paths after `home-manager switch` - refactor(desktop): D-Bus `--config-file` through profile path instead of compiled-in `--session` default — resolves session.conf lookup failures in nix-installed dbus-daemon - fix(scraping): `mesa.drivers` → `mesa` attribute rename for `VK_ICD_FILENAMES` — fixes nix eval failure on nixpkgs 26.05 - test(cfg): 9 tests for resolution parsing, scale/DPI computation, merge precedence, and default passthrough
…ised GUI - refactor(gui): D-Bus socket moved from /tmp/dbus/session_bus_socket to /run/user/<UID>/bus — apps that auto-discover the session bus via XDG convention now find it without DBUS_SESSION_BUS_ADDRESS - refactor(gui): PulseAudio runtime dir consolidated from /tmp/pulse-runtime into /run/user/<UID> — eliminates a bespoke tmpdir and unifies XDG_RUNTIME_DIR across all services - feat(entrypoint): export WEBKIT_DISABLE_SANDBOX_THIS_IS_DANGEROUS=1 — WebKit's internal bwrap sandbox fails inside Docker (no CAP_SYS_ADMIN); the container is the sandbox - refactor(gui): XDG_RUNTIME_DIR set once early in 50-gui.sh and propagated to shell rc files — previously each service set its own copy, and the value pointed to a PulseAudio-specific path
… runtime - fix(entrypoint): add $HOME/go/bin to PATH — tools installed via `go install` (e.g. wails3) were silently missing from non-login shells and entrypoint fragments
…automatic pkg-config resolution - feat(desktop): upgrade from GTK3 to GTK4 + WebKitGTK 6.0 + libsoup_3 + Vulkan — Wails 3 and WebKit-based GUI apps can now build and run inside the container - feat(desktop): add .dev outputs (gtk4, glib, cairo, pango, harfbuzz, graphene, libepoxy, wayland, X11, etc.) and xorg.xorgproto — CGo/native builds find all headers and .pc files without manual flags - fix(desktop): set PKG_CONFIG_PATH via home.sessionVariables to profile's lib/pkgconfig + share/pkgconfig — `pkg-config --cflags gtk4 x11` works out of the box instead of returning empty - feat(vm): add vm.nix module with QEMU full-system emulation, swtpm TPM 2.0, and OVMF UEFI firmware (x86-only) — enables Windows/Linux VM provisioning from within a container - feat(build): add wimlib for WIM/ESD archive manipulation (x86-only) — supports Windows ISO assembly workflows - chore(flake): enable x86_64-linux in nix flake checks — CI validates all stacks on x86 in addition to aarch64
…and management - feat(vm): QEMU spec builder with defaults/validation, VirtIO networking, UEFI firmware, and VNC — declarative VM configuration from Go structs - feat(vm): VM process management with QMP machine protocol, PID file tracking, and graceful/force shutdown — programmatic control without shelling out to monitor commands - feat(vm): Windows autounattend.xml generation and EFI boot image creation — unattended Windows 11 installation with TPM bypass, SSH, and RDP pre-configured - feat(vm): ISO download with progress, resume, and SHA-256 verification — large Windows ISO fetches survive network interruptions - feat(vm): SSH tunnel wait-loop, screenshot capture with blue-pixel desktop detection, and OpenSSH provisioning scripts — automated post-install validation without manual interaction - feat(vm): launch decision engine (attach/use-local/clone/build/dry-run) and VM discovery via PID+QMP liveness — idempotent `cell init` picks up where it left off - test(vm): comprehensive test suite (19 test files) covering command construction, spec validation, QMP protocol, port allocation, preflight, and provisioning
… and wimlib bindings - feat(uupdump): API client for discovering Windows builds, downloading update packages, and assembling them into bootable ISOs — automates the manual UUPDump.net workflow for obtaining any Windows build - feat(mctcatalog): Microsoft Media Creation Tool catalog parser with CAB extraction and product XML decoding — discovers official Windows ISO download links directly from Microsoft's CDN - feat(isokit): ISO 9660 / UDF image builder with EFI boot support via genisoimage and hdiutil — creates bootable Windows installer ISOs from staged directory trees on both Linux and macOS - feat(wimlib): CGo bindings for WIM/ESD archive operations (open, extract, apply, export, split) with build-tag stub fallback — enables Windows image manipulation without requiring wimlib at compile time - test(iso): test suites for all four packages covering API response parsing, CAB extraction, ISO creation, and WIM operations
…ning on macOS - feat(cmd): wire `--engine=qemu` into `cell build`, `cell init`, and `cell shell` — provisions and launches QEMU Windows VMs with the same CLI interface as docker/tart engines - feat(cmd): add `--qemu-ssh-port`, `--qemu-ssh-host`, `--qemu-windows-iso`, `--qemu-display` flags and TOML/env equivalents — QEMU engine is fully configurable via CLI, `.devcell.toml`, or environment variables - feat(cmd): `cell rdp` and `cell vnc` auto-discover running QEMU VMs via PID+QMP liveness check — QEMU Windows VMs appear alongside Docker containers in the connection picker - feat(cmd): build-tag-gated compilation (`build_qemu_darwin.go` / `*_stub.go`) — QEMU build/init only compiles on darwin; other platforms get a clear "not supported" error - feat(cfg): QEMU config fields (SSH, CPUs, memory, disk size, display, Windows ISO path) with defaults and env resolution — consistent with existing tart/vagrant config patterns - feat(runner): platform-aware `ImageExistsForPlatform` and `PullImageForPlatform` — thin builds pull the correct architecture image on cross-arch hosts - fix(runner): `isAttributeMissing` in platform preflight gracefully skips missing flake attributes — `nix eval` no longer hard-fails when a stack doesn't define an optional platform - test(cmd): comprehensive QEMU runner test suite covering config resolution, flag parsing, and engine selection
- fix(nix): mark `xorg.xorgproto` as `lib.lowPrio` in the desktop module — `cell build` on desktop/GTK4 stacks no longer dies with "two given paths contain a conflicting subpath", while xorgproto still supplies the `.pc` files `x11.pc` resolves against
…ry file - fix(taskfile): resolve `nix` and `nix-instantiate` from PATH instead of the hardcoded `/opt/devcell/.local/state/nix/profiles/profile/bin` — neither binary lives there, so every `.nix` file reported FAIL no matter its contents - fix(taskfile): drop `sudo` from the parse and eval calls — both are read-only and need no privileges, and sudo is currently broken in thin cells, which is what made the failure universal rather than occasional
… chmod-ing the shared nix store - fix(entrypoint): install a setuid sudo copy at `/run/wrappers/bin/sudo` instead of chmod-ing the store path — `sudo` in thin cells no longer dies with "must be owned by uid 0 and have the setuid bit set", and a `nix profile upgrade` in one cell stops breaking sudo in every other cell sharing the volume - fix(entrypoint): write the `/etc/pam.d/sudo` stub when it is missing — thin images ship no `/etc/pam.d` at all, so sudo aborted on PAM before it ever read sudoers - fix(entrypoint): pin the wrapper's store closure as a GC root and repoint `/bin`, `/usr/bin` and `/sbin` sudo at it — a shared-volume GC can no longer pull `sudoers.so` out from under a running cell, and scripts hardcoding FHS paths keep working - feat(entrypoint): warn at boot when setuid is neutralized by a nosuid mount or no-new-privileges — the breakage surfaces at container start instead of at the user's first `sudo` - fix(runner): put `/run/wrappers/bin` first on PATH in both the thin and pure image env — otherwise the non-setuid profile sudo shadows the wrapper and nothing improves - test(runner): pin the PATH ordering and guard that devcell never passes `--security-opt no-new-privileges`, which would silently neutralize the wrapper in every cell - test(container): cover the wrapper from an unprivileged caller — the existing CELL-86 tests exec as uid 0 and pass even when the setuid bit is missing entirely
…points can no longer be committed by accident - chore(tooling): Pin Go in `.mise.toml` as `go = "1.26"` — contributors get the toolchain from the one config `mise` actually reads; the old `golang` entry was an asdf-only plugin name that never resolved in a mise config - chore(tooling): Drop `.tool-versions` — one less file to keep in sync with `go.mod`, and no split between asdf and mise contributors - chore(tests): Ignore the Windows VM checkpoints (`windows-sshable-*`, `windows-wsl-*`, `windows-nix-*`, `windows-arm64.*`) and the cached `test/testdata/cellhome/` — a stray `git add -A` can no longer stage a 32 GB disk image
…EMU guests hardware acceleration, and module order in devcell.toml no longer changes the image tag - feat(cfg): Add `cell.kvm` / `DEVCELL_KVM`, passing the daemon host's `/dev/kvm` through as `--device` — Windows and other QEMU guests run on hardware virt instead of TCG; opt-in, because the device lives on the daemon host (e.g. the Colima VM) that the CLI cannot stat, so a wrong guess either fails `docker run` outright or silently drops back to TCG - fix(cfg): Sort `cell.modules` at load — `["desktop","electronics"]` and `["electronics","desktop"]` now resolve to the same image tag and home-manager closure, so reordering the list in TOML no longer forces a full rebuild - feat(cfg): Add a `[build]` section for `memory`, `cpus`, `max_jobs` and `cores` — thin-build resource ceilings are now settable per project in TOML rather than only through env vars - feat(cfg): Add `cell.libvirt_uri` and `cell.libvirt_path_map` — points a cell at the macOS host's session libvirtd (default `qemu+tcp://host.docker.internal/session`) and rewrites container paths to host paths, so domain XML generated inside a cell resolves on the host - feat(cfg): Add `cell.qemu_project_sync` with `off` / `push` / `two-way` — controls whether the guest receives project files and whether host files can be written back; defaults to `push`, the mode that cannot overwrite anything on the host - feat(cfg): Add `cell.stale_warning` — lets a user turn off the "cell is behind" nudge at start; enabled by default since it is a read-only prompt that proceeds unless answered - test(cfg, runner): Cover env > toml > default resolution and project-over-global merge precedence for every new key, plus `--device=/dev/kvm` placement in the docker argv — no user-facing impact
…unaway nix build can no longer starve the whole VM - feat(runner): Stream the nixhome overlay to the builder as a tar on stdin instead of bind-mounting the host path — thin builds now work when the selected daemon lives inside Docker Desktop or Colima, or on a remote host, where a second-generation container cannot resolve a host path at all - feat(runner): Cap the builder with `--memory` and `--cpus` derived from the daemon's own capacity (¾ of its memory, rounded to whole GiB) — a `nix build` spike now takes a deterministic cgroup kill confined to the builder instead of the VM-wide OOM killer starving sibling cells - fix(runner): Derive nix `max-jobs` and `cores` from the ceiling instead of leaving `max-jobs = auto` — `--cpus` is a CFS bandwidth quota, so `nproc` inside the container still reported every host CPU and nix scheduled one derivation per host CPU; under an 8 GiB ceiling that OOM-killed `npm ci` mid-install with exit 137 - fix(runner): Emit a ceiling only when it actually constrains the daemon — dockerd rejects a `--cpus` above its own CPU count with exit 125, which was fatal on a stock 2-CPU Colima VM, and a ceiling at or above the daemon's capacity protects nothing anyway - fix(runner): Treat `--memory` as a hard limit rather than relying on Docker's implicit 2x swap cushion — Lima and Docker Desktop VMs both run swapless in practice, so that cushion never existed and only the derived concurrency keeps the build inside the ceiling - feat(runner): Honour `DEVCELL_BUILD_MEMORY`, `DEVCELL_BUILD_CPUS`, `DEVCELL_NIX_MAX_JOBS` and `DEVCELL_NIX_CORES`, with `"0"` opting out — a user on an unusual daemon can override or disable the ceiling without patching devcell - test(runner): Cover ceiling derivation, clamping against daemon capacity, nix concurrency, and the tar context's file set and layout — no user-facing impact
…shared nix volume, and a drifted cell says so at start instead of silently doubling the store - feat(cmd): Add `cell cleanup` — reaps `gcroots/devcell/` entries no RUNNING container references, so a stopped cell's closure stops pinning gigabytes on the shared volume; a later `cell shell` on it rebuilds cleanly through the existing hydration gate - feat(runner): Warn at cell start when the cell's closure is behind the newest rev on the volume — starting it is the act that keeps a second full closure alive, so the user hears it at that moment with `cell build --update` as the remedy, and proceeds by default (CELL-391 policy: inform, not enforce) - feat(runner): Add a read-only nix-store health probe at startup reporting stale roots, distinct profile hashes, and orphaned generations — drift shows up as a number instead of as an unexplained full volume; nix itself is never invoked, since its own root-finding pass deleted 12 live auto roots in a measured "preview" - fix(runner): Stamp GC roots for every running container as a prune preflight — "running implies rooted" now holds by construction rather than by assumption, so a prune can no longer collect a store path out from under a live cell - feat(cmd): Report the nix store in `cell df` and dump client-to-daemon wiring under `--debug` — a wedged or misrouted daemon is diagnosable without hand-rolled docker invocations; the dump deliberately omits registry and auth config - feat(cmd): Wire the thin-build resource ceilings and the tar-stdin overlay into `cell build`, and give `--engine=libvirt` an explicit error naming `--engine=qemu` as the way to build a template — the deferred path fails with instructions instead of an obscure crash (CELL-379) - test(runner): Cover live-closure collection, reap and retention rules, health parsing, stale detection, and the prune preflight — no user-facing impact
…ers ~/.config/devcell/devcell.toml from a schema generated out of the Go types - feat(nix): Export `homeManagerModules.default` — a user sets `devcell.cell.stack`, `devcell.prompt`, `devcell.op.documents` and the rest in their home-manager config instead of hand-editing `~/.config/devcell/devcell.toml`, and gets the `cell` CLI from the same flake - feat(nix): Render only the global config layer — project-local `.devcell.toml` and `DEVCELL_*` env vars still win, so a declared machine-wide default and a hand-edited project file coexist rather than fighting - feat(cfg): Generate the option tree by reflecting over `CellConfig` — every `devcell.toml` key that exists in Go exists as a typed nix option, so adding a config field can no longer leave the nix module silently behind - feat(cfg): Default every leaf to `null` and omit unset keys from the rendered TOML — absence semantics match a hand-written config, so an undeclared `modules` stays undeclared rather than becoming `[]` - feat(tools): Add `hmoptgen`, which writes `nix/home-manager/options.nix` with a DO-NOT-EDIT header — the generated file is checked in, so consumers of the flake need no Go toolchain to evaluate it - test(cfg): Cover the Go-to-nix type mapping — scalars, `[]string`, `map[string]string`, nested structs as plain attrsets, and struct collections as submodules — no user-facing impact
…cells get a real dev-env — `cell build --engine=qemu` now boots a guest with WSL2, NixOS-WSL and the repo's home-manager profile, on Linux as well as macOS - feat(qemu): Provision the guest through a stage runner that drives PowerShell over SSH with retries, mid-stage reboots, per-stage deadlines and logs streamed to disk while they run — a failed provision names the stage that broke instead of hanging until the outer timeout, and `cell build` and the dev-env test exercise the same path so the test proves the shipped one (CELL-405) - feat(qemu): Install WSL2, import NixOS-WSL and activate the nixhome home-manager profile in the guest — a Windows cell ends up with the same nix toolchain as a Linux cell rather than a bare Windows box; the WSL account is deliberately a separate identity from the Windows session user (CELL-404) - feat(qemu): Push project files into the guest over the session's own SSH transport, and pull them back in `two-way` mode — the agent lands in the real tree instead of an empty directory, which neither virtiofs (no virtiofsd on macOS) nor 9p (no Windows client driver) could deliver (CELL-383) - feat(qemu): Generate unattend XML from a typed schema and ship first-logon provisioning as a real `.ps1` on a FAT control volume — a multi-line SSH key no longer dies on inline XML/cmd quoting, and a malformed answer file is caught before a two-hour install rather than after it (CELL-362, CELL-402) - feat(qemu): Diagnose a guest that never comes up — firmware fault and stall detection, guest log and registry extraction, and screenshots — so a boot failure reports a cause instead of a timeout; nested-virt capability under each accelerator is measured rather than assumed (CELL-392, CELL-398) - feat(cmd): Build the QEMU engine on Linux as well as macOS (`build_qemu_darwin.go` → `build_qemu.go`, tagged `darwin || linux`) and add a build budget — Linux hosts with `/dev/kvm` can provision Windows cells, and a runaway install stops at a declared ceiling - feat(isokit): Read and rewrite El Torito boot catalogs and locate file extents in place — the installer ISO can be patched without a full rebuild, which is what makes the answer volume and boot wiring reproducible - test(qemu, isokit): Cover the stage table, unattend schema, boot matrix, secure boot, control-volume round-trip, nixhome activation and the ISO surgery — no user-facing impact
…] `--engine=libvirt` runs Windows cells on the Mac host's hypervisor, so a cell on a Mac stops falling back to TCG emulation - feat(libvirt): Add the `libvirt` engine — defines and boots a prepped Windows template through the host's session libvirtd over `qemu+tcp://host.docker.internal/session`, so the guest runs on the Mac's HVF instead of the TCG emulation a container-local QEMU is limited to (CELL-372, CELL-377) - feat(libvirt): Rewrite container path prefixes to their host equivalents in the generated domain XML — the qcow2 template and ISOs a cell sees under its own bind-mount prefix now resolve for a QEMU process running on the host, which would otherwise fail to open them and never boot (CELL-375) - feat(cmd): Default a containerized cell on a Mac to libvirt when the host gateway resolves and `/dev/kvm` cannot be opened — the fast path is picked without the user knowing to ask for it, and `--local` still pins container-local QEMU when that is what they want (CELL-378) - fix(libvirt): Map each connection failure to one actionable message during preflight — an unreachable or half-configured libvirtd says which command to run next instead of surfacing a raw RPC handshake error - feat(cmd): Sync project files for the libvirt engine on the same push/pull contract as QEMU — `runProjectSync` is shared by both runners, so the agent lands in the real project tree on either engine (CELL-383) - feat(cmd): Wire the startup nix-store health report and the stale-cell nudge into `cell` start, and add `--auto-cleanup` to the forwarded-flag strip list — the health report is read-only and non-fatal, and mutation happens only behind that explicit opt-in (CELL-390, CELL-391) - chore(deps): Add `digitalocean/go-libvirt` and `libvirt.org/go/libvirtxml` — the RPC client and XML builder the engine is written against, with `flake.nix`'s `vendorHash` refreshed so `nix build .#cell` still resolves the module closure - test(libvirt, cmd): Cover the autodetect decision matrix, domain XML generation, path rewriting, preflight failure mapping, and the engine lifecycle against an injected client — no user-facing impact
… vendorHash hook can no longer pass a stale hash - fix(infra): Stop the pre-commit hook reporting a failed build as "vendorHash is already correct" — it inferred success from the absence of a `got:` line, so a build that died on disk space or network went green and let a stale hash reach the commit; the check now branches on nix's exit status and says which failure it hit - feat(infra): Add `task nix:sync` — resolves the vendorHash through `nix-update` and stages `flake.nix`, so a dependency change is one command and `git commit` then passes untouched; it refuses to leave the `sha256-AAAA…` placeholder behind, which the old task did on a failed build - refactor(infra): Make the hook read-only (`task nix:check-vendor-hash`) — it no longer rewrites a tracked file, so it stops fighting pre-commit's stash/restore and no longer forces a re-stage and re-commit, while keeping the guarantee that a commit can never carry a stale hash - feat(infra): Add `task hm:generate` — regenerates `nix/home-manager/options.nix` from `internal/cfg.CellConfig`, the target the generated file's own header tells you to run - feat(infra): Add `task debug:colima` — dumps daemon capacity, context wiring and the resource ceilings a thin build will actually emit, so an OOM-killed or `--cpus`-rejected build is diagnosable before it runs - feat(infra): Add the QEMU and dev-env targets for building, booting and checkpointing Windows guests — the flows the engine commits added are now driveable without hand-written qemu invocations
…resolves, `[cell] kvm = true` actually reaches QEMU, and a running cell is rooted even if it was never the last thing built - feat(nixhome): Expose `wsl-<stack>` and `wsl-<stack>-aarch64` home-manager outputs for the NixOS-WSL default user — the dev-env stage shipped with the QEMU engine switches to `./nixhome#wsl-base$ARCH_SUFFIX`, which until now resolved to nothing, so a Windows cell reached the home-manager step and failed there - feat(nixhome): Enable `programs.home-manager` in the base profile — a standalone activation inside WSL keeps the `home-manager` CLI on PATH, so the environment can be re-switched and its version asserted instead of being a one-shot - fix(entrypoint): Add the session user to the group owning `/dev/kvm` when the device is passed in — the node arrives `root:<host-gid>` mode 0660 with a GID that has no group entry in the image (994 on Colima), so QEMU died with "Could not access KVM kernel module: Permission denied" and `[cell] kvm = true` bought nothing; the GID is resolved at runtime because it differs per daemon host - fix(entrypoint): Stamp GC roots at container start rather than at thin-build time — the root set tracked "what was built last" instead of "what is running", so N projects sharing an image got one root between them and a rebuild could silently unroot all of them (CELL-332) - feat(entrypoint): Write per-root drift metadata — project, stack, modules and the locked nixpkgs rev — so the cleanup reaper and the stale-cell nudge can tell divergent closures apart instead of guessing (CELL-334, CELL-391) - refactor(entrypoint): Name roots by closure hash so re-stamping an identical closure is a no-op and many cells on one config converge on a single root pair; skipped when `gcroots` is not writable, which is the impure-image case (CELL-331)
…n sign the Windows binaries they build - feat(nixhome): Add a decompiler set to the android module — cfr, dex2jar, enjarify, procyon and androguard alongside jadx, so a class jadx renders badly has a second backend to fall back on rather than being a dead end - fix(nixhome): Move jadx out of the x86_64-only block — it is pure Java, so the android module on aarch64 (Apple Silicon Docker, ARM servers) now ships the whole decompiler toolkit instead of degrading to adb and nothing else; only the SDK, emulator and apktool stay x86-gated, apktool because it pulls in aapt - feat(nixhome): Add osslsigncode to the wine module — Authenticode-signs the PE and MSI artifacts from `wails3 build` directly, instead of needing signtool.exe running under Wine - docs(nixhome): Correct the android module's advertised size and platform notes — aarch64 is ~600 MB (adb plus decompilers), not the ~50 MB the old comment claimed when the module was adb-only there
…t's hypervisor instead of TCG emulation - docs(readme): Document the libvirt engine end to end — the one-time macOS libvirtd setup, the `qemu+tcp://host.docker.internal/session` URI, the container→host path map, and project sync modes — so the fast path is reachable without reading the source - docs(readme): State that libvirt mode boots an already-prepped template and that builds stay on `cell build --engine=qemu` — `--engine=libvirt` refuses deliberately, and without saying so the refusal reads as a bug (CELL-379) - docs(readme): Warn that `auth_tcp = "none"` is unauthenticated and must stay on loopback — anyone who reaches the port controls the VMs, and the setup snippet would otherwise be copied without that context - docs(readme): Document the qemu→libvirt auto-upgrade and the `--local` escape hatch — a cell on a Mac silently switching to a 10–20× faster path is invisible otherwise, as is the way to opt out
…as reading nothing, since CLAUDE.md was gitignored and AGENTS.md is never read - fix(repo): Add a `CLAUDE.md` that imports `@AGENTS.md`, and stop gitignoring it — Claude Code reads `CLAUDE.md` and never `AGENTS.md`, so with the former ignored and deleted the project rules were loading into no agent at all; `CLAUDE.local.md` takes over the ignore, which is the file that actually holds personal overrides - feat(repo): Track `AGENTS.md` so the project rules ship with the repo instead of being handed to each contributor out of band, and so every agent runner reads the same file rather than only Claude Code - refactor(repo): Cut `AGENTS.md` from 171 to 59 lines, dropping what an agent can read off the codebase — the Key Files list and stage diagram had already rotted (`Dockerfile` and `nixhome/profiles/` do not exist, `entrypoint.sh` is under `nixhome/`), so they were costing context to mislead; the pitfalls, gotchas and conventions that differ from tool defaults stay - feat(repo): Move the nixhome and test-bucket procedures into path-scoped `.claude/rules/`, loaded only when an agent reads `nixhome/**/*.nix` or `test/**` — multi-step procedures for one area no longer occupy every unrelated session - feat(repo): Move the vocabulary into an always-on rule and expand it — cell/project/container/stack/module now carry the name-resolution order and the pending `WorkspaceResource` rename, and drop the dead pointer to `.context/`, which is never committed - fix(repo): Change the ignore from `.claude/` to `.claude/*` with `!.claude/rules/` — git cannot re-include a path inside an excluded directory, so the rules would have been silently untracked while appearing to work locally; `commands/` and `scheduled_tasks.lock` stay ignored
… decompiling an APK no longer ends at jadx - feat(nixhome): Add APK acquisition and packaging — apkeep pulls from Google Play/APKPure, bundletool merges the split APKs APKPure actually serves, and apksigner re-signs whatever apktool rebuilds, so a downloaded app is installable without leaving the cell - feat(nixhome): Add static triage — apkleaks, apkid and quark-engine surface endpoints, secrets, packers and behaviour straight from a DEX, before any device is involved - feat(nixhome): Add dynamic analysis — mitmproxy captures app traffic, mitmproxy2swagger turns those flows into an OpenAPI spec, frida-tools and jnitrace instrument the running app, and scrcpy drives the device - feat(nixhome): Add rooted-device firmware tools — payload-dumper-go, abootimg and avbroot unpack an OTA and patch boot for Magisk, covering the Pixel workflow end to end - fix(nixhome): Correct the android module's advertised description and size — both had been stale since the toolkit went cross-platform, understating what an aarch64 user actually gets - docs(nixhome): Record which tools have no nixpkgs attribute — objection, apk-mitm, hermes-dec, mobsf, reflutter/blutter still need pip/npm, so the gap is not re-investigated Every package is verified to evaluate and substitute from cache on both aarch64-linux and x86_64-linux — nothing builds from source. pkgs.simg2img was evaluated and deliberately rejected: android-tools already ships simg2img/img2simg/append2simg, and a second copy is a hard home-manager-path collision rather than a silent shadow.
…uilt-in system prompt, not just append to it — and prompts of any size travel as files instead of argv - feat(cfg)!: BREAKING: `[llm].system_prompt` now REPLACES Claude Code's built-in prompt instead of appending to it — a cell that sets it loses the stock ~10.6 KB of tool guidance and safety instructions unless it also sets the new `append_system_prompt`, which is where today's behaviour moved - feat(cfg): Add the `append_system_prompt` layer across every source — TOML, `DEVCELL_APPEND_SYSTEM_PROMPT[_FILE]`, and `cell serve --append-system-prompt[-file]` — so text can stack on the stock prompt now that the old key means replacement - feat(runner): Deliver both prompts as generated files under `.devcell/prompts/<cell>/` — a prompt larger than 128 KiB no longer dies at process spawn with `Argument list too long`, and prompt text stops appearing in `ps aux` and `docker inspect` - fix(runner): Namespace generated prompts per cell — two cells open on one project would otherwise overwrite each other's file, and whichever claude started last would boot with the wrong container context - feat(nix): Add a `devcell.appendPrompt` shorthand to the home-manager module — the global layer can express both prompts, where `devcell.prompt` alone now silently means replacement for every cell and project - refactor(runner): Resolve both layers through one shared tier walk rather than a second copy of the seven-source precedence chain — no user-facing impact - test(runner,cfg,serve,cmd): Cover file materialization, per-cell isolation, base/overlay separation, conditional flag emission, and the merge lines a new config key needs
…rner, so side-by-side RDP tabs are instantly distinguishable
- feat(desktop): Add cell-ID watermark to SVG wallpaper template — multiple cells open side-by-side no longer require checking the terminal prompt to tell apart
- feat(desktop): Rasterize wallpaper at boot with cell name injected via sed → rsvg-convert → feh — ~250ms one-shot, no resident daemon
- fix(desktop): Strip `{{CELL_ID}}` placeholder at build time so fluxbox fallback wallpaper shows no literal text
…s` and every CLI command now reports anonymous usage events via PostHog - feat(telemetry): Add opt-in config persistence with UUID generation and DO_NOT_TRACK support — users control whether any data leaves the machine - feat(telemetry): Add PostHog client wrapper with Init/Track/Close lifecycle — disabled config or DO_NOT_TRACK=1 makes every call a no-op - feat(cli): Add `cell telemetry [on|off|status]` subcommand — matches the Homebrew/Terraform opt-in pattern - feat(cli): Wire telemetry.Track into every feature command (build, serve, init, rdp, vnc, models, modules, cleanup, auth) and runAgent() lifecycle — events carry engine, flags, duration, and exit status but never file paths or arguments - test(telemetry): Add config round-trip, client init/no-op, and feature-event property tests — 100% coverage of the new package - chore(nix): Update vendorHash for posthog-go and add nix-update to nixos module — `task nix:sync` now works inside cells
…nes — an flock-based lock serializes VM-launching tests across processes and worktrees - feat(qemu): flock-based test lock ensures one QEMU at a time, across `go test` invocations and worktrees — two concurrent TCG guests pushed both past their SSH deadlines, producing failures that read like guest bugs - test(qemu): every VM-launching test (boot, install, prepped, secureboot, sshable) acquires the lock before exec — the lock releases automatically on SIGKILL, so a wedged run cannot stale-lock the next one
… guest finally runs as the host user after activation - feat(qemu): wsl-adopt-user stage renames the distro user after home-manager activation — `whoami` now answers the host user instead of "nixos" - feat(qemu): harden-emulation provisioning step disables WerFault, Defender, Windows Update, and Search — the biggest resource wasters in a TCG build VM (~3 GB reclaimed) - feat(qemu): TCG builds use disk cache=unsafe and 8 GB guest RAM — eliminates sync flushes that are pure waste in a disposable build VM, and 4 GB starved the install - fix(qemu): nix-verify now proves the store is writable, not just that `nix --version` answers — the old check certified a distro that failed 13 minutes later on /nix/var/nix/db/big-lock - fix(qemu): home-manager activation exit code no longer swallowed by a `| tail` pipe — a failed switch now fails the stage instead of reporting "ok in 36s" - fix(qemu): WSL engine gets 4 vCPUs + 4 GB and E2E span bounds track the stage table — new stages can no longer silently fall outside test coverage - test(qemu): shipped-script invariants, source-level lock guard, nix-daemon helper, and expanded devenv coverage
…t description - docs(nix): add note that remarshal renders newlines as \n in quoted strings, not TOML triple-quoted multiline — content is preserved but harder to read in the generated devcell.toml
…t description - docs(nix): add note that remarshal renders newlines as \n in quoted strings, not TOML triple-quoted multiline — content is preserved but harder to read in the generated devcell.toml
…t description - docs(nix): add note that remarshal renders newlines as \n in quoted strings, not TOML triple-quoted multiline — content is preserved but harder to read in the generated devcell.toml
Users can now declare arbitrary nixpkgs packages across stable/unstable/edge channels in devcell.toml. Packages are injected into the overlay flake with lib.hiPri so they override module-provided versions. Build-time validation catches invalid names and cross-tier duplicates before nix evaluates.
Detect flake.nix in the project root and install its packages into a dedicated nix profile (~/.local/state/nix/profiles/project) at start. Trust prompt runs host-side in the Go CLI with a per-cell cache; the entrypoint fragment only checks DEVCELL_FLAKE_TRUST=1. Hash-based cache (flake.nix + flake.lock) skips redundant installs. Boot fails on install error with --skip-flake / DEVCELL_SKIP_FLAKE=1 escape hatch.
wimlib pulls in syslinux which has no aarch64 support, causing a platform mismatch error on colima (Apple Silicon).
…Router Add `--openrouter` CLI flag and `use_openrouter = true` config option to redirect Claude Code API calls through OpenRouter. API key is resolved from 1Password (deferred until after [op] documents resolve). Gateway model discovery and fast mode are enabled for both ollama and openrouter modes. When the global default model has an ollama/ prefix, openrouter mode falls back to the first model in [llm.models.providers.openrouter].
PhaseDetailedWarn() and ProgressSpinner.Warn() let phase rows signal warnings visually. NixStoreHealth.Summary() now returns a warn bool so stale roots, orphaned generations, and profile hash drift render ⚠ with the prune hint instead of appearing as successful ✓ rows.
…uting New graynet.nix provides tor, torsocks, and nyx for .onion routing and anonymous crawling. Scraping module gains GRAYNET_PROXY support to route Playwright browsers through a SOCKS/HTTP proxy (e.g. Tor via graynet).
…7, and test artifacts move to structured run.json - feat(winpe): replace bootstrap.cmd/agent.cmd/wim-builder.cmd with PowerShell equivalents (bootstrap.ps1, agent.ps1, devcell-wim-builder.ps1) — real-time progress streams via Tee-Object to virtio-serial + console + file instead of silent file capture - feat(winpe): bootstrap.cmd becomes a thin shim that probes volumes for xcopy-deployed pwsh.exe and launches bootstrap.ps1 — stock WinPE still boots without built-in PowerShell - feat(wim-builder): skip install.wim mount/unmount when only VirtIO driver ops are requested — driver-only builds finish in ~12 min instead of 20+ under TCG - feat(wim-builder): DISM feature verify now shows feature names alongside state — post-build logs are readable without cross-referencing DISM docs - refactor(qemu-test): replace run-info.txt and qemu.log with structured run.json and in-memory qemuLog buffer — test artifacts are machine-parseable and qemuLaunchHint reads live output instead of stale files - refactor(qemu-test): split TestWimBuilder into boot-wim (drivers only, 10 min) and full (all features, 30 min) subtests — CI can run the fast path without a Windows ISO - test(wim-builder): add DriversOnly_NoInstallWimMount asserting no install.wim references in driver-only scripts
… session resume, WireGuard tunnels, and OpenRouter for codex/opencode - feat(cli): resolve `[cell].default_command` (env/TOML) so `cell` with no subcommand launches a configured tool instead of showing help - feat(cfg): replace top-level `docker_privileged`/`docker_cap_add` with a `[docker]` section adding `mem_limit`/`cpu_limit`/`shm_size`, wired into `docker run` — containers get sane default memory/CPU/shm ceilings instead of running uncapped - feat(cli): inject a deterministic `CLAUDE_CODE_SESSION_ID` per tmux pane — `cell claude` resumes the same conversation on relaunch instead of starting fresh - feat(cli,nix): add `[[wireguard]]` config with validation and container plumbing (CELL-451) — cells can tunnel outbound traffic through a WireGuard peer for country-specific exit IPs - feat(cli): extend `--openrouter` from claude to codex and opencode via a shared `FillOpenRouterKey` helper — routing any of the three agents through OpenRouter now works uniformly - fix(entrypoint): strip the `PermissionRequest` hook when Claude Code is routed through OpenRouter — non-Anthropic models no longer abort in-flight tool calls on that hook - test(cmd,cfg,runner): cover default_command resolution, docker resource limits, session ID determinism, WireGuard validation/argv/PrepareWireguard, and openrouter config for codex/opencode
…splant so devcell.wim can boot WSL2 guests inside WinPE - feat(vm/qemu): transplant VMP services/binaries and drivers from install.wim into boot.wim, bypassing CBS package-parent rejection — WinPE images built by devcell can now enable Hyper-V without a full Windows install - feat(vm/qemu): inject the WSL2 engine MSI and register it for boot, with a wsl-boot verification pass — devcell.wim can bring up a working WSL2 guest, not just the hypervisor - feat(goregedit): read/write offline registry hives and patch BCD entries (hypervisorlaunchtype, boot-start driver set) in Go — no more shelling out to reg.exe/bcdedit for transplant patching - feat(gosshd): embed an in-guest SSH relay for live debugging of the WinPE guest during build/verify — replaces ad hoc serial-log polling - feat(vm/qemu): replace the cmd.exe bootstrap chain with a PowerShell 7 bootstrap that streams structured JSONL progress, and move test artifacts into a structured run.json — build failures are now diagnosable from progress events instead of scraping serial output - feat(wimlib): add DCS/LZMS decompression and directory-tree iteration bindings — supports reading and re-packing WIM images produced by the transplant/verify passes - chore(nix,go): add cdrkit/msitools packages and gliderlabs/ssh, golang.org/x/sys dependencies, and ignore test/results_archive — no user-facing impact
…ontainers - feat(cmd): add `cell start` to launch containers detached — cells stay up without an attached terminal. - feat(cmd): add `cell stop` to shut down the running container cleanly. - fix(cmd): `cell shell` reuses a running container via `docker exec` instead of starting a duplicate. - feat(runner): add `RunSpec.Detach` and `-d` argv support. - feat(runner): add `ContainerRunning` docker-inspect check. - feat(runner): add `exec.go` with `ExecSpec`/`BuildExecArgv`.
…, and close the gap that let it happen silently - fix(nix): update flake.nix vendorHash to match current go.mod/go.sum — unbreaks `nix flake update` / `darwin-rebuild switch` for anyone consuming this branch as a flake input - fix(task): require the vendorHash line itself to change in nix:check-vendor-hash, not just any flake.nix edit — an unrelated flake.nix touch in the same commit as a go.sum bump previously suppressed the warning entirely
…cratch/ - chore(docker): exclude .scratch/CONTINUE.md instead of the old root-level path — build context no longer ships the stale exclusion rule
- feat(task): add nix:build — runs `nix build .#cell --no-link` then executes the result, catching vendorHash/flake.nix breakage that the instant pre-commit check can only warn about - feat(task): nix:build depends on hm:generate — home-manager options.nix and the flake output are checked together, since both can go stale from the same root cause
…lve go-regedit/go-wimlib/go-winkit deps from real go.mod/go.sum instead of a private gitignored go.work - refactor(vm/qemu,internal): delete internal/goregedit, gosshd, hcsvm, isokit, mctcatalog, uupdump, wimlib, and the WinPE/WIM-carve files in internal/vm/qemu — smaller tree to maintain, no behavior change since callers now import the equivalent external packages - build(go.mod): publish real require entries for go-regedit, go-wimlib and go-winkit — closes the flake-input breakage where anyone pinning feature/wip without the local go.work couldn't build at all - build(nix): regenerate flake.nix's vendorHash to match the new go.sum so `nix build` fetches the right vendored deps for the four extracted modules - build(taskfile): rewrite nix:sync to resolve vendorHash via `nix build ...#cell.goModules --no-link` instead of nix-update — avoids copying the >100GB gitignored test/ tree into the Nix store, which previously filled disk on every sync - test(vm/qemu): add winpe_engine_remnants_test.go locking in which WinPE/WIM behavior must stay engine-side, so a future extraction can't silently regress what devcell itself still owns - refactor(cmd,tools): rewrite import sites in cmd/build_qemu*.go, tools/renderps1/main.go and ~40 vm/qemu tests to pull from the new external modules — no behavior change for CLI users
…, not a pinned subtree of devcell - refactor(runner): repoint UpstreamOwner/Repo/Subdir and flake refs at devcell-sh/community-home — nixhome now versions and releases independently of the devcell CLI repo - fix(runner): flip DefaultNixhomeGitRef from the "feature/wip" pin back to "main" — dev builds and fallback resolution track community-home's default branch instead of a stale temporary branch - docs(scaffold): update generated flake.nix's pin-a-release comment to the new github ref — copy-pasting it now produces a working pin - fix(scaffold): update Vagrant template's clean-machine fallback flake ref — machines without a local/opt nixhome now fetch from the correct repo instead of a dead path - test(runner): update upstream/pure-nixhome-resolver/thin-build fixtures and assertions to match the new ref format
…nstead of reaching default_command - fix(cmd): prepend configured default_command to os.Args before cobra parses flags — `cell -c` now runs `claude -c` instead of erroring - refactor(cmd): drop old post-parse RunE dispatch to default_command — no user-facing impact - test(cmd): add unit tests for rewriteDefaultCommand/applyDefaultCommand — guards flag-forwarding regressions
…hange - refactor(nix-store): call github.com/devcell-sh/go-nixoci instead of internal/nixstore — same CLI flags/output, same push/pull/resolve/volume behavior - chore(nix-store): delete internal/nixstore package and its tests — dead code removed now that go-nixoci is the single source of truth
- chore(fmt): apply gofmt whitespace/alignment/import-ordering across ~47 files — no behavior change - chore(fmt): align struct-literal fields and var blocks in cmd/ and internal/runner/vm — no behavior change - chore(fmt): reorder and group imports per gofmt convention — no behavior change - chore(fmt): trim redundant blank lines and reflow comments — no behavior change
- refactor(tools): relocate tools/hmoptgen/main.go to cmd/hmoptgen.go with //go:build ignore — one less tools/ directory, same generator - chore(build): point task hm:generate at go run cmd/hmoptgen.go — no behavior change
…nkit winpe.ExtractStage - refactor(qemu): remove leftover copy of the WinPE stage extractor — go-winkit/winpe owns it since v0.2.0; no caller referenced the old copy
- refactor(tools): relocate tools/hmoptgen/main.go to cmd/hmoptgen.go with //go:build ignore, like gendoc.go — one less top-level directory, same generator - refactor(tools): drop tools/renderps1 — the template lint harness now lives in go-winkit next to the .ps1.tmpl files it lints, so template bugs are caught where they are fixed - chore(build): task hm:generate runs go run cmd/hmoptgen.go; task test:powershell:lint now lints only devcell-owned guest scripts — no behavior change
…m/qemu - fix(vm/qemu): swap unattend.PadForFAT/StartupNSH for winpe package — unblocks go build ./...
… resolution moved to one shared resolver - refactor(cmd/build): remove --impure/--debian/--pure/--no-thin/--thick and legacy Dockerfile/pure build paths — those flags now error as unrecognized and every Docker build runs the thin nix2container-on-volume flow - refactor(cmd/root): hardcode thin=true in runAgent and drop the impure/AcquireImage fallback chain — agents (claude/shell/codex/gemini) can no longer fall back to a Dockerfile build when nix is unavailable on the host - feat(runner): add ResolveNixhomeRef(ver) resolving DEVCELL_NIXHOME > legacy DEVCELL_NIXHOME_PATH > upstream flake ref, used by cmd/build, cmd/build_tart_darwin, and cmd/init — DEVCELL_NIXHOME now takes precedence over the older DEVCELL_NIXHOME_PATH env var everywhere nixhome is resolved - refactor(cmd/init): drop --nixhome and --local-nixhome flags in favor of ResolveNixhomeRef — `cell init --nixhome <path>` and `--local-nixhome` no longer work; nixhome source is now env/upstream-driven only - refactor(cmd): remove dead scanStacksFromNixhome helper and unused nixhomePath plumbing through build_tart_darwin, init_tart_darwin, tart_runner, and initflow — no behavior change for users - chore(repo): move the nixhome package to its own repo and rename the CLI repo's public GitHub identity — flake.nix, README.md, and the OpenRouter HTTP-Referer now point at devcell-sh/devcell, and default nixhome forking/cloning now targets devcell-sh/community-home instead of DimmKirr/devcell, so old bookmarked/forked URLs to DimmKirr/devcell no longer match the canonical source
- chore(nixhome): delete local nixhome/ tree (modules, stacks, themes, entrypoint) — Nix edits must now happen in the community-home repo instead of here
- chore(claude-rules): drop .claude/rules/nixhome.md package-adding guide — contributors follow community-home's own rules for Nix packages instead
- ci(build): key nix-store cache tags on github.sha instead of hashFiles('nixhome/**') and drop DEVCELL_NIXHOME_PATH env — cache busts on every commit until community-home ships its own cache key, so builds may rebuild more than before
- ci(build): drop `--thin` flag from `cell build` invocations in dev/release workflows — CI now builds the default (non-thin) image path
- build(taskfile): remove nix:validate and image:pure:* tasks — `task nix:validate` and pure-image build/push commands no longer exist; use community-home's own validation instead
- chore(gitignore): remove now-irrelevant nixhome/user.nix entry — no functional effect, since the whole nixhome/ tree is now untracked here
- docs(agents): update AGENTS.md to point Nix-module edits at devcell-sh/community-home — contributors editing `.nix` files here are now told to look elsewhere first
…not this repo - refactor(cmd/build_qemu): call winpe.* instead of local qemu helpers — WinPE ISO/WIM/bootloader logic now tracks go-winkit upstream releases - chore(cmd/init_macos): drop dead imagesDir() helper and its unused vagrantfileDir binding — no user-facing impact - chore(vm/qemu): delete tests for WinPE logic that moved upstream — that coverage now lives in go-winkit, so this repo no longer duplicates it - chore(images): remove the unused impure Dockerfile/Vagrantfile.macOS/package.json/pyproject.toml build path — eliminates a stale, unbuildable image variant contributors could mistakenly reach for - docs(scaffold): reword the Vagrantfile.linux template comment to stop pointing at the deleted images/entrypoint.sh — avoids sending scaffolded-project users to a dead file
Contributor
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
devcell | 8cb88d4 | Commit Preview URL Branch Preview URL |
Aug 28 2026, 07:11 AM |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Add cell start/stop, Windows VM engine, WireGuard tunnels, and declarative packages
Summary
Cells can now run detached (
cell start/cell stop), provision Windows VMs end-to-end viacell build --engine=qemu, tunnel outbound traffic through WireGuard peers, declare nixpkgs indevcell.toml, and inject a project-levelflake.nixat boot. Internally, Windows/WinPE tooling moved to external repos (go-winkit,go-regedit,go-wimlib,go-nixoci), nixhome moved todevcell-sh/community-home, and all legacy build paths (impure, Dockerfile, pure) were removed in favor of thin-only builds.Breaking changes
--impure,--pure,--no-thin,--thickflags removed: every Docker build now runs the thin nix-on-volume flow--nixhome,--local-nixhomeflags removed: nixhome source is now env-driven (DEVCELL_NIXHOME) or upstream-defaultedtask nix:validateandimage:pure:*tasks removed: use community-home's own validationChanges
cell start/cell stopfor detached container lifecycle;cell shellreuses a running container instead of duplicating itcell build --engine=qemuwith WinPE bootstrap, DISM offline servicing, Hyper-V/WSL2 transplant, and SSH-wait install pipelinedefault_commandso barecelllaunches a configured tool; deterministic session resume per tmux pane[docker]resource limits (mem_limit/cpu_limit/shm_size),[[wireguard]]VPN tunnels,[packages]for declarative nixpkgs, project-levelflake.nixinjection at boot--openrouterextends to codex/opencode;cell telemetry on/off/statuswith PostHogcell -cand agent flags no longer die at root-command parsing[[volumes]]deduplication prevents Docker "Duplicate mount point" errorsNeed help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.