docker

package
v0.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 16, 2026 License: Apache-2.0 Imports: 44 Imported by: 0

Documentation

Overview

Package docker is the docker engine: it builds `docker run`, `docker exec` and thin-image build argv, manages images, the shared nix-store volume and devcell containers, watches in-container boot progress, and implements `cell build df`, `cell build prune` and `cell cleanup`.

Index

Constants

View Source
const (
	ContainerProfileLink    = "/opt/devcell/.local/state/nix/profiles/profile"
	ContainerGenerationLink = "/opt/devcell/.local/state/nix/profiles/home-manager"
)

Symlinks inside a running container that resolve to its closure's volume-resident store paths. Resolved with `docker exec <c> readlink -f`.

View Source
const (
	// DefaultRegistry is the fallback registry prefix for devcell images.
	DefaultRegistry = "ghcr.io/devcell-sh/devcell"

	// BootContainerPath is the container-side path where the host boot
	// directory is bind-mounted. Hosts the Go-generated entrypoint script.
	BootContainerPath = "/tmp/devcell-boot"
)
View Source
const (
	// DefaultBuildCPUs is "0" — no CFS quota by default. The memory ceiling
	// plus the max-jobs derived from it are the OOM guard; a CPU quota only
	// starves per-job cores (nproc ignores it anyway) and slows the build.
	DefaultBuildCPUs = "0"
)

Resource ceiling for the thin-build container (CELL-359).

The builder used to run uncapped, so a `nix build` spike was arbitrated by the VM-wide OOM killer only after the whole VM was already starved. Capping trades that for a deterministic cgroup kill confined to the builder: sibling cells survive and the failure names its own cause.

These are ceilings, NOT reservations, and they are only emitted when they actually constrain the daemon — see clampBuildLimits. A ceiling at or above what the daemon has protects nothing, and dockerd rejects a --cpus larger than its CPU count outright ("range of CPUs is from 0.01 to 2.00", exit 125), which is fatal on a stock 2-CPU Colima VM.

Two properties make a ceiling safe to emit, and the first cut of CELL-359 shipped with neither:

  • nix's own concurrency must be derived from the ceiling. `--cpus` is a CFS bandwidth quota, not a core count: nproc inside a `--cpus 4` container still reports every host CPU, so `max-jobs = auto` kept scheduling one job per host CPU. Eight concurrent derivations under an 8 GiB ceiling is ~1 GiB each, and `npm ci` over a large dependency tree needs several — the cgroup OOM killer took it out mid-install ("Killed", exit 137).

  • The ceiling cannot lean on swap as a cushion. Leaving --memory-swap unset gives Docker's 2x default, but Lima and Docker Desktop VMs both run swapless in practice (SwapTotal: 0), so the ceiling is hard at --memory and the concurrency derived from it is the only thing keeping the build inside it.

View Source
const DefaultSquidImage = "docker.io/ubuntu/squid:latest@sha256:6a097f68bae708cedbabd6188d68c7e2e7a38cedd05a176e1cc0ba29e3bbe029"
View Source
const DefaultThinStoreVolume = "devcell-nix-store"

DefaultThinStoreVolume is the named Docker volume that holds the thin-mode /nix store across builds and cell runs. Single shared volume by default so stack rebuilds reuse the existing nix store.

View Source
const NixCoreImage = "docker.io/nixos/nix:2.34.9"

NixCoreImage is the default nix base image for thin builds. Prefer cfg.DefaultNixImage for the canonical constant; this alias exists for call sites inside the docker engine that don't take a config parameter.

View Source
const NixDFReportScript = NixHealthProbeScript + "\n" + nixRootsListScript

NixDFReportScript is the health probe plus the root/meta listing — still purely read-only.

View Source
const NixGCRootReportScript = `` /* 1045-byte string literal not displayed */

NixGCRootReportScript prints the current GC root state: how many roots, how many unique hashes, and details from each -meta file. Used by the prune preflight to give drift visibility before destructive operations. Exported for test assertions.

View Source
const NixHealthProbeScript = `` /* 1582-byte string literal not displayed */

NixHealthProbeScript emits one machine-readable line:

total=N stale=N hashes=N generations=N orphaned=N

total/stale: devcell root symlinks and how many dangle. hashes: distinct profile hashes (>1 means config/lock drift). generations/orphaned: per-user profile generations and how many no devcell root protects (reclaimable by `cell build prune --pure`).

View Source
const ProfilePath = "/opt/devcell/.local/state/nix/profiles/profile"

ProfilePath is the canonical location of the home-manager profile baked into every thin image by thin_build.go.

View Source
const SafeNixGCScript = `` /* 1451-byte string literal not displayed */

SafeNixGCScript is the shell script that performs project-aware nix garbage collection. It removes only orphaned profile generations that aren't protected by project-scoped GC roots, then runs nix-store --gc.

CELL-334: also reads *-meta files to identify and clean up stale roots (roots whose metadata exists but whose profile target is dangling). Reports stale root count for drift visibility.

Exported for test assertions.

View Source
const ThinEntrypointSentinel = "/nix/var/nix/profiles/devcell-tools/bin/tini"

ThinEntrypointSentinel is the path the thin image's ENTRYPOINT references (built by thin_build.go:259). Used as the hydration probe in cmd/root.go's auto-build gate (CELL-38).

Variables

View Source
var ErrBuilderRunning = errors.New("builder already running")

ErrBuilderRunning is returned when the app's builder slot is occupied by a live container. Callers must never remove it; wait or let it finish.

View Source
var ErrDockerDaemonUnreachable = errors.New("Docker is not running. Please start Docker and retry")

ErrDockerDaemonUnreachable is returned by DockerDaemonReachable when the docker daemon doesn't respond. Stable so callers can match on it.

View Source
var Modules []string

Modules is the list of extra nix modules composed on top of the stack. Set from CellConfig at startup.

View Source
var PerCellImage bool

PerCellImage tags user images per cell instead of per stack. Set from CellConfig at startup; defaults to false (stack-based).

View Source
var Registry = DefaultRegistry

Registry is the active container registry. Set via cfg.ResolvedRegistry() at startup; defaults to DefaultRegistry.

View Source
var Stack = "base"

Stack is the resolved nix stack name (e.g. "ultimate", "go"). Set from CellConfig at startup; defaults to "base".

Functions

func BootGroupCount

func BootGroupCount(cellCfg cfg.CellConfig) int

BootGroupCount is how many group rows bootGroups will draw for cellCfg: the three fixed groups and Environment, plus Services when the GUI is on. The cage sizes its boot popup from it before the engine builds the groups.

func BuildArgv

func BuildArgv(spec RunSpec, fs FS, lookPath func(string) (string, error)) []string

BuildArgv constructs the full docker run argv for the given spec. It is pure given injectable FS and LookPath.

