CHANGELOG
All notable changes to this project will be documented here.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.7.0 - 2026-03-10
Section titled “0.7.0 - 2026-03-10”crib prunecommand removes stale and orphan workspace images. Shows a dry-run preview with sizes before prompting for confirmation.--allincludes orphan images from workspaces that no longer exist.--force/-fskips the prompt.crib removenow shows a preview of what will be deleted (container ID, images, state directory) and prompts for confirmation. Use--force/-fto skip.- All crib-managed images are now labeled with
crib.workspace={wsID}, enabling label-based discovery without name-pattern heuristics. Applied via--labelondocker buildand--change "LABEL ..."ondocker commit(snapshots). - Build images are automatically removed when a new build replaces them (hash change). The old image is deleted after the new one is successfully built.
crib removenow deletes all labeled images for the workspace in addition to the container and workspace state.
Changed
Section titled “Changed”- Breaking: Workspace IDs now include a 7-character hash of the absolute
project path:
{slug}-{hash}(e.g.my-app-a1b2c3d). Workspaces created before this change will not be recognized. Runcrib remove(if still accessible) or delete~/.crib/workspaces/manually, then runcrib upin each project to create a new workspace with the updated ID.
0.6.3 - 2026-03-09
Section titled “0.6.3 - 2026-03-09”Security
Section titled “Security”- OCI feature archives containing symbolic links are now rejected outright. Previously, crib rejected symlinks whose static target appeared to escape the extraction directory, but a chain of individually-safe symlinks could still compose into a directory escape: an earlier symlink pointing to the extraction root could be overwritten via the chain, redirecting subsequent file writes outside the extraction directory. Feature archives contain scripts and JSON and have no legitimate need for symlinks.
0.6.2 - 2026-03-09
Section titled “0.6.2 - 2026-03-09”crib stopfollowed bycrib upnow resumes from a snapshot when available, skipping the full image build and running only resume-flow lifecycle hooks (postStartCommand, postAttachCommand). Previously, stopping and starting a workspace re-ran the entire creation flow. Both single-container and compose paths are covered.crib rebuildstill forces a full rebuild as before.- SSH agent socket is now mounted at
/run/ssh-agent.sockinstead of/tmp/ssh-agent.sock. Docker-in-Docker features remount/tmpas a fresh tmpfs, which hid the bind-mounted socket and broke SSH agent forwarding in DinD containers.
0.6.1 - 2026-03-08
Section titled “0.6.1 - 2026-03-08”- Compose container lookup fallback now filters by service name. Previously,
when the primary service failed to start, crib could pick up the wrong
container (e.g. postgres instead of rails-app) and show misleading logs in
the error message. The fallback now uses
compose ps --format jsonwith service label matching, which also works on podman-compose (unlikecompose ps -q <service>which podman-compose doesn’t support).
0.6.0 - 2026-03-08
Section titled “0.6.0 - 2026-03-08”crib cache clean --force/-fflag to skip confirmation prompt.- DevContainer Feature entrypoints are now applied. Features that declare an
entrypoint(e.g. docker-in-docker startingdockerd) now have their entrypoint baked into the image. Multiple feature entrypoints are chained via a wrapper script. Feature runtime capabilities (privileged,init,capAdd,securityOpt,mounts,containerEnv) are now applied at container creation time for both single-container and compose paths.
Security
Section titled “Security”- Reject OCI feature archives containing symlinks that escape the extraction directory (absolute targets or relative traversal). Previously, a malicious feature archive could write to arbitrary host paths via symlinks.
- CI workflows now use explicit
permissions: contents: readinstead of GitHub’s default read-write.
Changed
Section titled “Changed”- Breaking: The
-Vshorthand for--verbosehas been removed. Use--verboseinstead. The-vshorthand remains reserved for--version, matching CLI conventions. - CLI now exits with code 2 for usage errors (bad flags, missing arguments) instead of code 1, making it easier to distinguish user mistakes from runtime failures in scripts.
- Noisy host-specific environment variables (
LS_COLORS,DISPLAY,WAYLAND_DISPLAY,XDG_SESSION_*,DBUS_SESSION_BUS_ADDRESS,TERM_PROGRAM,COLORTERM,DESKTOP_SESSION, etc.) are now filtered from the probed environment. These are meaningless inside containers and cluttered the output ofcrib run -- env. Users can still force any filtered variable viaremoteEnvindevcontainer.json.
- SSH
known_hostscould not be written inside the container. The SSH plugin created~/.ssh/as root but only chowned individual files, leaving the directory root-owned. The container user could not create new files in it. - Plugin environment variables (e.g.
BUNDLE_PATH,CARGO_HOME,HISTFILE) now survivecrib restart. Previously, simple restart paths only preserved pluginPathPrependentries but silently dropped pluginEnvvalues. The values survived only because they were present in the stored result from a previouscrib up, but a plugin that changed an env value between restarts would have the old stored value win over the fresh one. crib deletenow removes named volumes declared in compose files (e.g. database data). Previously,docker compose downran without--volumes, leaving orphaned volumes behind.crib restartno longer loses software installed by lifecycle hooks (e.g. mise-managed ruby/node) on compose workspaces. The compose override now references the snapshot image, so even if the container is recreated, the hook-installed state is preserved.- Compose restart paths (
crib restart,crib upon stopped containers) now preserve the storedImageNamein workspace state. Previously, these paths saved an emptyImageName, causing subsequent operations to lose track of the feature image. - Preserve Docker image PATH entries (e.g.
/usr/local/bundle/binin ruby images) that login shells drop during the env probe. Previously,crib execcould lose these entries, requiringbundle execor similar wrappers. - Plugin PATH additions (e.g.
~/.bundle/binfor bundler cache) now work with zsh. Previously relied on/etc/profile.d/scripts which zsh doesn’t source. PATH additions are now injected directly via remoteEnv. crib cache cleanandcrib cache listno longer require workspace state to exist. They now work even ifcrib upwas never run or the project was deleted.crib cache clean --allnow prompts for confirmation since it removes volumes from other projects.- Sensitive env var values (tokens, keys, passwords) are now redacted in
--debugoutput. Previously, values likeGITHUB_TOKENappeared in plaintext in exec command logs. crib restartand redundantcrib upno longer lose probed environment (mise, rbenv, nvm PATH entries) or plugin PATH additions. Previously, restart paths that skip env re-probing overwrote the savedremoteEnv, socrib runcould not find tools likerubyornodeafter a restart.- Plugin dispatch in the already-running container path now passes arguments in the correct order. Previously, the remote user was passed as the image name, causing plugins to see an incorrect image and potentially resolve wrong home directory paths.
0.5.0 - 2026-03-05
Section titled “0.5.0 - 2026-03-05”crib runcommand: runs commands through a login shell so tools installed by version managers (mise, asdf, nvm, rbenv) are available on PATH. Use instead ofcrib execwhen the command depends on shell init files.crib cache listandcrib cache cleancommands for inspecting and removing package cache volumes.--allflag operates across all workspaces.- Package cache volumes are now per-workspace (
crib-cache-{workspace}-{provider}) instead of shared globally. This prevents cross-contamination between projects. crib logscommand with--follow/-fand--tailflags. Shows container logs for single-container workspaces; shows all service logs for compose workspaces.crib doctorcommand to detect and fix workspace health issues. Checks runtime availability, compose availability, orphaned workspaces (source directory deleted), dangling containers (crib label but no workspace state), and stale plugin data. Use--fixto auto-clean.- Package cache sharing plugin: shares host package caches (npm, pip, go,
cargo, maven, gradle, bundler, apt, downloads) via named Docker volumes.
Configure in
.cribrc:cache = npm, pip, go. Thedownloadsprovider is a general-purpose cache directory at~/.cache/crib(exposed viaCRIB_CACHEenv var) for ad hoc file caching. Thebundlerprovider setsBUNDLE_BINand adds~/.bundle/binto PATH via/etc/profile.d/, so gem executables likerspecwork directly incrib shellandcrib run. - Build-time cache mounts: when package cache providers are configured, crib
attaches BuildKit
--mount=type=cachedirectives to DevContainer Feature install steps. This speeds up feature installation across rebuilds by reusing cached packages (especially apt). - Auto-snapshot: after
crib upcompletes create-time hooks, the container is committed to a local snapshot image. On subsequentcrib restartrecreations, the snapshot is used so hook effects are already baked in. If hook definitions change, the snapshot is considered stale and full setup runs instead.crib rebuildalways starts fresh. - Each plugin now emits a progress line (“Running plugin: <name>”) during
crib upandcrib rebuild, visible without any flags.
bundlercache provider: mount volume at~/.bundleinstead of~/.bundle/cacheto avoid permission errors creating~/.bundle/bin(Docker created the parent directory as root).crib cache list: compose workspaces showed the full volume name (including compose project prefix) in the PROVIDER column. Also fixed compose override to declare volumes with explicitname:so new volumes use the expected name.- Installation
curlcommand now works in zsh (URL was missing quotes, causing a parse error with the embeddedsedexpression). Archive filenames no longer include the version, soreleases/latest/download/crib_linux_amd64.tar.gzis a stable URL that always points to the latest release. .envfiles with quoted values (KEY="value with spaces",KEY='value') and inline comments (KEY=value # comment) now parse correctly. Previously only bareKEY=valuesyntax was supported.workspaceMountstrings with a missingtargetfield now produce an explicit error instead of silently creating an unusable mount.- Compose workspaces: stderr noise from
compose upandcompose down(e.g. “No container found”, SIGTERM warnings) is now suppressed in normal mode. Use-V/--verboseto see full compose output. - Compose workspaces:
crib upandcrib rebuildnow detect when the primary container exits immediately aftercompose up(e.g. port conflicts) and report a clear error with container logs, instead of cascading into confusing plugin copy and lifecycle hook failures. - Plugin file copies now bail out on the first exec failure instead of logging identical errors for every remaining copy.
- Compose workspaces:
crib restartnow includes plugin-injected env vars and mounts (package cache, SSH agent, shell history, etc.) in the compose override. Previously the simple restart path skipped plugin dispatch, so these were missing until a fullcrib down && crib up.
0.4.1 - 2026-03-02
Section titled “0.4.1 - 2026-03-02”- Plugins (coding-agents, ssh, shell-history) now run for Docker Compose
workspaces with the correct container user. Previously, plugins only ran for
single-container devcontainers, and the initial fix defaulted to root when
remoteUser/containerUserweren’t set indevcontainer.json, causing permission errors (e.g.zsh: locking failed for /root/.crib_history/.shell_history). The engine now resolves the user from the compose service and image before dispatching plugins. - Compose override files are now written to a system temp directory instead of
inside
.devcontainer/. Previously,chown -Rduring container setup could change ownership of the override file, making it unremovable by the host user.
0.4.0 - 2026-03-01
Section titled “0.4.0 - 2026-03-01”- Plugin system with bundled plugin support for
pre-container-run. Plugins can inject mounts, environment variables, extra run args, and file copies into containers. Fail-open error handling (one broken plugin doesn’t block container creation). Seedocs/plugin-development.md. - coding-agents plugin: automatically injects Claude Code credentials into
containers so AI coding tools work without re-authentication. Detects
~/.claude/.credentials.jsonon the host and copies it into the container. Supports a workspace credentials mode for teams that need org-specific accounts: setcustomizations.crib.coding-agents.credentialsto"workspace"indevcontainer.jsonto persist container-created credentials across rebuilds instead of injecting host credentials. - ssh plugin: shares SSH configuration with containers. Forwards the SSH
agent socket, copies
~/.ssh/configand public keys, and extracts git SSH signing config (gpg.format=ssh) into a minimal.gitconfig. Private keys stay on the host; commit signing works via the forwarded agent (OpenSSH 8.2+). - shell-history plugin: persists bash/zsh history across container
recreations. Bind-mounts a history directory from workspace state and sets
HISTFILE. - Automatic port publishing for single-container workspaces.
forwardPortsandappPortfromdevcontainer.jsonare now translated into--publishflags ondocker run, so ports work without manualrunArgsworkarounds. - Published ports shown on
crib ps,crib up,crib restart, andcrib rebuild. For compose workspaces, ports are parsed fromdocker compose psoutput. - Plugin customizations: plugins now receive
customizations.cribfromdevcontainer.json, enabling per-project plugin configuration. - Documentation website at fgrehm.github.io/crib,
with
llms.txtfor AI tool discovery. -V/--verbosenow prints each lifecycle hook command before running it (e.g.$ npm install), making it easier to diagnose hook failures.build.optionsfromdevcontainer.jsonis now passed todocker build/podman buildas extra CLI flags (e.g.--network=host,--progress=plain).- Object-syntax lifecycle hooks now run their named entries in parallel, matching the devcontainer spec. String and array hooks are unchanged (sequential).
waitForis now respected: a “Container ready.” progress message is emitted after the specified lifecycle stage completes (default:updateContentCommand).
Changed
Section titled “Changed”--debugnow implies--verbose: subprocess stdout (hooks, build, compose) is shown when debug logging is active.crib shellnow rejects arguments with a helpful error suggestingcrib exec.
crib restartno longer fails with “cannot determine image name” on Dockerfile-based workspaces where the stored result had an empty image name. Falls back to rebuilding the image instead of erroring.crib rebuildno longer fails when no container exists (e.g. first build or after manual container removal).- Container deletion is faster (skips stop grace period).
make installnow uses correct permissions.
0.3.1 - 2026-02-28
Section titled “0.3.1 - 2026-02-28”down/stopon rootless Podman no longer fails with “no pod with name or ID … found”. Thex-podman: { in_pod: false }override was only passed duringup, socompose downtried to remove a pod that never existed.rebuildnow actually rebuilds images. Previously it passedRecreate: falsetoUp, which took the stored-result shortcut and skipped the image build.- Environment probe now runs after lifecycle hooks (in addition to before), so
the persisted environment for
shell/execincludes tools installed by hooks (e.g.mise installinbin/setup). - Filter mise internal state variables (
__MISE_*,MISE_SHELL) from the probed environment. These are session-specific and confused mise when injected into a new shell viacrib shell.
0.3.0 - 2026-02-27
Section titled “0.3.0 - 2026-02-27”Changed
Section titled “Changed”- Rename
stoptodown(alias:stop). Now stops and removes the container instead of just stopping it, clearing lifecycle hook markers so the nextupruns all hooks from scratch. - Rename
deletetoremove(aliases:rm,delete). Removes container and workspace state. - Add short aliases:
list(ls),status(ps),shell(sh). rebuildno longer needs to re-save workspace state after removing the container (usesdowninstead of the olddelete).- Display crib version at the start of
up,down,remove,rebuild, andrestartcommands. Dev builds include commit SHA and build timestamp. - Suppress noisy compose stdout (container name listings) during up/down/restart.
Use
--verbose/-Vto see full compose output. status/psnow shows all compose service statuses for compose workspaces.
- Lifecycle hooks (
onCreateCommand,updateContentCommand,postCreateCommand) now run afterdown+upcycle. Previously, host-side hook markers persisted across container removal, causing hooks to be skipped. restartfor compose workspaces now usescompose upinstead ofcompose restart, fixing failures when dependency services (databases, sidecars) were stopped.upafterdownfor compose workspaces no longer rebuilds images. When a stored result exists with a previously built image, the build is skipped and services are started directly.
0.2.0 - 2026-02-26
Section titled “0.2.0 - 2026-02-26”crib restartcommand with smart change detection- No config changes: simple container restart, runs only resume-flow hooks
(
postStartCommand,postAttachCommand) - Safe changes (volumes, mounts, ports, env, runArgs, user): recreates container without rebuilding the image, runs only resume-flow hooks
- Image-affecting changes (image, Dockerfile, features, build args): reports error
and suggests
crib rebuild
- No config changes: simple container restart, runs only resume-flow hooks
(
RestartContainermethod in container driver interfaceRestartmethod in compose helper- Smart Restart section in README
- New project logo
Changed
Section titled “Changed”- Refactor engine package: extract
change.go,restart.gofromengine.go - Deduplicate config parsing (
parseAndSubstitute) and user resolution (resolveRemoteUser)
0.1.0 - 2026-02-25
Section titled “0.1.0 - 2026-02-25”- Core
cribCLI for managing dev containers - Support for Docker and Podman via single OCI driver
.devcontainerconfiguration parsing, variable substitution, and merging- All three configuration scenarios: image-based, Dockerfile-based, Docker Compose-based
- DevContainer Features support with OCI image resolution and ordering
- Workspace state management in
~/.crib/workspaces/ - Implicit workspace resolution from current working directory
- Commands:
up,stop,delete,status,list,exec,shell,rebuild,version - All lifecycle hooks:
initializeCommand,onCreateCommand,updateContentCommand,postCreateCommand,postStartCommand,postAttachCommand userEnvProbesupport for probing user environment (mise, rbenv, nvm, etc.)- Image metadata parsing (
devcontainer.metadatalabel) with spec-compliant merge rules updateRemoteUserUIDwith UID/GID sync and conflict resolution- Auto-injection of
--userns=keep-id/userns_mode: "keep-id"for rootless Podman - Container user auto-detection via
whoamifor compose containers - Early result persistence so
exec/shellwork while lifecycle hooks are still running - Version info on error output for debugging
- Container naming with
crib-{workspace-id}convention - Container labeling with
crib.workspace={id}for discovery - Build and test tooling (Makefile, golangci-lint v2, pre-commit hooks)
- Debug logging via
--debugflag