func BuildCleanupPrompt

func BuildCleanupPrompt(closures []LiveClosure) string

BuildCleanupPrompt is the confirmation text for `cell cleanup`. States the retention rule (running-only) and how many containers stay protected — the primary non-debug UX for the reaper.

func BuildCleanupScript

func BuildCleanupScript(closures []LiveClosure) string

BuildCleanupScript emits the reaper shell: remove gcroots/devcell/ entries whose hash is not in the live set. Only root symlinks and their -meta files are touched — never auto/ roots (namespace-local, CELL-330) and never the store itself (that's `cell build prune`'s job).

func BuildExecArgv

func BuildExecArgv(spec ExecSpec) []string

BuildExecArgv constructs a docker exec argv for attaching to a running container.

func BuildLabel

func BuildLabel(prefix, stack string, explicit bool) string

BuildLabel renders a progress label for an image build. When the stack was explicitly chosen by the user (TOML `stack = "..."` or `--stack`/env override), the qualifier is surfaced so they know what's about to run. Otherwise it's omitted because stacks are deprecated (CELL-6/337) and surfacing the resolved-default misleads — implies an opt-in that wasn't made.

func BuildPrunePrompt

func BuildPrunePrompt(opts PruneOpts) string

BuildPrunePrompt returns the user-facing confirmation warning text for the given prune mode. All prompts share the format:

⚠  This will delete ALL <specific-list>.
   Target: <host-specific-detail>
   Continue? [y/N]

The exact list and target line are mode-specific, per CELL-101.

func BuilderContainerState

func BuilderContainerState(ctx context.Context, name string) (exists, running bool)

BuilderContainerState reports whether a container with the given name exists and whether it is currently running.

func BuilderOrphanedByCancel

func BuilderOrphanedByCancel(runErr, ctxErr error) bool

BuilderOrphanedByCancel is true when the `docker run` client died because the caller's context was cancelled (Ctrl-C / SIGTERM). The client is SIGKILLed but the container keeps running — an orphan holding the shared /nix volume — so the caller must remove its own builder in that case.

func CleanupOrphanedProxies

func CleanupOrphanedProxies(ctx context.Context, skipCellName string) error

CleanupOrphanedProxies finds proxy sidecars whose corresponding cell container is no longer running, and removes them along with their internal networks. skipCellName is the current cell being started: its gateway is not orphaned, just pre-created before the container boots.

func ClosureAliveArgv

func ClosureAliveArgv(volume, image string) []string

ClosureAliveArgv returns the docker argv for the closure-alive probe. Runs inside the target image with the nix volume mounted so the profile's /nix/store/... symlink target can be resolved.

func ClosureDeadWarning

func ClosureDeadWarning(alive bool) (warning string, dead bool)

ClosureDeadWarning returns a user-facing warning and dead=true when the closure probe reports the image's nix paths are gone.

func CollectRunningContainers

func CollectRunningContainers(ctx context.Context) ([]string, error)

CollectRunningContainers returns `docker ps` output for display.

func CollectVolumeMounts

func CollectVolumeMounts(ctx context.Context) map[string][]string

CollectVolumeMounts returns a map of volume name → container names that mount it.

func ConfirmDestructive

func ConfirmDestructive(out io.Writer, in io.Reader, skipYes bool, isTTY bool, warning string) bool

ConfirmDestructive prints `warning` to out, reads one line from in, and returns true iff the user typed y/Y/yes/YES (case-insensitive, trimmed).

skipYes=true bypasses the prompt entirely (used for `-y` / `--yes`). The warning is not printed in this case — the user already opted in.

isTTY=false without skipYes refuses to proceed (returns false after printing a refusal message that mentions --yes). This prevents accidental destruction when stdin is piped (e.g. `echo y | cell build prune --force`).

func ConfirmProceed

func ConfirmProceed(out io.Writer, in io.Reader, isTTY bool, warning string) bool

ConfirmProceed is the default-YES twin of ConfirmDestructive: a nudge, not a gate. Prints the warning; bare Enter or anything except n/no proceeds. Non-TTY prints the warning and proceeds unconditionally — automation is never blocked by a hygiene nudge.

func ConsumeContainerEventsPanel

func ConsumeContainerEventsPanel(events <-chan ContainerEvent, panel ux.BootView) bool

ConsumeContainerEventsPanel reads JSONL events from a ContainerLogWatcher and updates the boot panel. Handles both system events (from _notify in the entrypoint) and service events (from devcell-event in s6 services). Returns true when boot.ready arrived (clean finish), false on channel close.

func ContainerLabel

func ContainerLabel(ctx context.Context, name, key string) string

ContainerLabel returns the value of a Docker label on a running or stopped container, or "" if the container or label is missing.

func ContainerRunning

func ContainerRunning(ctx context.Context, name string) bool

ContainerRunning checks if a container with the given name is currently running.

func DebugArgv

func DebugArgv(argv []string) string

DebugArgv renders an argv for --debug output. Multi-line elements (embedded `sh -c` scripts) are elided to a one-line placeholder — dumping the probe script made the health check look like it printed the script instead of running it.

func DetectArch

func DetectArch() string

DetectArch returns "aarch64" or "x86_64". Respects DEVCELL_ARCH env override ("amd64"→"x86_64", "arm64"→"aarch64") for cross-architecture builds.

func DockerBindProbeArgv

func DockerBindProbeArgv(image, volume, source, marker string) []string

DockerBindProbeArgv composes the non-mutating debug probe command.

func DockerDaemonReachable

func DockerDaemonReachable(ctx context.Context) error

DockerDaemonReachable probes the daemon with a single `docker info` call. Cheap (~30ms warm) and avoids the confusing fallout (`Pulling core image nixos/nix:latest failed`) when the socket is unreachable. Call once at the top of any path that will later invoke docker (CELL-44).

func DockerHostPath

func DockerHostPath(p string) string

DockerHostPath translates container-local paths (e.g. /devcell-256/nixhome) to Docker-accessible host paths when running inside a devcell container. Uses DEVCELL_HOST_PROJECT_DIR env var set by the entrypoint.

func DockerPlatform

func DockerPlatform(arch string) string

DockerPlatform returns the docker --platform value for the given nix arch.

func DockerResolveContainerLink(ctx context.Context, container, link string) (string, error)

DockerResolveContainerLink resolves a symlink inside a running container to its final target — the only namespace where /opt/devcell resolves.

func DockerRunningDevcellContainers

func DockerRunningDevcellContainers(ctx context.Context) ([]string, error)

DockerRunningDevcellContainers lists RUNNING devcell containers by the devcell.basedir label every `cell` launch stamps (runner.go BuildArgv). Running-only on purpose: `docker ps` without -a (CELL-334 retention decision, 2026-08-01).

func DockerVolumeDebug

func DockerVolumeDebug(ctx context.Context, volume string) string

DockerVolumeDebug returns daemon-side metadata for a named volume.

func EnsureIsolatedNetwork

func EnsureIsolatedNetwork(ctx context.Context, name string, isolateGateway bool) error

EnsureIsolatedNetwork creates an --internal docker network if it doesn't exist. If isolateGateway is true, the bridge gateway mode is set to "isolated" so the host is unreachable from containers on the network. Returns an error if the network exists but is not internal.

func EnsureNetwork

func EnsureNetwork(ctx context.Context) error

EnsureNetwork creates the devcell-network docker network if it doesn't exist.

func EnsureProxySidecar

func EnsureProxySidecar(ctx context.Context, opts ProxySidecarOpts) error

EnsureProxySidecar starts the squid proxy sidecar container. It generates the squid config, writes it to disk, starts the container on the internal network, connects it to the egress network, and waits for TCP readiness on port 3128.

func EnsureVolumeShell

func EnsureVolumeShell(ctx context.Context, volumeName, coreImage string) error

EnsureVolumeShell ensures the nix volume contains the shell closure from the core image so /bin/sh resolves when the volume mounts at /nix.

The nixos/nix image's /bin/sh is a symlink into /nix/store/<hash>/bin/bash. When the shared nix-store volume mounts over /nix, the symlink target must exist on the volume. A stale volume (from a prior image version) has different store hashes, making /bin/sh dangle and the thin build unbootable.

The fix: mount the volume at /mnt/nix (leaving the image's own /nix intact) and copy the default profile's closure to the volume. This also covers coreutils, nix, git, and other tools the thin build script needs.

No-op when the volume does not exist yet (Docker auto-populates on first mount) or the closure is already present.

func EnsureVolumeShellArgv

func EnsureVolumeShellArgv(volumeName, coreImage string) []string

EnsureVolumeShellArgv returns the docker argv that bootstraps the nix volume with the core image's shell closure. Pure for testing.

func FlakeNixImage

func FlakeNixImage() string

FlakeNixImage returns the lightweight nixos/nix image used for flake lock/update and stack discovery. Decoupled from the binary version so dev builds with -dirty tags don't require a registry push.

func FormatJSON

func FormatJSON(snap DFSnapshot, opts FormatOpts, w io.Writer) error

FormatJSON emits a stable schema for scripting. Keys are camelCase so `jq` recipes can be pinned across releases. The order of entries is the same as FormatTable — sorted by ReclaimBytes desc — so a single `jq '.entries[0]'` always picks the biggest reclaimable thing.

func FormatNixStoreSection

func FormatNixStoreSection(r NixStoreReport, w io.Writer)

FormatNixStoreSection renders the nix block of `cell build df`. An empty report (no volume data) renders nothing.

func FormatTable

func FormatTable(snap DFSnapshot, opts FormatOpts, w io.Writer) error

FormatTable renders the ranked entries as a human-readable table with a totals footer and bottom reclaim hint. Mirrors the layout shown in the `cell build df` design (CELL-98).

func FormatTableWithVM

func FormatTableWithVM(snap DFSnapshot, opts FormatOpts, vm VMDiskInfo, containers []string, volMounts map[string][]string, w io.Writer) error

FormatTableWithVM renders the ranked entries table plus Docker VM disk info, running containers, volume-to-container mapping, and cleanup instructions.

func GenerateACLFiles

func GenerateACLFiles(allow, block []string) map[string]string

GenerateACLFiles classifies allow and block entries into domain and CIDR files suitable for squid dstdomain and dst ACLs. Returns a map of filename to file content.

func GenerateSquidConf

func GenerateSquidConf(mode string) string

GenerateSquidConf returns a complete squid.conf for the proxy sidecar. mode is "allow" (deny by default) or "block" (allow by default).

func HydrateNixCache

func HydrateNixCache(ctx context.Context, volumeName, registry, arch string) error

HydrateNixCache pulls the nix cache image from GHCR into the named Docker volume. Shows a progress spinner. Errors are non-fatal: a failed pull means the build runs from scratch (slower but correct).

func ImageExists

func ImageExists(ctx context.Context, tag string) bool

ImageExists returns true if a Docker image with the given tag exists locally.

func ImageExistsForPlatform

func ImageExistsForPlatform(ctx context.Context, tag, platform string) bool

ImageExistsForPlatform returns true if a Docker image with the given tag exists locally AND matches the requested platform (e.g. "linux/amd64"). Empty platform falls back to ImageExists (host default).

func ImageLabel

func ImageLabel(ctx context.Context, image, key string) string

ImageLabel returns one label of a local image, or "" when the image or the label is missing.

func ImageVersions

func ImageVersions(ctx context.Context) (base, user string)

ImageVersions reads build metadata from the user image. Returns (base, user) strings for backward compatibility with callers. Format: user = "<commit> built <date>" when both known,

"built <date>" when only date,
"<commit>" when only commit,
"" otherwise (falls through to legacy file read).

func InternalNetworkName

func InternalNetworkName(cellName string) string

InternalNetworkName returns the internal network name for a given cell.

func KVMProbeArgv

func KVMProbeArgv() []string

KVMProbeArgv returns the argv for a throwaway container that tests whether the Docker daemon host exposes /dev/kvm. The container does nothing: if docker can attach the device node the exit code is 0; otherwise docker itself errors out before the container starts.

func LocalImageIDFor

func LocalImageIDFor(ctx context.Context, tag string) (string, error)

LocalImageIDFor returns the full image ID (sha256:...) for tag. Used to pin the running container to the exact image just built, rather than the mutable tag which could race with a concurrent build.

func LocalImageSize

func LocalImageSize(ctx context.Context, tag string) int64

LocalImageSize returns the on-daemon size in bytes of the local image with the given tag. Used post-build to show "Loaded: 1.4 GB" alongside the spinner success — skopeo's per-blob progress is empty when stdout isn't a TTY, so we synthesize a summary from `docker image inspect` instead.

Returns 0 on any error; callers treat 0 as "size unknown, skip the summary".

func NixCacheDockerArch

func NixCacheDockerArch(arch string) string

NixCacheDockerArch normalizes a nix-style arch ("aarch64", "x86_64") to Docker convention ("arm64", "amd64").

func NixCacheImageRef

func NixCacheImageRef(registry, arch string) string

NixCacheImageRef returns the GHCR nix cache image reference for the given registry and Docker-convention arch (arm64 or amd64).

func NixHealthProbeArgv

func NixHealthProbeArgv(volume string) []string

NixHealthProbeArgv wraps the probe in a docker-run of the given volume.

func ParseClosureAliveResult

func ParseClosureAliveResult(stdout string, err error) (resolvedPath string, alive bool)

ParseClosureAliveResult interprets the probe's stdout + error. Returns the resolved store path and whether the closure is alive.

func ParseSize

func ParseSize(s string) (int64, error)

ParseSize handles docker's CLI size format. Examples:

"29.8GB" → 29_800_000_000   (decimal GB, not GiB — docker convention)
"813.6kB" → 813_600         (lowercase k, like docker prints)
"0B" → 0
"N/A" or "" → -1            (volume with no size info)

Returns -1 for unknown so the caller can distinguish "small but real" from "size not reported."

func PickImageTag

func PickImageTag(impure bool) string

PickImageTag is the single seam every runtime caller (`cell claude`, `cell shell`, `cell codex`, `cell gemini`, …) uses to decide which local image variant to exec into.

After the 2026-05-15 flip (CELL-183) + CELL-165 vocab rename, pure is the default and `impure` (was `debian`) is the opt-in legacy path:

impure=false (default):    UserImageTagPure() (nix2container, devcell-user:<stack>-pure)
impure=true  (--impure):   UserImageTag()    (bare Dockerfile-built, devcell-user:<stack>)

func PickImageTagThin

func PickImageTagThin() string

PickImageTagThin returns the thin image tag for runtime callers.

func PrepareWireguard

func PrepareWireguard(cellHome string, cellCfg cfg.CellConfig) error

PrepareWireguard writes WireGuard config files for each enabled entry to <cellHome>/.wg/<name>.conf. PrivateKey lines are stripped from the config; a PostUp directive loads the key from /run/secrets/wg-private-key at runtime. No-op when no entries are enabled.

func ProbeDockerBind

func ProbeDockerBind(ctx context.Context, image, volume, source, marker string) (string, error)

ProbeDockerBind verifies both that the daemon accepts source and that marker is visible inside a container. It is intended for --debug diagnostics only.

func ProbeKVM

func ProbeKVM(ctx context.Context) bool

ProbeKVM runs a throwaway container to detect /dev/kvm on the daemon host. Results are cached for kvmCacheTTL so the ~100ms probe runs at most once per session.

func ProjectNixpkgsRev

func ProjectNixpkgsRev(baseDir string) (string, error)

ProjectNixpkgsRev reads the scaffolded flake.lock under <baseDir>/.devcell/flake.lock and returns nodes.nixpkgs.locked.rev. A missing or unparsable lock returns "" without error — the warning simply won't fire (never fatal, never blocking).

func ProxyConfDir

func ProxyConfDir(cellHome, cellName string) string

ProxyConfDir returns the host-side directory for squid config files.

func ProxySidecarName

func ProxySidecarName(cellName string) string

ProxySidecarName returns the gateway sidecar container name for a given cell.

func PullImageForPlatform

func PullImageForPlatform(ctx context.Context, tag, platform string, verbose bool) error

PullImageForPlatform pulls a Docker image for a specific platform. Empty platform uses Docker's default (host architecture). When verbose is true, docker pull output is streamed to os.Stderr.

func ReclaimBuilderSlot

func ReclaimBuilderSlot(name string, exists, running bool) (remove bool, err error)

ReclaimBuilderSlot decides what to do with a pre-existing builder container of the same name before starting a new build: nothing when absent, remove when it exited (a crashed or interrupted run left it behind), and refuse — never kill — when it is running.

func RemoveNetwork

func RemoveNetwork(ctx context.Context, name string) error

RemoveNetwork removes a docker network by name. Best-effort.

func RemoveOrphanedContainer

func RemoveOrphanedContainer(ctx context.Context, name string) error

RemoveOrphanedContainer removes a stopped container with the given name if it exists. Returns nil if the container doesn't exist or was successfully removed. Returns an error if the container is currently running.

func RemoveProxySidecar

func RemoveProxySidecar(ctx context.Context, name string) error

RemoveProxySidecar removes the proxy sidecar container. Best-effort.

func ResolveBuildTag

func ResolveBuildTag(custom, derived string) string

ResolveBuildTag returns the tag a `cell build` invocation should use: custom (typically from --image) when non-empty, falling back to the auto-derived stack tag. Trims whitespace on custom to forgive copy-paste.

func ResolveTrustFlake

func ResolveTrustFlake(baseDir, cellHome string) bool

resolveTrustFlake checks if the project has a flake.nix and whether the user has trusted it. On first encounter, prompts interactively and caches the answer in cellHome. Returns true if DEVCELL_FLAKE_TRUST=1 should be passed to the container. ResolveTrustFlake answers "install this project's flake.nix packages?" once per workspace: a recorded answer wins, otherwise the user is asked on the terminal. The cage calls it before opening its boot popup, since no prompt can be answered while the popup owns the keyboard.

func RunCleanup

func RunCleanup(a RunCleanupArgs) error

RunCleanup orchestrates the reaper: build the plan, prompt, execute. Rejection is a clean no-op (user intent, not an error).

func RunDF

func RunDF(a RunDFArgs) error

RunDF orchestrates: collect → parse → format. The pure-data steps live in df.go; this function just wires them.

func RunPrune

func RunPrune(a RunPruneArgs) error

RunPrune orchestrates: build the step plan, prompt the user, execute the steps. DryRun steps are printed but not executed. IgnoreError steps don't abort the loop on failure. Returns nil if the prompt was rejected (not an error — user intent).

func ShouldHydrateFromCache

func ShouldHydrateFromCache(hydrated bool, getenv func(string) string) bool

ShouldHydrateFromCache decides whether to pull the nix cache before building. Returns true when the volume is not yet hydrated AND the user hasn't disabled cache hydration via DEVCELL_NIX_CACHE=0.

func StaleCellWarning

func StaleCellWarning(projectRev string, h NixStoreHealth) (string, bool)

StaleCellWarning decides whether this project's closure is behind the newest rev live on the volume, and renders the warning if so. Unknown revs on either side mean silence — a nudge must never fire on guesswork.

func StampRootsScript

func StampRootsScript(closures []LiveClosure) string

StampRootsScript emits the shell that stamps hash-named GC roots for every live closure. Idempotent — re-stamping the same closure is a no-op (`ln -sfT` to the identical target). Only creates; never deletes, never GCs. Empty input yields an empty script (nothing to stamp).

func ThinBuildArgvFull

func ThinBuildArgvFull(coreImage, containerName, volumeName, nixhomeRef, thinTag, hmTarget, arch, stack, modules, projectName string) []string

ThinBuildArgvFull composes the docker run argv for the thin build. Runs nixos/nix with the nix store volume + docker socket. Inside: home-manager switch (uses volume cache), then docker build to produce the thin image (nix-core + config, no /nix/store baked in).

hmTarget is the home-manager flake target name (typically "local" for thin); stack is the user-facing stack name written to DEVCELL_STACK/metadata.json; modules is a CSV of module names written to DEVCELL_MODULES (CELL-41); projectName is the project directory basename used to scope GC roots so different projects don't overwrite each other's nix store references (CELL-320).

nixhomeRef accepts EITHER:

  • a filesystem path (e.g. /home/bob/nixhome): the caller streams a tar archive to stdin, which is extracted at /opt/nixhome before home-manager
  • a flake reference (e.g. github:devcell-sh/home/main): no archive; home-manager runs against `<ref>#devcell-<stack><arch>` directly, letting nix fetch and cache under /nix/store. This is the clean-machine path (CELL-38), no local nixhome required.

Detected by prefix: anything starting with a flake-scheme (github:, git+, path:, http:, etc.) is treated as remote; everything else is treated as a local filesystem path.

func ThinBuildArgvWithPackages

func ThinBuildArgvWithPackages(coreImage, containerName, volumeName, nixhomeRef, thinTag, hmTarget, arch, stack, modules, projectName, packages string) []string

ThinBuildArgvWithPackages is ThinBuildArgvFull plus the per-module package counts ("desktop: 69, base: 35") stamped on the image as the devcell.packages label, so `cell <agent>` can show them at start without evaluating nix again. Empty when the build could not evaluate them.

func ThinBuilderContainerName

func ThinBuilderContainerName(appName string) string

ThinBuilderContainerName names the thin builder for one cell (`<project>-<cellID>`, the same key as `devcell-<cell>`). One fixed name for every project meant a build starting in project B force-removed — SIGKILLed, exit 137 with no OOM anywhere — the build project A had in flight.

func ThinStoreVolume

func ThinStoreVolume() string

ThinStoreVolume returns the volume name to use for the thin /nix store. Reads DEVCELL_NIX_VOLUME (trimmed); falls back to DefaultThinStoreVolume. When DEVCELL_ARCH requests a non-native architecture (e.g. amd64 on an ARM host), the default volume name gets an arch suffix so cross-arch builds use a separate nix store — mixing architectures on a single volume wastes space and causes dangling-symlink issues on every build.

func UpdateFlakeLock

func UpdateFlakeLock(ctx context.Context, configDir string, lockOnly bool, verbose bool, out io.Writer) error

UpdateFlakeLock runs nix flake lock (or update) inside a lightweight nixos/nix container with configDir bind-mounted.

func UserImageTag

func UserImageTag() string

UserImageTag returns the user's local image tag — the bare-name local devcell image. Unchanged across the 2026-05-15 flip: this name is the user's "current image" concept and existing local images keep working.

Default (stack-based): devcell-user:<stack> or devcell-user:<stack>-<mod1>-<mod2>-<sha8> Legacy (per_cell_image=true): devcell-user:<cell>

Used by:

  • `cell <agent> --impure` (PickImageTag(true) → bare local tag)
  • `cell build --impure` (docker build → tags this name)

Override with DEVCELL_USER_IMAGE env var.

func UserImageTagPure

func UserImageTagPure() string

UserImageTagPure returns the local tag for nix2container-built (pure) images — the DEFAULT path after the 2026-05-15 flip (CELL-189). Same repo as UserImageTag with a "-pure" suffix.

Example: UserImageTag()="devcell-user:ultimate" → UserImageTagPure()="devcell-user:ultimate-pure".

func UserImageTagThin

func UserImageTagThin() string

UserImageTagThin returns the local tag for thin images — nix store lives on a Docker named volume, not baked into the image.

Resolution order:

  1. DEVCELL_USER_IMAGE_THIN — legacy explicit override
  2. DEVCELL_USER_IMAGE — modern unified override; used as-is (no suffix). CELL-286 prep: every devcell image we publish is thin, so the `-thin` suffix on the env-driven path is redundant. CI sets `DEVCELL_USER_IMAGE=ghcr.io/devcell-sh/devcell:v<ver>-<arch>` and expects this exact tag at runtime (no suffix appended).
  3. UserImageTag() + "-thin" — local-dev fallback, preserves the suffix convention so `devcell-user:<stack>-thin` doesn't collide with `-pure`/`-impure` legacy local tags.

Example (no env): UserImageTag()="devcell-user:ultimate" → "devcell-user:ultimate-thin".

func ValidateProxyCompatibility

func ValidateProxyCompatibility(c cfg.CellConfig) error

ValidateProxyCompatibility checks that no docker/cell settings conflict with proxy-mode network isolation. Returns nil when everything is compatible. Each error names the offending TOML key and suggests --force-insecure-network.

The denied caps and mount paths come from SolitaryPolicy as the canonical source of truth, keeping validation and BuildArgv enforcement in sync.

func VolumeContains

func VolumeContains(ctx context.Context, volumeName, sentinelPath string) bool

VolumeContains spawns a throwaway busybox container (~5 MB) to test whether a sentinel path exists inside the named volume. sentinelPath must be the absolute in-container path the image's ENTRYPOINT references (e.g. /nix/var/nix/profiles/devcell-tools/bin/tini); see VolumeContainsArgv for the mount-layout rationale.

func VolumeContainsArgv

func VolumeContainsArgv(volumeName, sentinelPath string) []string

VolumeContainsArgv returns the docker argv used by VolumeContains. Pure so the mount layout can be unit-tested without spawning docker.

The volume MUST be mounted at /nix (not /probe). Nix profile entries are absolute symlinks rooted at /nix/store/...; mounting elsewhere makes `test -e` follow a dangling target and report MISSING for a perfectly hydrated volume (CELL-45). Mounting at /nix matches what the builder and runner already do, so absolute symlinks resolve in-container.

func VolumeExists

func VolumeExists(ctx context.Context, name string) bool

VolumeExists shells out to `docker volume inspect` — returns true on exit 0.

func VolumeHydrated

func VolumeHydrated(volumeName, sentinelPath string, exists func(string) bool, probe func(string, string) bool) bool

VolumeHydrated reports whether a named Docker volume exists AND contains the given sentinel path. Use to gate auto-build in `cell shell`/`claude`/... so a stale or pruned `/nix` volume triggers a rebuild instead of failing later at the kernel's `exec: tini: no such file or directory` (CELL-40/332).

Pure function — caller injects the two probes so unit tests don't need docker. For runtime use, see VolumeExists and VolumeContains below.

func WriteThinBuildContext

func WriteThinBuildContext(w io.Writer, buildDir string) error

WriteThinBuildContext writes the local nixhome overlay needed by the thin builder. Streaming this through the Docker API avoids asking a VM-backed daemon to resolve a host path for a second-generation container.

Types

type BuildLimits

type BuildLimits struct {
	Memory  string // --memory value, "" to omit
	CPUs    string // --cpus value, "" to omit
	MaxJobs int    // nix max-jobs, 0 to leave at "auto"
	Cores   int    // nix cores, 0 to leave at nix's own default
}

BuildLimits is what the builder may use: the docker ceilings plus the nix concurrency derived from them. A zero field means "emit nothing".

func ResolveBuildLimits

func ResolveBuildLimits() BuildLimits

ResolveBuildLimits returns the build resource limits that will be applied. Exported for debug logging in cmd/build.go.

type ContainerEvent

type ContainerEvent struct {
	TS      time.Time `json:"ts"`
	Scope   string    `json:"scope"`
	Service string    `json:"service,omitempty"`
	Status  string    `json:"status,omitempty"`
	Msg     string    `json:"msg,omitempty"`
}

ContainerEvent is one parsed JSONL event from docker logs.

type ContainerLogWatcher

type ContainerLogWatcher struct {
	// contains filtered or unexported fields
}

ContainerLogWatcher follows docker logs for a running container, parses JSONL lines into ContainerEvent structs, and emits them on a buffered channel. Non-JSONL lines are silently skipped.

func NewContainerLogWatcher

func NewContainerLogWatcher(ctx context.Context, container string, since time.Time) *ContainerLogWatcher

NewContainerLogWatcher starts `docker logs --follow --since=<since> <container>` and parses JSONL into ContainerEvents on the returned watcher's channel.

func (*ContainerLogWatcher) Close

func (w *ContainerLogWatcher) Close()

Close stops the docker logs subprocess and waits for the reader goroutine.

func (*ContainerLogWatcher) Events

func (w *ContainerLogWatcher) Events() <-chan ContainerEvent

Events returns the channel of parsed container events.

type DFCache

type DFCache struct {
	ID           string
	Description  string
	SizeBytes    int64
	InUse        bool
	Shared       bool
	UsageCount   int
	CreatedSince string
}

type DFCollector

type DFCollector interface {
	CollectSystemDF(ctx context.Context) ([]byte, error)
}

DFCollector is the test seam for `cell build df` — production wires ExecCollector, which shells to `docker system df -v --format json`. Tests inject a fake that returns a canned JSON fixture.

type DFContainer

type DFContainer struct {
	ID        string
	Names     string
	ImageRef  string // "sha256:..." — cross-ref to DFImage.ID
	State     string
	SizeBytes int64 // writable layer size
}

type DFImage

type DFImage struct {
	ID           string // full "sha256:..." form, as docker returns it
	Repository   string
	Tag          string
	Containers   int   // pin count (0 = orphan, candidate for `docker image rm`)
	SizeBytes    int64 // total size
	UniqueBytes  int64 // bytes that would actually free on removal
	SharedBytes  int64
	CreatedSince string
}

type DFOpts

type DFOpts struct {
	TopN  int         // 0 = all rows
	Kinds []EntryKind // empty = all kinds
	JSON  bool        // true → FormatJSON, false → FormatTable
}

DFOpts controls a single `cell build df` invocation.

type DFSnapshot

type DFSnapshot struct {
	Images     []DFImage
	Containers []DFContainer
	Volumes    []DFVolume
	BuildCache []DFCache
}

DFSnapshot is the typed shape of `docker system df -v --format json`. All numeric fields arrive from docker as strings ("29.8GB", "3", "N/A") and are parsed once via parseSize / strconv.Atoi at unmarshal time.

func ParseSystemDF

func ParseSystemDF(raw []byte) (DFSnapshot, error)

ParseSystemDF unmarshals the JSON blob produced by `docker system df -v --format json` into typed structs with parsed sizes.

type DFVolume

type DFVolume struct {
	Name      string
	Links     int   // pin count
	SizeBytes int64 // -1 when docker reports "N/A"
}

type DockerCapacity

type DockerCapacity struct {
	NCPU     int
	MemBytes int64
}

DockerCapacity is what the build daemon advertises for itself. Note this is the daemon's own ceiling, which on macOS is the Colima/Docker Desktop VM — not the host Mac's specs, and not necessarily the daemon this process runs under if contexts differ.

type DockerDebugInfo

type DockerDebugInfo struct {
	Context       string
	Endpoint      string
	DockerHostEnv string
	Runtime       string
	Name          string
	ServerVersion string
	OperatingOS   string
	Architecture  string
	RootDir       string
	CPUs          int
	MemoryBytes   int64
	Socket        string
	SocketTarget  string
}

DockerDebugInfo describes the client-to-daemon connection used by Cell. It deliberately excludes registry and authentication configuration.

func CollectDockerDebugInfo

func CollectDockerDebugInfo(ctx context.Context) (DockerDebugInfo, error)

CollectDockerDebugInfo reports the actual daemon selected by the current Docker CLI environment. This matters inside a cell, where /var/run/docker.sock was fixed when the outer container was created.

type Engine

type Engine struct{}

Engine runs a cell as a docker container built from a thin image: a small image whose /nix lives on the shared nix-store volume. It registers itself as engine.Docker.

Docker-only commands (cell stop, cell cleanup, cell build df, cell build prune) are not engine verbs; cmd/ calls this package for them directly.

func (Engine) Build

func (Engine) Build(ctx context.Context, opts engine.BuildOpts) error

Build builds the cell's thin image: it syncs the nixhome flake into the build dir, writes the overlay flake for the cell's stack and modules, and runs home-manager and `docker build` in a builder container. opts.Stack (else DEVCELL_STACK) overrides the stack, opts.Image the tag, and opts.Update empties the nix-store volume first. opts.DryRun, opts.Force, opts.NoCache and opts.Stage are not honored.

func (Engine) Init

func (Engine) Init(_ context.Context, opts engine.InitOpts) error

Init scaffolds devcell.toml and the .devcell/ build context in the project, then locks the nix flake inputs, or updates them with opts.Update or opts.Force. It builds no image; Build does.

func (Engine) Run

func (Engine) Run(ctx context.Context, opts engine.RunOpts) error

Run acquires the thin image (building it when it is missing, the nix-store volume is unpopulated, or opts.Rebuild is set), runs the boot checklist, and runs the agent in a new container with the terminal attached. With opts.Detach the container starts in the background and Run returns once docker started it; with opts.DryRun it prints the docker run argv instead. A non-zero agent exit is *engine.ExitError.

Part of the checklist is engine-neutral but so far runs only here: the system prompt, git identity, 1Password secrets, the OpenRouter key, the Claude session ID, the cell home backup, the cellrun record and the command_finish telemetry event. opts.Force, opts.Update and opts.Background are not honored; --force and --stack=<name> in opts.Args reach the agent.

type EntryKind

type EntryKind string

EntryKind tags a RankedEntry's origin so the formatter can render the right reclaim hint and column heading.

const (
	EntryKindImage     EntryKind = "image"
	EntryKindContainer EntryKind = "container"
	EntryKindVolume    EntryKind = "volume"
	EntryKindCache     EntryKind = "cache"
)

type ExecCollector

type ExecCollector struct{}

ExecCollector is the real collector. It runs docker once and returns the raw JSON for ParseSystemDF to handle.

func (ExecCollector) CollectSystemDF

func (ExecCollector) CollectSystemDF(ctx context.Context) ([]byte, error)

type ExecSpec

type ExecSpec struct {
	ContainerName string
	User          string
	Binary        string
	Args          []string
	TTY           bool
	Env           map[string]string
}

ExecSpec holds the parameters for building a docker exec argv.

type FS

type FS interface {
	Stat(path string) error
}

FS abstracts filesystem stat for testability.

var OsFS FS = FSFunc(func(path string) error {
	_, err := os.Stat(path)
	return err
})

OsFS is the real filesystem implementation.

type FSFunc

type FSFunc func(string) error

FSFunc is a function that implements FS.

func (FSFunc) Stat

func (f FSFunc) Stat(path string) error

type FormatOpts

type FormatOpts struct {
	TopN  int         // 0 means "all"
	Kinds []EntryKind // empty means all kinds
}

FormatOpts controls FormatTable / FormatJSON output.

type ImageMetadata

type ImageMetadata struct {
	BaseImage string   `json:"base_image"`
	Stack     string   `json:"stack"`
	Modules   []string `json:"modules"`
	GitCommit string   `json:"git_commit"`
	BuildDate string   `json:"build_date"`
	Packages  int      `json:"packages"`
}

ImageMetadata holds structured build metadata from /etc/devcell/metadata.json.

func ImageMetadataFromContainer

func ImageMetadataFromContainer(ctx context.Context) ImageMetadata

ImageMetadataFromContainer reads build metadata for the current launch's image.

Source-of-truth flip (2026-05-16): the date/rev now come from the OCI image manifest (labels + Created field) rather than /etc/devcell/metadata.json inside the image. Why: when metadata.json carried a real per-build timestamp, every `cell build` invocation perturbed homeRoot's tar hash and forced skopeo to re-push the ~3.9GB customization layer even when no source had changed. Pinning metadata.json to static placeholders eliminates that; the date moves to OCI manifest labels (which only affect the tiny manifest blob, not layer content).

Reads:

  • .Created — OCI manifest creation timestamp (= our buildDate)
  • .Config.Labels — devcell.stack, org.opencontainers.image.revision, etc.
  • .Config.Env — DEVCELL_STACK (falls back to DEVCELL_PROFILE for older images)

Falls back to a runtime `cat /etc/devcell/metadata.json` for older images that predate the manifest-based stamping.

func ParseImageMetadata

func ParseImageMetadata(data []byte) ImageMetadata

ParseImageMetadata parses JSON into ImageMetadata. Returns zero value on error.

type LiveClosure

type LiveClosure struct {
	Container      string
	ProfilePath    string // /nix/store/<hash>-home-manager-path
	GenerationPath string // /nix/store/<hash>-home-manager-generation
}

LiveClosure is one running container's protected store paths.

func CollectLiveClosures

func CollectLiveClosures(
	ps func() ([]string, error),
	resolve func(container, link string) (string, error),
	logf func(format string, args ...any),
) ([]LiveClosure, error)

CollectLiveClosures enumerates running devcell containers via ps and resolves each one's profile + generation store paths via resolve. Dependency-injected for tests; the runtime call site wires `docker ps` and `docker exec readlink -f`.

A running container whose closure cannot be resolved fails the whole collection: an unknown closure means neither the reaper nor the prune gate can guarantee safety, so both must refuse — naming the cell.

logf, when non-nil, receives one line per datapoint (--debug visibility).

type NixRootEntry

type NixRootEntry struct {
	Name  string
	Stale bool
}

NixRootEntry is one GC root symlink on the volume.

type NixRootMeta

type NixRootMeta struct {
	Hash    string
	Project string
	Stack   string
	Nixpkgs string
}

NixRootMeta is the parsed -meta file for one root hash.

type NixStoreHealth

type NixStoreHealth struct {
	TotalRoots          int
	StaleRoots          int
	ProfileHashes       int
	Generations         int
	OrphanedGenerations int

	// Lock-drift datapoints from the *-meta files stamped at container
	// start (CELL-332). Zero values on pre-CELL-332 volumes. CELL-391
	// consumes these for the stale-cell warning.
	DistinctRevs   int    // distinct nixpkgs revs across metas
	NewestRev      string // rev with the most recent stamped= timestamp
	NewestProjects int    // how many metas sit on NewestRev
}

NixStoreHealth is the parsed probe result.

func ParseNixStoreHealth

func ParseNixStoreHealth(out string) (NixStoreHealth, error)

ParseNixStoreHealth finds the datapoint line in probe output (docker may prepend image-pull noise) and parses its key=value fields. Missing keys are zero values (older probes / pre-CELL-332 volumes emit fewer fields). No datapoint line means the probe degraded — callers treat that as "skip silently", never as fatal.

func (NixStoreHealth) Summary

func (h NixStoreHealth) Summary() (tag, detail string, warn bool)

Summary returns a short tag for the phase row, a longer diagnostic detail for debug output, and whether the result is a warning. The tag is what users see next to the dot; the detail carries counts and remediation hints.

type NixStoreReport

type NixStoreReport struct {
	Volume string
	Health NixStoreHealth
	Roots  []NixRootEntry
	Metas  []NixRootMeta
}

NixStoreReport is the full parsed df report for the nix volume.

func CollectNixStore

func CollectNixStore(ctx context.Context) (NixStoreReport, bool)

CollectNixStore runs the report against the thin store volume. Any failure returns ok=false — the df section is simply omitted, matching the CELL-390 degrade-to-silence contract.

func ParseNixStoreReport

func ParseNixStoreReport(volume, out string) (NixStoreReport, error)

ParseNixStoreReport parses combined probe + listing output.

type ProxySidecarOpts

type ProxySidecarOpts struct {
	CellName      string
	WorkspaceName string
	CellID        string
	WorkspaceHome string
	InternalNet   string
	EgressNet     string // "devcell-network"
	Proxy         cfg.NetworkProxySection
	SquidImage    string // defaults to DefaultSquidImage if empty
}

ProxySidecarOpts holds everything needed to start a proxy sidecar.

type PruneOpts

type PruneOpts struct {
	// GOOS is the target operating system: "darwin" or "linux".
	// Tests pass this explicitly; the runtime call site passes runtime.GOOS.
	GOOS string

	// Force enables nuclear cleanup mode (docker desktop wipe / qcow nuke /
	// aggressive nix GC). Default mode is the standard prune sequence.
	Force bool

	// Pure selects the nix path instead of the docker path.
	Pure bool

	// Rootless indicates a rootless Docker daemon on Linux. Only relevant
	// when GOOS == "linux" && !Pure && Force. The runtime call site
	// auto-detects this via `docker info`.
	Rootless bool

	// NixOS indicates /etc/NIXOS is present. Only relevant when
	// GOOS == "linux" && Pure && Force; triggers system-profile cleanup.
	NixOS bool

	// HomeDir is the resolved user home (e.g. /home/warden). Used to
	// compose absolute paths in macOS Docker Desktop wipe and the
	// `~/.cache/nix` wipe on Linux. Tests pass a fixed value.
	HomeDir string

	// LinuxBuilderHost is the SSH target for the macOS linux-builder VM.
	// Default: "builder@linux-builder".
	LinuxBuilderHost string

	// LiveClosures are the running containers' resolved store paths
	// (CollectLiveClosures). When set, the pure prune plan stamps GC roots
	// for them before the safe GC step, so "running implies rooted" holds
	// by construction instead of being assumed (CELL-334 preflight gate).
	LiveClosures []LiveClosure
}

PruneOpts describes a `cell build prune` invocation.

type PruneStep

type PruneStep struct {
	// Argv is the command and arguments.
	Argv []string

	// Func, when non-nil, is called instead of exec'ing Argv. Use this
	// to replace shell-substituted commands with pure Go. When set, Argv
	// is still joined for display (progress/dry-run output).
	Func func(ctx context.Context) error

	// IgnoreError lets the step fail without aborting the plan
	// (e.g. `docker rm` with no containers exits non-zero).
	IgnoreError bool

	// DryRun, when true, signals the runtime to print the argv as a
	// `# (dry-run)` comment instead of executing. Used for the macOS
	// linux-builder qcow nuke until paths are verified on a live setup.
	DryRun bool
}

PruneStep is one command in a prune plan.

func BuildCleanupSteps

func BuildCleanupSteps(closures []LiveClosure) []PruneStep

BuildCleanupSteps wraps the reaper script in a docker-run step mounting the nix volume — the only namespace where the devcell roots and their volume-resident targets both resolve (CELL-333 lesson).

func BuildDockerPruneSteps

func BuildDockerPruneSteps(opts PruneOpts) []PruneStep

BuildDockerPruneSteps composes the docker prune plan. Default mode is the cleandocker sequence (identical on Darwin and Linux). Force mode branches by OS.

func BuildNixPruneSteps

func BuildNixPruneSteps(opts PruneOpts) []PruneStep

BuildNixPruneSteps composes the nix prune plan. Default mode runs `nix-collect-garbage -d` + `nix-store --optimise` (via ssh on macOS, locally on Linux native). Force mode branches by OS (Darwin: dry-run qcow nuke plan; Linux: aggressive local GC).

type RankedEntry

type RankedEntry struct {
	Kind         EntryKind
	ID           string
	Label        string // e.g. "devcell-user:ultimate-pure" or "nix profile install (truncated)"
	SizeBytes    int64
	ReclaimBytes int64
	PinCount     int // >0 means "in use" — formatter hides reclaim hint
}

RankedEntry is the unified row type used by ranking + formatting. ReclaimBytes is the sort key — for images that's UniqueBytes; for everything else it's the full SizeBytes.

func RankEntries

func RankEntries(snap DFSnapshot, topN int) []RankedEntry

RankEntries flattens the snapshot into a single list sorted by ReclaimBytes desc. topN <= 0 returns all entries.

type RunCleanupArgs

type RunCleanupArgs struct {
	Closures []LiveClosure
	Exec     func(step PruneStep) error
	Out      io.Writer
	In       io.Reader
	SkipYes  bool
	IsTTY    bool
}

RunCleanupArgs bundles inputs to RunCleanup, mirroring RunPruneArgs.

type RunDFArgs

type RunDFArgs struct {
	Ctx       context.Context
	Collector DFCollector
	Opts      DFOpts
	Out       io.Writer
}

RunDFArgs bundles inputs to RunDF — keeps the call site readable (matches the pattern of RunPruneArgs).

type RunPruneArgs

type RunPruneArgs struct {
	Opts    PruneOpts
	Exec    func(step PruneStep) error
	Out     io.Writer
	In      io.Reader
	SkipYes bool // --yes / -y
	IsTTY   bool // term.IsTerminal(int(os.Stdin.Fd()))
}

RunPruneArgs bundles inputs to RunPrune. Keeps the call site readable when callers wire in real stdin/stdout/exec.

type RunSpec

type RunSpec struct {
	Config          config.Config
	CellCfg         cfg.CellConfig
	Binary          string
	DefaultFlags    []string
	UserArgs        []string
	Debug           bool                // pass DEVCELL_DEBUG=true into the container
	NixDaemon       bool                // pass DEVCELL_NIX_DAEMON=true into the container
	NoPorts         bool                // skip all -p port mappings (user ports and GUI)
	TrustFlake      bool                // pass DEVCELL_FLAKE_TRUST=1 into the container
	Image           string              // image ID or tag to run; defaults to UserImageTag
	ExtraEnv        map[string]string   // additional env vars injected by the command handler
	InheritEnv      []string            // env var names to inherit from host (passed as -e KEY with no value)
	Getenv          func(string) string // env lookup; defaults to os.Getenv when nil
	GitConfig       func(string) string // host `git config` lookup (cell.HostGitConfig); nil skips that tier
	ThinImage       bool                // when true, mount devcell-nix-store volume for /nix
	BootDir         string              // host-side boot dir for the Go-generated entrypoint script; empty disables the bind-mount
	TTY             bool                // allocate a pseudo-TTY (-it); set from isatty check on stdin
	Detach          bool                // run container in detached mode (-d); set by `cell start`
	Lifecycle       string              // "persistent" (cell start) or "oneshot" (cell claude); empty defaults to "oneshot"
	NoSecrets       bool                // skip `op run --` prefix and all secrets injection
	ProxyEnabled    bool                // when true, use internal network and inject proxy env vars
	KVMAutoDetected bool                // daemon host has /dev/kvm; set by the boot-checklist probe
	Policy          cfg.Policy          // isolation policy; nil defaults to OpenPolicy
}

RunSpec holds everything needed to build the docker run argv.

type VMDiskInfo

type VMDiskInfo struct {
	TotalBytes int64
	UsedBytes  int64
	AvailBytes int64
}

VMDiskInfo holds Docker VM filesystem info from `docker run alpine df`.

func CollectVMDisk

func CollectVMDisk(ctx context.Context) (VMDiskInfo, error)

CollectVMDisk probes the Docker VM filesystem size.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL