RELEASES

Changelog

What changed in each release, newest first.


0.8.0

Machines: sandboxes on your other hosts, over ssh, in the same CLI and dashboard, with your ssh-agent relayed only where you allow it. No more half-built sandboxes: an instance is recorded from the moment run starts, a failure leaves it listed as failed with its error, and Ctrl-C undoes the whole run. Every run, rebuild and start keeps a build log, and the dashboard runs them in the background instead of handing them the screen. run, rebuild, start, stop, rm and service rebuild now run in the host daemon, so a build outlives the terminal that started it, and -d leaves it running. The dashboard gets everything it shows from the daemon too.

Behaviour change: a failed run now leaves an instance to rm. Before, only a failing lifecycle command (postCreateCommand, …) kept its container, and a run that failed earlier (worktree, image build, container create) left its worktree, branch and services behind with no instance pointing at them. Now every failure keeps a failed instance; devsandbox rm <name> removes all of it, or devsandbox rebuild <name> retries.

TERMINAL
devsandbox rebuild web-4   # retry a failed run
devsandbox rm web-4        # or remove everything it made

Added

  • machine add installs devsandbox on the machine. When the machine has no devsandbox or another version, machine add offers to copy this one there (to ~/.local/bin/devsandbox, or over the old one); --auto-install skips the question and is needed off a TTY. A devsandbox in ~/.local/bin is found even when the machine’s login shell doesn’t put it on PATH. devsandbox --version prints the version.
TERMINAL
devsandbox machine add me@my-server --auto-install
  • Machines: sandboxes on other hosts, over ssh. devsandbox machine add <ssh target> registers a host running devsandbox (its config roots are offered to pick from, or pass --config '<path there>'); machine ls shows each one’s state, machine rm forgets it. Their sandboxes and instances then show in the dashboard next to yours, in a MACHINE column, and every key works on them, terminals included. On the CLI a name is looked up on every machine: one match is used wherever it is, my-server/test or @/test (this host) picks when it’s ambiguous. Verbs run on the machine with its login shell’s environment. Your ssh-agent reaches a machine only if you ask: machine add --agent, machine set <name> --agent on|off, or a on the dashboard’s Machines tab (5, listed while a machine is registered: each machine’s target, link state, agent and version) (off by default, takes effect at once), relayed by devsandbox rather than ssh -A. A login shell that prints a banner no longer makes the machine unreachable. exec, logs, inspect, done and rename run there over ssh. --machine <name> runs a command without a name there (ps, ls, stop --all, …). A machine that’s down keeps its last rows, marked unreachable. Not yet on machines: VS Code, the Inbox. Adding a machine means trusting it. Details: docs/machines.md.
TERMINAL
devsandbox machine add me@my-server
devsandbox run api              # `api` is only defined on my-server
devsandbox exec test            # an instance there: over ssh
devsandbox stop my-server/test  # qualified, when `test` exists here too
  • Port forwards into a machine’s sandboxes. devsandbox port my-server/web 3000 opens a port of an instance on a machine on this host, and port --machine my-server --service redis 6379 one of its global services. In the dashboard, p on a machine’s row does the same; the Ports tab lists those forwards with a MACHINE column, and like local ones they’re kept by the host daemon and come back after it restarts. Each forward is tunnelled over your own ssh to the machine, reconnecting if it drops. A machine’s forwardPorts stay on that machine: forwards into it are only the ones you ask for here. The machine needs this devsandbox version. Details: docs/architecture/daemon/forwards.md, Remote bridges.
TERMINAL
devsandbox port my-server/web 3000
devsandbox port --machine my-server --service redis 6379
  • Forwards show the listening process without lsof. The process listening on a forwarded port (node (pid 412)) now comes from the in-container helper, so it shows in images that have no lsof, and on a machine’s forwards too. Running instances pick up the new helper when restarted.

  • API: forwards into machines. forwards.add takes machine (the forward still binds on this host; dir is the config root there), forwards.list takes machines: true to include them, and forward rows carry machine. Details: docs/reference/api.md, Forwards.

  • API: machines. machines.probe|add|list|get|rm, roots.list, a machine param that runs a quick call or a verb on a machine, machines: true on instances.watch (rows tagged machine, plus each machine’s link state), instances.resolve and sandboxes.list (every machine’s matches), and found on instances.resolve’s answer. Details: docs/api.md, Machines.

  • -d / --detach on run, rebuild and start. Returns as soon as the instance is registered and prints its name (run -d --json: the run record); the build goes on in the host daemon. Follow it with devsandbox logs --build <name> --follow, or watch its row in the dashboard.

TERMINAL
devsandbox run -d web --branch fix/login
  • API: the verbs as methods. run, rebuild, start, stop, rm and services.rebuild stream their output and phase over the API, plus instances.resolve, instances.cancel and sandboxes.list. Details: docs/api.md, Verbs.

  • API: everything the dashboard shows. instances.list takes a depth (disk, listing or full, to skip the runtime or just its stats) and also answers thread_children. New: instances.procs and instances.signal (an instance’s processes and tracked runs; SIGTERM/SIGKILL one), instances.buildLog, instances.rename, instances.done, containers.logs and containers.inspect (devsandbox containers only), sandboxes.get and services.get (a config as written and resolved). instances.changed now also fires when an instance is created, removed, renamed, marked done or changes step. Details: docs/api.md.

  • API: instances.watch. A client watching a config root gets its snapshot pushed as instances.snapshot every 2 s (cpu/mem every 7 s), from one poll shared by every watcher; the dashboard uses it, so two dashboards cost the runtime no more than one. stop, rm and services.rebuild take detach (a hang-up leaves them running), run, rebuild and start take tty (run on a pty of that size), and instances.buildLog takes ansi (keep the colours). Details: docs/api.md.

  • status --json and ls --json sandbox rows gain dispatcher and worktree_branch: whether the sandbox is a dispatcher, and its resolved worktree-branch. Details: docs/json-output.md.

  • Build logs. run, rebuild and start write their whole output, the containers’ build and lifecycle commands included, to a log per instance (~/.local/share/devsandbox/logs/<instance id>/build.log; the one before is kept as build.1.log, and rm deletes both). devsandbox logs --build <name> prints it; --follow keeps printing until the instance is done building. The command’s error, if it failed, is the last line. Builds run on a terminal of their own when you’re on one (or start them from the dashboard), so docker’s progress display and colours show as they used to, live and in the dashboard’s build log (b), which also scrolls with the mouse wheel. Piped, logs --build prints plain text.

TERMINAL
devsandbox logs --build web-4 --follow
  • ps, status --json and the dashboard show where an instance is. While it’s being made, an instance’s status is its step (preparing, building, creating, then the lifecycle command running, like postCreateCommand), and failed when it stopped partway; the dashboard’s Detail panel shows the failed step and error. status --json rows carry it as phase ({"state": "in-flight", "step": "building"} or {"state": "failed", "step": …, "error": …}), absent once the instance is ready; ps --json rows of instances with a container too. Details: docs/json-output.md.

Changed

  • run, rebuild, start, stop, rm and service rebuild run in the host daemon. The command starts devsandbox serve if needed, hands it the work and prints its output as it comes, exiting as before. Closing the terminal, or losing the ssh session, no longer stops a build; Ctrl-C still cancels it (the rollback’s output shows, exit 130), and a second one stops it at once. Prompts (the sandbox picker, an ambiguous name, rm’s branch question) still happen in your terminal. If no daemon answers, one warning: line and the command runs in your terminal as before (on Windows it always does, with no daemon).

  • The dashboard is a client of the host daemon. Everything it shows comes from devsandbox serve and every action goes to it, so it needs the daemon (it starts one when it opens, as before). If the daemon goes away, the last tree stays on screen marked stale with the reason, and actions say the daemon is needed. On Windows, which has no daemon, the dashboard isn’t available for now. It no longer runs an autostart pass of its own when it opens; the daemon’s covers every config root that has instances.

  • The dashboard no longer takes over the screen for stop, rm or a service’s rebuild. They run in the daemon, and the status line says how they ended, or why not (e.g. rm refusing a dirty worktree). rm’s question about deleting the branch is a y/n popup; esc removes nothing. Quitting the dashboard while one of them is still going leaves it running.

  • s on a stopped instance starts it like devsandbox start. It used to be a bare container start; it now also runs the services, postStartCommand and the autostart pass, in the background, with a build log under b.

  • Each command runs with your shell’s environment. The daemon runs every request with the caller’s whole environment, never its own: ${localEnv:…} in the config, SSH_AUTH_SOCK, GIT_*, DEVSANDBOX_RUNTIME and PATH are those of the shell you typed the command in, even when the daemon was started long ago or by systemd.

  • The dashboard runs run, rebuild and start in the background, through the host daemon. It no longer steps aside for the build and waits for a key: the command keeps going even if you quit the dashboard, a status line says when it started, and the instance’s row shows its step as it goes, failed included. When it can’t reach the daemon, the status line says one is needed instead. b on an instance opens its build log, following it while it builds. d on a row still building asks to cancel it; on a failed row it removes the instance, and s rebuilds it.

  • The host daemon follows container events. Instead of listing containers every few seconds, it wakes on the runtime’s events and on edits to your config, so the dashboard reflects a change sooner and an idle daemon (kept up by serve install) costs next to nothing.

  • Ctrl-C rolls a run back. Cancelling a run (Ctrl-C or SIGTERM) removes everything it had made: container, services and network, volumes, worktrees, the branch it created if it has no new commits, and the instance itself. A second Ctrl-C quits at once without cleaning up. A cancelled rebuild leaves the instance failed.

  • Commands leave an instance alone while it’s being made. start, stop, rename, rebuild and rm (even with --force) refuse an instance whose run or rebuild is still going and say what it’s doing; start also refuses a failed one and points at rebuild and rm. rebuild --all and autostart skip both. exec works once the container exists, so you can shell into one whose postCreateCommand failed. A dispatcher’s devsbd ensure of such a child fails with a message naming its step instead of starting or recreating it.

Fixed

  • Copying in the dashboard works in terminals that ignore OSC 52, such as iTerm2 by default. That covers the dashboard’s own selections and copies from apps in its terminal tabs, including tabs on a machine. A dashboard running on your machine now also puts the text on the clipboard itself (pbcopy, wl-copy or xclip).

  • Upgrading devsandbox under a running dashboard or daemon no longer breaks it. Once the binary was replaced, new terminal tabs, builds and other spawned commands failed until a restart; they now run the new one.

  • A run killed outright shows as failed. A run or rebuild killed by SIGKILL, the OOM killer or a reboot used to leave its instance looking half-made or not at all; it now shows as failed with interrupted, ready to rebuild or rm.

  • Two runs started at once no longer share an instance name. Both could pick the same default name and build over each other; now the second stops with an error before making anything.

Commits
  • be6fc4a docs(skills): tell agents to add sandboxes to the workspace’s config
  • c286190 docs(site): frame a sandbox as the stack and one config per workspace
  • b14a8d5 docs(site): add a compared-to-docker page
  • 686da2d docs(site): cover machine add installing devsandbox and the host clipboard copy
  • e0a718b docs(site): make the landing page say what devsandbox is for
  • 7917bf1 fix(devsbd): mark a failed bridge done before reporting its outcome, so a caller seeing the failure also sees it dead
  • 6a8efb3 fix(tui): also copy through the host clipboard command, since iTerm2 ignores osc 52 by default and dashboard copies (own and relayed from terminal tabs) were lost
  • 9c286da fix(machine): find devsandbox in ~/.local/bin off the login path and only warn when it can’t be copied, so add works on hosts whose login shell misses ~/.local/bin
  • 93d8d20 docs(machine): close the machine-install plan
  • 000c807 feat(machine): offer to install devsandbox on machine add
  • ad6ff05 feat(machine): let machines.probe and machines.add take a command
  • 8865a83 feat(machine): check a machine’s devsandbox and copy this binary there
  • 649b827 feat(serve): wake the host poll on config edits
  • 04fa19d docs(serve): close the runtime-events plan
  • ca725cb feat(serve): let container events drive the host poll
  • ecf0181 feat(serve): follow the runtime’s container event stream
  • 048f612 fix: ra hang bug
  • 38eb0a5 docs(tui): bring the changelog, selection regions and stale comments in line with the machines tab
  • 06af1a0 docs(tui): close the machines tab plan now that the tab and its agent toggle shipped
  • 70f0246 feat(tui): let a on the machines tab flip a machine’s agent, so relaying keys no longer needs a trip to the cli
  • decbcf8 feat(tui): add a machines tab listing every registered machine’s link and agent, so the dashboard can show what only machine ls did
  • f07511a docs(machines): close the agent plan, record the never-ssh-a decision and the relay’s known costs
  • 042a430 feat(machines): keep a machine’s agent relay up for cli re-runs and drop ssh -A everywhere, so keys reach a machine only when you said so
  • e23d074 refactor(config): group implemented devcontainer properties together
  • b742fa0 feat(serve): relay the agent to a machine only while it’s wanted and its agent is on, and drop ssh -A from the daemon’s links
  • 79f6766 feat(machine): add a per-machine agent setting, off by default, so forwarding your keys becomes a choice per host
  • b5075e4 feat(machines): add an agent relay over the helper’s frame protocol, so a machine can get this host’s agent without ssh -A
  • 7c75b99 fix(machines): skip what a remote login shell prints before the relay’s sentinel, so a chatty profile no longer makes the machine unreachable
  • 3f9effb docs(plans): plan opt-in agent forwarding over our own relay and a sentinel for noisy login shells
  • b27bb92 docs(agents): treat everything as unreleased before 1.0 so plans stop carrying compat work
  • d2197ec docs(plans): drop compat and changelog notes from the machines plan, machines are unreleased
  • de38c42 docs(plans): plan surviving a noisy remote login shell and opt-in, live-switchable agent forwarding for machines
  • a6a0d0e docs(status): plan dropping the host lsof fallback once the helper covers every route
  • 18e1892 docs(forwards): record remote forwards as shipped, with their limits, and archive the plan
  • f38975b feat(tui): forward a machine’s instances and services from the dashboard, with their forwards in the ports tab
  • a2ccf7e feat(serve): hold forwards into a machine’s instances in the daemon, persisted and dropped with the machine
  • f27796e feat(port): forward a machine’s instance or global service from the cli, tunnelled over ssh
  • d86800a feat(port): add the hidden tunnel command and a remote opener, so a forward’s bridge can run over ssh to a machine
  • 6b0b55d refactor(forward): open a forward’s bridge through a pluggable opener, so a remote one can come from ssh
  • d40e4b5 feat(devsbd): answer a forward’s listening process from the helper, so it works without lsof and over a remote bridge
  • aebbf31 docs(plans): plan remote forwards as an ssh-carried bridge, helper-side process lookup first
  • 8e0edaa chore(skills): make orchestrated work keep the docs tree current and close its plan
  • 97502e1 refactor(serve): drop dead idle and quiet knobs left from the in-process dashboard
  • b2a5e39 docs(comments): drop plan-step tags and describe the daemon-era code as it is
  • f2a4a64 docs: point every code and site citation at the new docs tree, and test that they keep resolving
  • ac334c9 docs: one status file, an overview and a map; archive shipped plans, drop docs the code outgrew
  • 6afcdc5 docs(plans): trim unfinished work into current plans and triage every old doc
  • 1f265ea docs(decisions): keep only the decisions that still hold, standalone
  • a1f4371 docs(tui): describe the dashboard as a daemon client as built
  • ed04e91 docs(inbox): describe the inbox and automations as built, with a verified inbox cli reference
  • fc67220 docs(devsbd): describe the helper, its protocol, bridges and ssh-agent relay as built
  • 423ce69 docs(serve): describe the daemon as built and verify the api reference against the method table
  • 7172a5b docs(instances): describe config, runtimes and instances as they work today, verified against the code
  • 25e878d docs: split docs into living reference, decisions and plans so status stops drifting
  • 98749d6 docs(machines): document machines across the docs, site and cli skill
  • 5647d09 feat(tui): show and act on machines’ sandboxes
  • dee2e5c feat(cli): resolve names across machines
  • 519bb23 feat(machines): forward calls and verbs to a machine
  • 2e7a08f feat(machines): held links and the federated snapshot
  • 78ba576 feat(machines): machines.toml registry and machine add|ls|rm
  • e897bf2 feat(machines): ssh transport, link hello, relay session env and roots.list
  • 348d39d docs(site): show stop, rm and build cancel as the dashboard does now
  • 4e25f3c docs(serve): describe the daemon-owned host side and the watched snapshot
  • e35209d fix(tui): size a build’s pty to the build log modal, not the terminal
  • 667378a docs(changelog): note the shared poll, pty builds and upgrade fix
  • 747f751 feat(tui): watch the root instead of polling instances.list
  • 3098829 feat(serve): poll once for every dashboard and push the snapshot
  • 686a362 feat(tui): show build logs in colour, replayed as the terminal drew them
  • 62cf9ec feat(serve): run build verbs on a pty when the client has a terminal
  • 8d341bd fix(tui): scroll the logs, build log and help modals with the wheel
  • 5367fcd fix(tui): let stop, rm and service rebuild outlive a dashboard quit
  • 27b7209 fix(serve): spawn the binary now at our path, not the replaced one
  • 4d9a749 docs(serve): plan the work before machines
  • b925e66 docs(tui): describe the dashboard as a client of devsandbox serve
  • 7b0544c feat(tui): open instance terminals and :exec through devsandbox exec
  • 665323e feat(tui): send stop, rm, s and service rebuild to the daemon
  • 7c17dbe feat(tui): ask the daemon for logs, inspect, config, build logs and edits
  • 7e7f59c feat(tui): read the instance tree and procs from the daemon
  • 3b36501 feat(serve): rename, mark done, signal procs and rebuild services over the api
  • b43bcc9 feat(serve): serve procs, logs, inspect, build logs and config views
  • 5b3e7b7 feat(serve): serve the dashboard’s snapshot over the api
  • 710b2df refactor(tui): build the dashboard on unix only
  • a3d7919 docs(tui): plan rollout step 4, the dashboard as a pure client
  • 4fa528b docs(serve): record the calls on step 3’s known limitations
  • df0c28e docs(serve): describe the verbs running in the daemon, -d and the caller’s env
  • 61a1151 feat(serve): adopt verbs a previous daemon left running
  • b4ea475 feat(tui): send run, rebuild and start to the daemon and cancel through it
  • 6b6fdd7 feat(cli): send run, rebuild, start, stop and rm to the daemon
  • 71c0f24 feat(serve): resolve names, cancel builds and ask rm’s question over the api
  • 0fe841e feat(serve): run run, rebuild, start, stop and rm as daemon requests
  • 4360b83 docs(serve): plan rollout step 3, the verbs as daemon requests
  • 9762f34 docs(changelog): describe failed instances, build logs and background dashboard builds
  • 26ae476 feat(tui): run, rebuild and start in the background with a build-log view
  • 19a9d5a feat(tui): show a building or failed instance’s phase as its status
  • b65237f fix(run): keep a run that fails before its container as failed
  • d06e9bd feat(logs): keep a per-instance build log and show it with logs –build
  • 2dd4d72 feat(run): roll back a cancelled run and guard verbs against unfinished instances
  • 1cfaaa2 fix(run): record the instance first and roll back a run that fails before its container
  • ef5f9e6 feat(state): record an instance’s in-flight progress
  • e9a4ab1 docs(serve): design moving the verbs into the daemon, remote machines and the instance-phase plan

0.7.1

Critical fix: upgrade if you run more than one instance of the same repo. A git worktree prune or git gc inside one sandbox could delete git’s records of every other instance’s worktree, leaving them with fatal: not a git repository. devsandbox now locks the worktrees it creates. After upgrading, run devsandbox start once on each existing instance to lock its worktree (safe on a running one), and devsandbox rebuild it to also get the git gc guard. This release also adds local-folder features and fixes a few host-daemon start and shutdown issues.

TERMINAL
devsandbox start <instance>

Added

  • Local-folder features. A features key starting with ./, ../ or / is a feature folder (devcontainer-feature.json + install.sh) resolved against the config dir, so a config can ship its own feature without publishing it to a registry. Options, containerEnv, installsAfter, mounts, privileged and entrypoint work as for OCI features. Editing a file in the folder marks the sandbox’s instances as drifted, so devsandbox rebuild picks the change up, and devsandbox ls warns when the folder or its devcontainer-feature.json is missing.
TOML
[template.base.features."./features/toolkit"]
ripgrep = true

Fixed

  • Critical: git worktree prune inside a sandbox no longer breaks the other instances’ worktrees. A container sees the repo’s worktree records but not the other checkouts, so a prune (or git gc) there deleted their records and left each one with fatal: not a git repository. devsandbox now locks the worktrees it creates, so prune and gc skip them; existing instances get locked on their next start. An instance whose record was already pruned gets a warning on start instead; git won’t work in it until the record is restored. Containers also get gc.worktreePruneExpire=never (after a rebuild for existing ones), so git gc leaves worktrees you made yourself alone too. Details: docs/worktree-locking.md.
  • devsandbox rebuild starts the host daemon, like run and start. A rebuilt dispatcher or inbox = true instance with no dashboard open and no daemon running had no host, so its devsbd control commands exited 75 until some other command started one.
  • An upgrade or devsandbox serve shutdown no longer brings back the old daemon. A dispatcher’s child op still running while the daemon shut down could start a fresh daemon from the old binary, which then kept running instead of the new one (or undid the shutdown).
  • The “no host connected” error says how to get one. Control commands that exit 75 now suggest running any devsandbox command (or devsandbox serve install) instead of opening the dashboard, which no longer serves them.
Commits
  • 6a9f65a chore(site): add pnpm shot to screenshot built pages
  • 029b3ac fix(site): tidy the architecture diagrams’ joins and index list
  • 23ba27b docs(changelog): note the serve drain race and no-host hint fixes
  • 5f1d597 docs(changelog): note that rebuild now starts the host daemon
  • 8c2b83e docs(devsbd): point the no-host hint at the daemon, not the dashboard
  • 2977de1 fix(serve): never lazy-start a daemon from a dispatcher child op
  • 138d46b fix(rebuild): start the host daemon after a rebuild
  • 7ae7921 fix(worktree): lock sandbox worktrees so a prune inside a container can’t break the others
  • 454e99e docs(site): add unlinked /architecture pages for contributors
  • 002dda3 docs(features): document local-folder features
  • fc97df0 feat(features): support local-folder features

0.7.0

A per-user host daemon now keeps notifications, dispatchers and port forwards working with the dashboard closed, and serves an API that editors, GUIs and the new npm connect() client can drive. Sandboxes can keep one Inbox thread per item: a header with a state and buttons, and a conversation of messages, forms and your replies, answered from the dashboard, the new devsandbox inbox command or any API client, with events streamed back as they happen. The Inbox becomes a list of what’s waiting on you rather than a log, and instances can be marked done instead of removed. VS Code can open straight at a file and line.

Added

  • A host daemon keeps devsandbox live with the dashboard closed. devsandbox serve is a per-user background process that now owns the connection to every running sandbox’s helper: the ssh-agent relay, notifications and desktop popups, dispatcher control commands and port forwards. You don’t start it: the dashboard, run, start and the commands that relay your ssh-agent start it when needed, and it exits 10 minutes after its last user left (a dashboard, a forward, a running dispatcher or inbox = true sandbox). A newer devsandbox takes over from an older daemon by itself. devsandbox serve install runs it from login as a systemd user unit (Linux) or a LaunchAgent (macOS), for sandboxes the container runtime restarts on boot; an upgrade repoints the installed unit at the new binary, and serve uninstall removes it. To keep it from idling without a unit, use devsandbox serve --keep-alive or ~/.local/share/devsandbox/daemon.config.toml. exec, start and run no longer start an ssh-agent bridge of their own: one bridge per container, relaying the agent of whichever session last ran a command. Details: docs/serve.md.
TOML
# ~/.local/share/devsandbox/daemon.config.toml
[serve]
keep-alive = true
TERMINAL
devsandbox serve install
  • Port forwards outlive the dashboard. Ports-tab forwards and every sandbox’s forwardPorts now run in the host daemon, so closing the dashboard no longer drops them, and every open dashboard shows the same ones. Forwards you add are saved in ~/.local/share/devsandbox/forwards.toml and come back on the same host port after a daemon restart or a reboot, until d stops them. devsandbox port still forwards in the foreground until you stop it.

  • Inbox threads. A sandbox that declares inbox = true can keep one thread per item in the Inbox (a PR, an email to answer). devsbd thread put sets its header: a title, a link, a state (needs-you, active or done), a status, the child instance it’s about, up to 9 buttons and an optional reply box (compose, with a hint line under it saying what sending does). A put that changes nothing is ignored (no unread mark, no popup), so a sandbox can simply re-send all its threads on every pass; a desktop popup only fires when a thread enters needs-you, and state and status changes show as one-line markers in the thread. devsbd thread send adds a message under the header: markdown and key/value fields blocks, identified by an id of your choosing, so re-sending the same message is free and sending changed content edits it in place; devsbd thread withdraw takes one back. Everything is queued like notify, so it works with no daemon running. devsbd thread rm drops a thread, devsbd thread ls [--feed] lists yours back, messages included. dispatcher alone only grants the child commands, so a dispatcher that wants threads declares both. A rejected put or send shows up as an error notification from your instance, with the reason. Full reference: docs/automations-guide.md.

TOML
[sandbox.pr-dispatcher]
dispatcher = { spawn = ["web"] }
inbox = true
TERMINAL
devsbd thread put --json '{"key":"pr-123","title":"#123 fix login","state":"needs-you","status":"review replies","child":"pr-123","actions":[{"id":"retry","label":"Retry"},{"id":"done","label":"Done","done":true}],"compose":{"hint":"Starts a run with your message"}}'
devsbd thread send --json '{"thread":"pr-123","id":"run-1","blocks":[{"type":"markdown","text":"Addressed 3 of 4 comments, **1 needs your call**."}]}'
  • Forms in messages. A message can carry a form: choice, text and yes/no questions, with defaults (a text default is how you offer a draft for editing). The dashboard pins open forms under the thread’s header: tab between questions, space to pick, e to edit a text answer, enter to see what will be sent and enter again to submit. Half-filled answers are saved as you go, so they survive a restart and show the same in every dashboard. Submitting sends one submit event with every answer, defaults filled in, and the thread shows what you answered (you answered "<title>" over each label: value) where you’ll read the owner’s reply to it. A question can follow up another (under), shown indented under it, and only be asked while one of its options is picked (when): a reply text that only matters for “Post the reply” is hidden, not required and sent as null when you pick “Don’t reply”.

  • devsandbox inbox answers threads from a shell. ls and show (with --json) read the Inbox without the daemon; reply, act, submit, done and reopen answer a thread, named <owner>/<key> or by the id ls shows. The owner gets the same events as from the dashboard.

TERMINAL
devsandbox inbox ls --view needs-you
devsandbox inbox reply pr-dispatcher/pr-123 "Rebase it first"
  • An API for editors and GUIs. The host daemon serves a JSON-lines API on a unix socket: the Inbox (threads, replies, actions, forms, done/reopen, notifications), instances, port forwards, and change notifications. devsandbox api --stdio relays it over stdin/stdout for clients that can only spawn a process; it exits 0 when its input ends and 75 only when the daemon hung up first. The npm package’s connect() wraps it with typed inbox, instances and forwards methods and events. Whoever can open the socket can act as you on every sandbox, like docker.sock. Reference: docs/api.md.
TS
const api = await devsandbox.connect({ name: 'my-tool' });
await api.subscribe(['inbox']);
const [t] = await api.inbox.list('needs-you');
  • Thread buttons that act right away. A button can carry a built-in host action that runs in the dashboard with no round trip: open VS Code on the thread’s child (at a file and line), a terminal, its logs, a port forward, a link, or rm with the usual confirm. On a stopped child, VS Code, the terminal and the forward start it first (the button says (stopped)); logs don’t, since a stopped container’s log says why it stopped. The target is always the thread’s child or the thread’s owner itself, never another instance, and there is no arbitrary host command.

  • Events: your answers, back to the thread’s owner. Every other button, a reply, a form submission, marking a thread done and reopening it become events the owner reads with devsbd events (JSON lines, each naming its thread). --wait SECS returns as soon as there’s one; --follow keeps a stream open and prints each event the moment it happens, with a ping after 30 s of quiet, and a newer follower from the same instance replaces the older one, so two copies of a loop never both act on a click. --thread KEY narrows either to one thread. Delivery is at least once: events stay until the owner acks them with devsbd events ack <id>… after saving its own state. devsbd features lists the newer verbs a helper has (thread-send, events-follow, …), so a script can check before using them.

TERMINAL
devsbd events --follow
devsbd events ack e-1790900001-3f2a
  • Done instances. An instance marked done keeps its container, worktree and runs, and shows dimmed with a ✓ in the dashboard and (done) in ps, until you remove it. Mark it with devsandbox done <instance>, a thread’s Done, or a dispatcher’s devsbd done <key>; clear it with devsandbox undone, u on the thread, or a dispatcher’s ensure reusing it. devsbd ls, ps --json and status --json report done. npm: done(name) / undone(name), and done on instance rows.
  • d removes the selected instance in the dashboard. Like s, it acts right away with no prompt: it runs rm, which still refuses a dirty worktree and asks before deleting the branch.
TERMINAL
devsandbox done web-pr-123
  • devsandbox vscode --goto opens VS Code at a file and line. The path is relative to the instance’s workspace folder. It opens (or focuses) the window as before, then has the in-container helper open the file in that window, waiting up to 30 s for it to attach; if that fails, the window still opens and the output says why the line was dropped. In the dashboard: :code <instance> --goto path:line.
TERMINAL
devsandbox vscode web-2 --goto src/main.rs:42:7
  • Select and copy text with the mouse in the dashboard. Drag in any pane to select text inside it, without borders or the panes next to it. Releasing copies it to your clipboard (copy on select, on by default); with that setting off, ctrl-shift-c or cmd-c copies it, and so does ctrl-c over a selection, for terminals that send that key plain or keep ctrl-shift-c for their own copy (Alacritty, GNOME Terminal, kitty and most Linux terminals: there the dashboard tells you ctrl-c). The copy goes through your terminal (OSC 52), so it works over SSH and from a container; under tmux, enable allow-passthrough. Inbox threads copy as rendered, with bullets, quotes, tables and links as shown and wrapped lines joined back, and dragging past the edge scrolls so a long message copies whole; the config, help and logs views work the same way. shift-drag still gives your terminal’s own selection. In the integrated terminal, apps that use the mouse (vim, tmux, zidane) now get your clicks, drags and hover, and what they copy to the clipboard now reaches yours (a setting turns that off); in a plain shell you select the output, scrollback included.

  • Dashboard settings. ? is now Settings & help: the settings on top, toggled with space, enter or a click (tab moves between them), and the key reference below. They are saved per user in ~/.local/share/devsandbox/dashboard.toml. copy on select (on) copies a mouse selection on release; terminal clipboard (on) lets apps in the integrated terminal set your clipboard.

Changed

  • The Inbox shows what’s waiting on you. Side by side: a card list on the left (title, compact age, state/level chip and sender; a selected card gets an accent bar), the selected thread always open on the right, with a ‹ Needs you │ Active │ Done │ All › strip above the list stepped by ←/→. The tab count and the yellow ✉N on instance rows now count threads that need you and unread notifications, not every unread record. In the thread, the header stays pinned at the top (title, the same state and status chip as the card, the child and whether it’s stopped, the buttons), open forms sit under it, and the conversation runs newest first, so the newest thing is read first. enter moves focus into the thread pane: 1–9 run buttons, r replies in a box at the bottom of the pane (shown only when the thread takes replies; it wraps, grows to six rows, and the line under it says what sending does; alt-enter adds a line), tab moves to an open form, d marks done, u reopens, o/t/l/p act on the thread’s child, and enter opens the link; esc steps back. The per-instance grouping and inline folding are gone: a notification’s earlier records are listed in the pane. A thread is read when selected and again if it changes while selected; notifications are marked read the same way or when you leave the Inbox after seeing them. d on a notification dismisses it and on a thread marks it done; D clears notifications only.

  • The dashboard takes more mouse clicks. Click a tab title to switch tabs. In the Inbox, click a card to select it, a view name to switch views, the thread or its reply box to focus them; the wheel moves through the cards or scrolls the thread.

  • Inbox text is rendered as markdown. Thread messages, replies and notification bodies show headings, lists, code blocks, quotes, links and tables (aligned when they fit) instead of raw ** and backticks; titles and statuses get inline code and emphasis. Long lines wrap at word boundaries. Press m on a thread to see the source. Control characters from containers are stripped before they are stored or drawn.

  • A newer devsandbox updates the helper in running instances. Before, a running dispatcher only got new devsbd verbs after devsandbox start; now the host daemon of a newer devsandbox installs them, so opening its dashboard (or any command that starts the daemon) is enough.

  • The Inbox is stored in inbox.json. It’s shared by every dashboard, the host daemon and devsandbox inbox. On first use, the notifications of the old inbox.toml are carried over; threads from earlier builds aren’t (their owners re-send them on their next pass).

  • devsbd ensure, stop, rm and done answer JSON. Each prints one JSON line instead of the bare instance name, so a dispatcher never parses prose: ensure gives name, key, sandbox, state, and whether the child was created or started; stop gives state: "stopped", rm removed: true, done done: true. A script that read the name from stdout reads .name now. Errors are unchanged: a message on stderr and a non-zero exit.

  • rm cleans up merged branches without asking. With no flag, rm first runs git fetch --prune origin, then decides about the branch the worktree has checked out. If every commit on it is already on its remote branch, or the remote branch it tracked has been deleted (a merged PR), the branch is deleted without a prompt. Only a branch with commits that exist nowhere else still prompts, and off a TTY it is kept. Branches you reused with --branch follow the same rules now. The default branch (origin/HEAD, main, master) is never deleted. When rm deletes a branch, it also deletes the branch run created if that branch is an ancestor of it. --delete-branch / --keep-branch still decide outright.

Fixed

  • Instances no longer vanish from state while their container stays. Commands saved the whole state as they had loaded it, so a slow run, rebuild or rm running alongside another command could drop the instance that command had just recorded, leaving its container and worktree behind with no entry. A dispatcher child lost this way blocked its key for good: every devsbd ensure failed with container … already exists but is not in state. State changes are now applied under a lock to the current file, written atomically. A dispatcher’s ensure also replaces such a leftover container when it carries that dispatcher’s label and its worktree is gone; if the worktree is still on disk, ensure refuses and names the worktree, so you can check it for work before removing it on the host.

  • SSH works in a dispatcher’s child as soon as devsbd ensure returns. A child that ensure had just started had no working ssh-agent for about a second, so a devsbd exec right after it (git fetch, git push) failed with Permission denied (publickey). ensure now waits (up to 12 s) for the agent relay of a child it created or started; if it isn’t ready by then, its answer carries a note saying why.

  • Long commands in the : prompt stay visible. Text past the prompt’s width was hidden though it was still run; the prompt now scrolls sideways to keep the cursor in view.

  • A press in the Inbox thread pane no longer grabs the divider. A press on the pane’s first text column started resizing the list/thread split; now only the borders do.

  • The help and logs screens keep the mouse to themselves. With either open, clicks and the wheel reached the integrated terminal hidden underneath.

  • Apps in the integrated terminal get the mouse and the clipboard. They never received clicks or drags, and what they copied to the clipboard was dropped.

  • Dismissed notifications no longer come back with several dashboards open. Each dashboard kept its own copy of the Inbox and saved it over the others’, so records dismissed in one reappeared from another, and restarts seemed to add clutter. All dashboards now share one store, so a dismissal in one is gone from all.

  • A notification is no longer lost if the dashboard dies while receiving it. The container’s copy was dropped as soon as the dashboard said it had it, before it was saved; now it’s saved first, and resent otherwise.

  • rm asks about the right branch. It used to name the branch run created, even after the worktree had switched to or renamed another branch. Now it reads the worktree’s current branch before removing it. For instances from before branches were recorded that have since been renamed, the old fallback guessed the branch name from the new name; it now uses the instance’s original name.

Commits
  • b0f96a0 fix(state): apply state changes under a lock to the current file
  • b006504 feat(inbox): show a submission’s answers where the user reads the outcome, and let a form question follow up another (under/when) so a reply for one option no longer reads as a separate question
  • b89fb10 fix(dispatch): ensure waits for a child’s ssh-agent relay after bringing it up, so an exec right after can use ssh instead of failing with publickey
  • 3e997c7 feat(tui): d removes the selected instance instead of marking it done
  • cdea7cd feat(inbox): drop the v2 put and event compat so old-shape puts are rejected with a pointer to thread send and compose, and events name only their thread
  • 4d10d56 fix(serve): retry a restored forward’s saved port for a second before moving it, since a draining predecessor or a forked child can hold it for a moment
  • b48e4a9 docs: describe the daemon, owner threads, messages, forms, follow and the inbox cli as they now work, and mark the old threads design superseded
  • 4858573 feat(tui): start a stopped child before running its vscode, terminal or forward action and answer dispatcher child ops with json, so thread buttons work on parked children and scripts never parse prose
  • ca97a65 feat(inbox): add devsandbox inbox ls|show|reply|act|submit|done|reopen so scripts and terminals can read and answer threads without the dashboard
  • 24bd49e feat(inbox): stream owner events with devsbd events –follow, one live follower per owner, so a dispatcher reacts at once instead of long-polling the store
  • b6e421e fix(api): report stdin’s eof before half-closing the relay’s socket, since a fast daemon hang-up was read as the daemon leaving and exited 75
  • 1f3a563 feat(inbox): name the event’s thread field thread and let devsbd events filter by –thread, keeping key as an alias until the dispatcher moves over
  • 619e8b0 test(serve): give the relay test’s daemon a 1 s idle timeout since 200 ms flaked under a loaded parallel run
  • 07e75b5 feat(tui): pin open forms under the thread header with choice, text and confirm widgets, a draft saved on every change and an enter-to-confirm submit
  • e77f363 feat(inbox): add forms to messages with host-kept drafts, a one-shot submission and a submit event with defaults filled, so structured input is data instead of files in the child
  • 4866133 feat(tui): pin the thread header and action row, show the feed newest first and the compose hint under the box, so the newest thing is read first and sending says what it does
  • 0cb96ec feat(inbox): add devsbd thread send and withdraw for idempotent owner messages with markdown and fields blocks, so explanations live in the feed instead of an overwritten header
  • 24e07eb feat(inbox): store threads as a v3 json feed of messages, replies, actions and collapsing markers, since the overwritten message and per-field timeline made threads read backwards
  • 1874d60 feat(config): gate inbox threads and events on a new inbox = true sandbox setting so owning threads is its own capability and dispatcher only grants child ops
  • a7f45f8 docs(inbox): name feed items by direction (message, reply, action, submission) and make enter confirm a form, since post was ambiguous next to submissions
  • 6058656 test(serve): run serve install under the real systemd and launchd
  • e16f8fd fix(serve): keep a literal $ in the systemd unit’s executable path
  • 55e133c docs(serve): record the snapshot, forward-holder and forward-bridge trade-offs as future work so they’re revisited deliberately
  • 05e4252 feat(serve): persist ad-hoc forwards in the data dir so a restarted or upgraded daemon brings them back on the same host ports
  • b044237 feat(serve): start and hand off through the installed unit, repointing it at the newer binary, so an upgrade no longer leaves the managed daemon stopped
  • ff8167b feat(serve): read keep-alive from a per-user daemon.config.toml in the data dir so the daemon setting needs no flag
  • c36ded7 docs(inbox): record the post-step-10 follow-ups agreed after the daemon review so they’re implemented before the content model
  • 666641d feat(serve): add serve install|uninstall for a keep-alive systemd user unit or launchagent so runtime-restarted dispatchers find a live host at login
  • 685c0e6 feat(api): add the api –stdio relay and a typed npm client so editor extensions and guis can drive the inbox, instances and forwards without socket handling
  • 1902817 feat(serve): own port forwards in the daemon and serve them over the api so configured and ad-hoc forwards outlive the dashboard
  • 5cc83cc feat(serve): relay ssh-agent through the daemon’s one bridge per container and pick the newest live agent a client reports, since per-command bridges duplicated relays and the daemon’s inherited agent could be stale
  • 29323e7 feat(tui): talk to the daemon through one reconnecting worker so inbox writes, change pokes and container status lines flow through the api and survive a handoff
  • e17d1d8 feat(serve): serve a json-lines api for the inbox and instances with change subscriptions so every client acts through one path
  • 19ccee0 feat(serve): run bridges, the notify drain, popups and autostart in the daemon so dispatchers keep a live host when no dashboard is open
  • f681248 feat(serve): add the devsandbox serve daemon skeleton (socket, start lock, lazy start, version handoff, idle exit) so bridges and the inbox can outlive the dashboard
  • 582a8bc refactor(inbox): route every inbox read and write through inbox::ops so the coming daemon api, cli and tui share one code path and produce identical events
  • 8299e8d fix(tui): build the inbox chip in one place and show replies in full, since the card hid the status the pane showed and multi-line replies were folded onto one row
  • 203b06a fix(tui): wrap and grow the inbox reply box and scroll the : prompt sideways, since text past the box width was invisible though it was still sent
  • dc19ab7 docs(inbox): plan the inbox redesign so each step can be implemented and reviewed on its own
  • 9c74eba fix(rm): decide on the worktree’s current branch and delete merged or pushed ones without asking, since the prompt named the stale recorded branch and merged prs left branches behind
  • 69a6cbb feat(tui): copy on select by default again and name ctrl-c as the copy key in terminals that keep ctrl-shift-c, since alacritty and most linux terminals never pass it on
  • 99f2025 docs(tui): document dashboard settings, copy keys, terminal clipboard relay and hover, and changelog the mouse fixes found along the way
  • 7d5e1dd feat(tui): forward hover to integrated-terminal apps that track all motion, since they got no pointer movement without a button held
  • c4c80de feat(tui): add dashboard settings to the ? screen and make copy on select an option, off by default, since copying every selection clobbers the clipboard
  • 46954fb fix(tui): relay osc 52 copies from apps in the integrated terminal to the outer clipboard, since the emulator dropped them and copying in zidane did nothing
  • c5a8484 docs(tui): plan selection follow-ups (terminal app clipboard, settings, hover), from first use feedback
  • c342d45 docs(tui): document mouse selection and let ctrl-shift-c copy it again, since most terminals keep that key for their own (now empty) selection
  • ca77669 feat(tui): forward mouse presses, drags and releases to terminal apps that track the mouse and select locally otherwise, since apps in the integrated terminal never saw a drag
  • a802bf1 feat(tui): copy whole rendered markdown from a thread, rejoining wrapped lines and autoscrolling past the pane edge, so a long llm message pastes as it was written
  • bd1d29d docs(tui): plan mouse text selection, since mouse capture leaves the dashboard with no way to copy text
  • abd2a63 chore: more simplifications
  • 680db64 chore: simplified README.md
  • 5473290 feat(tui): lay out markdown tables in threads as aligned columns when they fit, since llm summaries lean on them
  • 4fd02eb feat(tui): render thread and notification markdown with word wrap and a raw toggle, and strip control characters from container text, since dispatcher text is mostly llm markdown
  • 9bdbbb4 docs(inbox): plan markdown rendering in threads, since dispatcher text is mostly llm markdown
  • 268c006 feat(tui): make the tab bar, inbox cards, view strip and thread pane clickable, like the terminal panel already was
  • 44a37c9 perf(mux): shrink per-stream thread stacks to 128 KiB
  • a9b93b2 docs(tui): describe the side-by-side inbox, cards, focus zones and reply box
  • 16217a6 feat(tui): let the inbox list/thread divider be dragged, so long titles or long threads get the room they need
  • 37e8a80 feat(tui): render inbox items as compact two-line cards with a view strip, so the list reads at a glance instead of as a log table
  • a572f71 feat(tui): show the inbox list and the selected thread side by side with a reply box in the thread, so reading and answering need no pane switching
  • 510ca74 docs(inbox): plan the side-by-side inbox layout with cards and an inline reply box, from first-build feedback
  • caeb4a8 test(devsbd): retry the refused-dial check on a fresh port, since a freed ephemeral port can be taken by a parallel test
  • 010a60e docs(inbox): document threads, events, done instances and vscode –goto for dispatcher authors and dashboard users
  • 0bdab8f feat(vscode): add –goto and use it for the thread vscode action, so a thread can open the draft at the line that needs a decision
  • 69462cd feat(devsbd): add vscode-goto, which finds the attached window’s extension host and runs its own remote cli as its owner, so the host can open a file at a line
  • 069e639 feat(done): let users and dispatchers mark an instance done, so ‘I’ve looked, carry on’ no longer means removing the child
  • 7fa8d35 feat(inbox): queue user actions and replies as events a dispatcher pulls with devsbd events, so users can answer from the inbox
  • 4351ba9 feat(tui): run a thread’s host actions on its child or sender from the pane, so looking at the work needs no dispatcher round trip
  • fd255fc feat(tui): show the inbox as threads in needs-you/active/done views with a focused thread pane, so the badge counts what waits on the user, not noise
  • 7c8d623 feat(inbox): let dispatchers put and remove stateful threads through the outbox, applied idempotently so re-asserting every pass adds no clutter
  • d1ae90b feat(inbox): share one locked inbox store across dashboards and ack records only once stored, so dismissed records stay gone and a dying dashboard can’t lose one
  • 25d2a4d docs(inbox): give archiving on rm a step, it had none
  • c05ee41 fix(tui): reinstall a stale helper before spawning a bridge so running dispatchers get new devsbd verbs without a restart
  • e0472aa docs(inbox): plan inbox threads so dispatchers can show state, offer actions and take answers instead of re-sending notifications

0.6.0

The config file is now devsandboxes.toml, and it can be written in YAML too; an existing config.toml is renamed for you. Extra folders get their own worktree when someone else owns the checkout, rm --force clears out stuck instances, dispatchers can see which branches are in use, and the dashboard opens instantly and keeps its Inbox across restarts while putting far less load on dockerd.

Added

  • YAML configs. devsandboxes.yaml (or .yml) works in place of devsandboxes.toml: same keys, extends merging and validation, only the syntax differs. A sandbox hashes the same in either format, so converting a config doesn’t mark instances for rebuild. Keep one config file per config dir; with more than one, devsandbox stops with an error.
YAML
sandbox:
  web:
    folder: ../web
    image: mcr.microsoft.com/devcontainers/base:ubuntu
    services: [database]
  • devsbd branches <sandbox> tells a dispatcher which branches are already checked out. It prints a JSON list of every branch in a worktree of the sandbox’s repo and who holds it: one of the dispatcher’s own children, another instance, the base checkout, or a worktree devsandbox doesn’t know. It reads git on the host each time, so a git switch inside an instance is seen. With --ahead, each row also gets the number of commits origin doesn’t have yet, and local branches that are ahead but not checked out are listed too. A dispatcher can use it to leave alone PRs that someone is working on locally. The sandbox must be in the dispatcher’s spawn. Running dispatchers pick up the new helper when restarted.
TERMINAL
devsbd branches web --ahead
  • rm --force (-f) removes an instance whatever state it’s in. A worktree with uncommitted or untracked changes is removed anyway, changes discarded, instead of rm refusing. A step that fails (a worktree git won’t remove, a repo the host refuses to run git in) prints a warning and rm carries on, so the instance still leaves the list. Whatever couldn’t be removed stays on disk. The branch of a worktree that was left in place is kept. In the dashboard: :rm --force <instance>. npm: rm(name, { force: true }).
TERMINAL
devsandbox rm --force web-2
  • The dashboard’s Instances tree has a TYPE column when a sandbox is a dispatcher. Instances of a dispatcher sandbox read dispatcher, the others -. Without any dispatcher in the config the column isn’t shown.

Changed

  • The config file is now devsandboxes.toml. An existing config.toml is renamed the next time devsandbox runs in its config dir (with a notice on stderr), and is read as-is until then. A config.toml with no sandbox, template or services table isn’t a devsandbox config and is left alone.

  • Inbox times say how long ago a notification came in. The TIME column reads just now, 12s ago, 5 min ago, 3h ago, 1 day ago, … 6 days ago, then the date and time (Oct 2 14:32) once a notification is a week old. Before, it showed only the time of day, so yesterday’s notifications looked like today’s. The Detail pane shows the date and time.

  • folders entries get their own git worktree when the checkout belongs to someone else. If an extra folder is a git repo and it’s another sandbox’s folder, or another instance already mounts it, the instance now gets a worktree of it, detached at that checkout’s HEAD. Before, the instance mounted the live checkout, so the owner switching branches changed what the instance saw. The worktree is kept across rebuild and removed by rm. Dispatcher children always get one. To keep the old live view for an entry, write it as a table with worktree = "never", or use "always" to force a worktree:

TOML
[sandbox.web]
folder = "../web"
folders = { "/workspaces/api" = "../api", "/workspaces/.shared" = { path = "../.shared", worktree = "never" } }
  • A sandbox’s own folder now also gets a worktree when another instance mounts that folder directly as a folders entry.

  • The Inbox keeps its history. Notifications are saved in inbox.toml next to state.toml, so they survive closing the dashboard; d/D remove them from the file too. A notification with the same --key no longer replaces the earlier one: it becomes the head of a thread, with the older ones folded under it (→ to show them). With more than one instance sending, the Inbox groups them per instance, foldable like the Instances tree. The limit is now 200 notifications per instance (was 50, and 200 overall).

  • worktree-link no longer migrates old shared-files/<sandbox>/ stores. The automatic move only relinked the instance being run, so every other instance on the same repo was left with dangling links. A link into an old store is now skipped with a warning that shows where the file should go. Move it there, delete the old link, and rebuild.

  • The dashboard opens instantly and fills in as the runtime answers. The tree is drawn from state.toml and the config right away, statuses (… until then) arrive with the first container listing, and CPU/MEM after that. Before, the dashboard waited about 1.5s for docker stats and showed “no sandboxes defined” meanwhile.

  • The dashboard puts much less load on dockerd. CPU/MEM now refresh every 7s instead of every 2s (statuses still every 2s), the runtime version is asked once, and process lists are only fetched while the Instances tab is open. Instance drift is read from the container listing instead of two docker inspects per instance.

Fixed

  • Listing containers no longer makes dockerd compute their disk sizes. Every ls, ps, gc, autostart pass and dashboard refresh asked for container sizes as a side effect of the output format, which spiked dockerd’s CPU every 2s while the dashboard was open and made each listing take ~300ms instead of ~20ms.

  • Opening VS Code no longer clears an instance’s extensions. devsandbox vscode and the dashboard’s o read the sandbox config from the current directory, not from the instance’s own config directory. Run from anywhere else, the extensions list was rewritten as empty, so VS Code didn’t install anything. They now read the instance’s own config. If that config still can’t be read, the list is left as it was. Instances already hit by this get their list back the next time you open VS Code from the new version.

  • An rm that failed partway can be retried. A worktree an earlier rm had already removed made the next rm fail too; it is now pruned instead.

  • The dashboard no longer warns about mount sources run creates itself. shared-volumes paths and per-instance mount sources that don’t exist yet are made by run, so a sandbox with no instance no longer shows them as problems.

  • VS Code labels the primary workspace root by its folder name, matching the extra folders roots, instead of the instance name.

Commits
  • 3a63926 feat(config): read devsandboxes.toml (or .yaml/.yml) and rename a legacy config.toml, so the config file is recognizable on its own and can be written in yaml
  • b25563f feat(tui): show inbox times as how long ago, then date and time after a week, so older notifications don’t pass for today’s
  • 16e793a feat(tui): accept –force on the :rm prompt so the dashboard can remove instances the cli’s rm –force can
  • 2b0bb63 feat(npm): let rm pass –force so node callers can remove instances with uncommitted changes or failed teardown steps
  • 647b127 feat(rm): add –force so an instance can be removed despite uncommitted worktree changes or failed teardown steps
  • aa50d82 docs(changelog): note the dashboard startup, dockerd load and type column changes
  • 3aacc06 perf(tui): collect stats every 7s, ask the runtime version once and fetch processes only on the instances tab to cut idle dockerd load
  • bff0cf7 perf(docker): list containers with an explicit ps template so the cli stops asking dockerd to compute every container’s disk size
  • fe376b0 perf(tui): render the tree from state and config before asking the runtime, and read instance drift from ps labels instead of two inspects per instance
  • 376bdb6 fix(tui): show the tree right away on startup instead of a false ‘no sandboxes’ message while docker stats samples
  • 092958d fix(tui): size the services column to its content when type is shown so the type column sits beside it, not at the far edge
  • 8dcbfd2 fix(tui): stop warning about shared-volumes and per-instance mount sources that run creates before any instance exists
  • b0ad91f feat(tui): add a type column to the instances tree so dispatcher instances stand out from the ones they spawn
  • e7239b9 fix: update changelog and cleanup plan
  • 20d8f6a docs(dispatch): document devsbd branches in the automations guide, design and changelog so dispatcher authors know to check branch holders before acting
  • f2a9e43 feat(devsbd): expose the branches op as devsbd branches [–ahead] so dispatcher scripts can call it
  • 985f9ef feat(dispatch): add a read-only branches control op so a dispatcher can see which branches are checked out, and unpushed, before touching a PR
  • 1a723d4 docs(dispatcher): plan a devsbd branches op so dispatchers can tell which branches are in use
  • 2a485c4 fix(vscode): resolve extensions from the instance’s config root and keep the registered ones when it can’t be resolved, so opening from another directory no longer wipes them
  • 11081b9 feat(tui): persist the inbox and thread keyed notifications per instance
  • f655b58 fix(vscode): label the primary workspace root by its directory name, so it matches the extra folders instead of showing the instance name
  • 3975498 fix(rm): prune instead of remove a worktree a previous rm already removed, so an rm that failed later can be retried
  • deb0e1a fix(config): hash folders tables that restate the default like the plain string, so moving an entry to the table form isn’t drift
  • c2acba2 docs(folders): document per-entry worktrees in the run design doc and changelog so the release notes explain the new default and the never opt-out
  • f756796 feat(rm): remove an instance’s folders worktrees too, before the primary so an interrupted rm can be retried
  • 756df51 feat(run): give folders entries a detached worktree when another sandbox owns or already mounts the checkout, so its branch can’t shift underneath the instance
  • 435549b feat(run): record folders mounts and decide per entry who owns the live checkout, so a direct extra mount also reserves it from other instances
  • 878e1e7 feat(config): accept { path, worktree } folders entries so each extra folder can opt in or out of its own worktree
  • 0997d43 fix(worktree): stop migrating old per-sandbox worktree-link stores, since moving the copy left every other instance’s links dangling; old links are now reported for a manual move
  • 0467ffb docs(readme): lead with a theme-aware logo and a short pitch pointing at the docs site
  • c8be397 fix(site): space pages that open on prose from the article divider
  • b72fa8d docs(site): install skills via skills.sh in the quick start, style the agent prompt outside examples
  • 78d0afd docs(site): lead the quick start with the agent, move the cli walkthrough to going further
  • 61b2cdb chore(skills): hide repo-only skills from npx skills add
  • e750396 fix(site): center diagram arrowheads on their lines
  • 5ffe39b feat(site): pitch the CLI as low-level local tooling on the landing page, so readers see devsandbox can replace docker compose for agent-built stacks

0.5.0

Sandboxes can now start themselves after a reboot and run unattended dispatcher scripts that create, drive and clean up their own child instances. Messages from inside containers show up in the dashboard and as desktop notifications. See docs/automations-guide.md. run --json, a command-less exec and exact-name matching make devsandbox easier to drive from scripts and other programs, and the docs now live at devsandboxes.com.

Added

  • autostart: a sandbox’s instances come up once per boot, when the dashboard opens or after your first devsandbox run / start. A sandbox with no instance yet gets one; dispatcher children are left to their dispatcher.
  • autostart = "runtime": docker and podman restart the containers themselves at boot (--restart unless-stopped), with no devsandbox process needed. Podman also needs podman-restart.service enabled. On Apple container it behaves like true. Changing autostart never marks an instance as drifted; the next start applies it in place.
TOML
[sandbox.triage]
folder = "../triage"
autostart = "runtime"   # or true
  • Boot hook: new containers run devsbd boot on every start, which brings back the helper daemon and, in runtime mode, postStartCommand (output in /run/devsandbox/boot.log). Containers created before this need devsandbox rebuild --force to get it.
  • devsbd notify: any sandbox can send the user a message. It is queued inside the container until a dashboard is open, then shown in the new Inbox tab (unread badges on instance rows, enter opens an http(s) link, d/D dismiss) and as a desktop notification (notify-send / osascript).
TERMINAL
devsbd notify --level warn --key pr-123 --link https://github.com/o/r/pull/123 "PR 123 needs you"
  • Dispatchers: a sandbox that declares dispatcher can manage child instances through devsbd, while the dashboard is open. Children are named <sandbox>-<key>, always get their own worktree, are marked ⇠ <dispatcher> in the TUI, and outlive the dispatcher. Host-side operations log to <data>/devsandbox/logs/dispatch-*.log. Exit codes: 75 when no dashboard is connected, 77 when denied.
    • spawn = ["*"] means every sandbox in the config root except other dispatchers, and it grants their mounts, docker socket and privileges. Children can never be dispatchers themselves.
    • max-instances defaults to 10 and counts stopped children too.
    • --branch accepts plain branch names only ([A-Za-z0-9._/-]) and is used literally, with no ${…} expansion.
    • --env refuses names that could take over processes in the child: PATH, HOME, SHELL, LD_*, DYLD_*, GIT_*, NODE_OPTIONS, PYTHON* and similar. Its values are kept across a devsandbox rebuild of the child, including when ensure recreates a missing container. On an existing child, ensure --env replaces the saved values, and every command devsandbox runs in the child (exec, dashboard terminals, devsbd exec runs, lifecycle commands) uses them from then on, over a same-named remoteEnv; already-running processes keep the old ones until a rebuild.
TOML
[sandbox.pr-dispatcher]
folder = "../pr-dispatcher"
autostart = "runtime"
dispatcher = { spawn = ["web"], max-instances = 10 }   # "*" = any non-dispatcher sandbox
postStartCommand = "nohup ./loop.sh >loop.log 2>&1 &"
TERMINAL
devsbd ensure web --key pr-123 --branch feat/x   # create or start web-pr-123
devsbd exec pr-123 --detach -- zidane -p "babysit PR 123"
devsbd run logs pr-123 <id> --follow
devsbd ls; devsbd stop pr-123; devsbd rm pr-123
  • Runs: commands started with devsbd exec are tracked in the child (id, log, exit status). Follow them with devsbd run ls|logs|wait, clean them up with devsbd run rm (--force kills a running one) and devsbd run prune --keep N; the TUI shows a child’s last runs under its processes.
  • devsbd is on PATH in containers (/usr/local/bin/devsbd).
  • forwardPorts: while the dashboard is open it forwards each running instance’s configured ports to 127.0.0.1, the same way devsandbox port does (no container restart). Each instance keeps its host ports across dashboard restarts, and a port already taken moves to the next free one up. A global service’s port is forwarded once, however many instances list it. In the Ports tab these rows are marked (config); d stops one until the dashboard reopens.
TOML
[sandbox.api]
services = ["db"]
# 3000, "db:5432" as in devcontainer.json; "8080:3000" / "15432:db:5432" pick the host port
forwardPorts = [3000, "8080:3000", "db:5432"]
  • run --json: prints one {"schema": 1, "data": {...}} document for the new instance (name, instance_id, sandbox, container, workspace, folder, base_folder, worktree, branch; the last two null without a worktree). Everything child processes print (image builds, docker run, git, lifecycle hooks, autostart) goes to stderr, so stdout parses as-is for programmatic and GUI callers.
  • exec <name> without a command opens a login shell (zsh, else bash, else sh), the same one the dashboard’s terminal opens. With neither -i nor -t given it runs with -i, plus -t when stdin is a TTY, so it works as is from a terminal or a pty (node-pty); explicit flags are used as given.
  • npm: execArgv(name, cmd?, { tty, interactive }) returns { file, args } for spawning devsandbox exec yourself (node-pty, child_process.spawn) without shell quoting.
  • rm --delete-branch / rm --keep-branch answer the “delete branch?” question without prompting, so scripts and other callers without a TTY can clean up the branch run created. --delete-branch deletes it like answering yes (git branch -D, so unmerged commits go too); --keep-branch keeps it. Branches run reused rather than created are always kept. With neither flag, rm still asks on a TTY and keeps the branch otherwise. npm: rm(name, { deleteBranch }), where true / false pass --delete-branch / --keep-branch.
  • Docs site at devsandboxes.com: a quick start, a playable tour of the dashboard, the full config.toml reference, the Node API reference, ten worked examples (parallel coding agents, shared and per-instance services, package caches, ssh-agent and git push, babysitting pull requests, porting a devcontainer.json, …), full-text search (⌘K / Ctrl+K) and this changelog.

Changed

  • Worktrees reuse existing branches. run --branch X, and a dispatcher’s devsbd ensure --branch X, no longer fail when X exists:

    • a local X is checked out as it is;
    • an X that only exists on origin (a PR head, for example) becomes a local branch tracking origin/X, so a plain git push updates it;
    • a branch that’s already checked out somewhere else gives an error saying where.

    rm only offers to delete branches devsandbox created itself.

  • New containers use sh -c '… devsbd boot & exec sleep infinity' as their command instead of sleep infinity, so images need /bin/sh.

  • start sets the container’s restart policy from autostart, overwriting a policy set by hand.

  • While the dashboard is open it keeps a helper connection to every running instance that has the helper, not only when an ssh-agent is being relayed.

  • devsandbox status --json instance rows include instance_id and, for children, dispatcher.

  • rebuild applies worktree-include. Before, adding a pattern marked instances as drifted, but the rebuild copied nothing. Now rebuild copies any missing matching files into the worktree; existing files are never overwritten. A copied file you delete on purpose comes back on the next rebuild, so drop the pattern instead.

  • devsandbox port 3000 (and the Ports tab) moves to 3001, 3002, … when 3000 is taken on the host, instead of a random port.

  • A service ports entry that gives only the container port ("5432") now gets the same port on the host when it’s free, instead of a random one. If the port is taken, the runtime still picks one.

  • An exact instance name wins. With instances web and web-2, devsandbox exec web (and rm, stop, logs, …) now means the instance web instead of an ambiguity that failed off a TTY and prompted on one, including inside a pty. An exact instance id counts too; sandbox and folder names only apply when nothing matches exactly. exec now uses the same resolution as the other verbs, so it also accepts an instance’s id after a rename.

  • npm (breaking): run() resolves with the new instance’s record (RunRecord) instead of its name; use (await run(...)).name for the old value. It calls run --json, so build and autostart output can no longer end up as the “name”.

  • In the generated VS Code workspace, the main folder is labelled with its directory name (webcontainer) instead of the instance name (webcontainer-sqlite), like the extra folders roots. If an extra root has the same directory name, the instance name is kept. Existing instances pick this up on rebuild --force.

Fixed

  • rm on a dirty worktree no longer leaves an instance without its container. rm removed the container, volumes and services first, then git worktree remove refused the uncommitted or untracked changes, stranding the instance. It now checks the worktree first and refuses with the list of changes, removing nothing.
  • Keyboard chords like ctrl+m reach apps in dashboard terminals. In terminals that support the kitty keyboard protocol (kitty, Alacritty ≥ 0.13, Ghostty, WezTerm, foot, iTerm2 ≥ 3.5), the dashboard now turns the protocol on and passes it through to apps inside its terminals that ask for it. zidane’s ctrl+m, for example, no longer arrives as Enter. Shells and other apps that don’t ask for it get the same keys as before. GNOME Terminal and tmux don’t support the protocol, so there ctrl+m and Enter still can’t be told apart.
  • The mouse wheel scrolls full-screen apps in dashboard terminals. It used to scroll only the terminal’s own scrollback, which full-screen apps don’t have. Now apps that track the mouse (zidane, vim with mouse=a, htop) get the wheel directly, and pagers like less and man get ↑/↓. Shells still scroll their history.
  • Forwarded ports reach servers listening only on IPv6 loopback. Dev servers told localhost (webpack-dev-server, Vite, Node 17+) often bind only [::1], while forwards dialed 127.0.0.1, so every connection was refused. A loopback dial now also tries the other family (127.0.0.1 ↔ ::1). Running instances pick up the new helper when restarted.
  • worktree-link works for several sandboxes on the same folder. The shared copy used to live in one store per sandbox. The base checkout can only link to one of them, so the second sandbox’s worktrees got nothing. The store is now per repo: shared-files/<folder name>-<hash>/, shared by every sandbox on that folder. The next run or rebuild moves files from the old shared-files/<sandbox>/ stores and relinks the base checkout and worktrees. Instances still running on an old store need a rebuild. If two old stores held different copies of a file, the one the base checkout linked to wins, and the other stays where it was with a warning.
  • ssh-agent forwarding no longer breaks run on macOS + docker in builds without the helper (cargo install). The fallback bind-mounted the Mac’s launchd agent socket, which can’t cross into the runtime VM, so docker run failed with mkdir …/agent/<instance>.sock: file exists. It now mounts Docker Desktop / OrbStack’s /run/host-services/ssh-auth.sock instead. Builds that embed the helper were never affected.
  • devsandbox port and the Ports tab work on macOS. Accepted connections inherited non-blocking mode from the listener there, so every relay read failed with os error 35 and no forwarded connection got through.
  • 0.4.0 never reached crates.io (the publish step failed on the release archives in the checkout); cargo install devsandbox goes straight from 0.3.4 to this release.

Security

  • Git on the host can no longer run code planted by a sandbox. Worktree instances mount the base repo’s .git read-write, so a container could plant hooks or command-running config (core.fsmonitor, core.sshCommand, filter drivers, include.path, …) that the host’s git worktree add / fetch / worktree remove would then run as you.
    • Every git command devsandbox runs on the host now has hooks and fsmonitor turned off.
    • It first checks the repo’s local config against an allowlist. If a key could run a command, it refuses and names the key: review it and remove it. ext:: remotes and object alternates are refused as well.
    • This affects every worktree instance, not only dispatcher children.
  • The commands devsandbox runs as root inside containers (helper install, boot file, Apple /etc/hosts update, workspace file) use /bin/sh with a fixed PATH, so they don’t depend on the container’s PATH.
  • Limits on what a container can make the host do:
    • at most 8 concurrent notify/control handlers per container;
    • output from host-run commands is capped and timed out (5 min for in-child commands, 30 min for dispatch operations, which are killed with their whole process group);
    • desktop popups are rate-limited per instance (3 at once, then one every 10 s), and repeats of the same key within 60 s don’t pop up again;
    • the inbox keeps at most 50 entries per instance (200 overall);
    • devsbd run ls lists only the newest 50 runs and shortens long command lines.
Commits
  • c0af00e feat(site): add a changelog page rendered from CHANGELOG.md
  • d70eb16 fix(npm): keep execArgv names from parsing as flags, correct exec/run docs
  • d5e0884 fix(rm): refuse a dirty worktree before tearing anything down, add branch flags
  • 623710f feat(cli): make run, exec and name resolution usable from programs
  • 88a16b9 docs(site): add a dashboard tour page that walks through each feature beside a sticky, playable mini dashboard, so readers learn the tui before installing it
  • e3d2b28 feat(site): fake the command prompt, port forwards, logs, config explorer and run/stop in the mini dashboard, so the docs can teach every key hands-on
  • ef295b7 docs(tui): document kitty keyboard pass-through and wheel routing for integrated terminals
  • 47a6a19 docs(tui): correct the pty TERM comment, docker exec does not forward it into the container
  • ba99a4f fix(tui): answer xtversion in integrated terminals, since opentui apps like zidane ignore the kitty flags reply until the terminal identifies itself
  • 52d9194 fix(tui): send the mouse wheel to integrated-terminal apps that track the mouse or use the alternate screen, which has no scrollback to scroll
  • fb9e02d feat(tui): send kitty-encoded keys to integrated-terminal apps that enabled the protocol, so ctrl+m no longer arrives as enter
  • 95c6236 feat(tui): emulate the kitty keyboard protocol’s flag stacks and queries in the integrated terminal, so apps inside can opt into it
  • 0ac6963 refactor(site): extract the mini dashboard into a mountable component, so the docs can host a second one driven from outside
  • dd3ad31 feat(tui): enable the kitty keyboard protocol on terminals that support it, so chords like ctrl+m can reach apps in the integrated terminal
  • 721b5ab feat(site): make t open a terminal in the landing mini dashboard, split and tabbed like the real one, so the preview shows the feature it advertises
  • 7ff786f fix(site): tighten the examples that need no setup and make the pr babysitter readable at a glance
  • b126a11 docs(site): trim the pr babysitter example to the new example shape and give its loop a codex variant, so it works with the reader’s agent
  • f5cb4c1 docs(site): trim the features and devcontainer porting examples to the new example shape, keeping the porting reference tables readers come for
  • a1ee4f4 docs(site): trim the port forwarding and vs code examples to the new example shape, and stop calling forwardPorts ignored now the dashboard honours it
  • c0d24e9 docs(site): trim the caches and ssh push examples to the new example shape, with the agent picked by the reader on the push one
  • 2b6c5ae docs(site): trim the shared and isolated services examples to the new example shape, with the agent picked by the reader on the isolated one
  • 06fb312 feat(site): let readers pick their coding agent once and trim the parallel agents example, so examples read short and match the agent they use
  • 0e41f55 feat(site): animate the how-it-works section and make the mini dashboard playable, so pressing o shows vs code attaching to a sandbox
  • ab6d22b feat(site): how-it-works section with a host/sandboxes/agents diagram next to a mini dashboard, so visitors see how the tool is used
  • 59e5404 feat(site): position the landing page as devcontainers for ai agents and drop the devcontainer comparison the audience doesn’t need
  • b0b5a9b fix: add github icon to link
  • edda15a ci(site): move pages deploy into a dispatchable workflow that release calls, so docs-only changes can ship between releases
  • 9e23dc4 ci(site): build the docs site on every pr and deploy it to pages on release, so the docs always match what users can install
  • 83bd81b fix(site): load sidebar nav styles on every page, so the mobile drawer outside /docs has no bullets or stray chevrons
  • 5c0961c docs(config): escape the pipes that truncated the dispatcher row and name the real service/gc commands
  • 9400e75 feat(site): 404, og image, one reading order with prev/next and edit links, a link check in the build, and a warning-free build
  • 874d74b feat(site): pagefind full-text search behind a ⌘k dialog, so any field, flag or example is one keystroke away
  • 6dd530f feat(site): remaining examples, port forwarding, vscode, features, the pr babysit dispatcher and porting a devcontainer.json
  • f140d5f feat(site): examples section with one recipe per use case and a copyable agent prompt, starting with worktrees, services, caches and ssh
  • 2fc8c4b feat(site): node api reference generated from the npm package’s index.d.ts, so the docs follow the published typings
  • d127052 feat(site): config reference rendered from the config-toml-spec skill, so the site and the agent skill never drift
  • ce29397 feat(site): quick start page, from install to a second worktree instance and the dashboard
  • fc10522 feat(site): landing page that pitches devsandboxes as devcontainers multiplied, with a hero that mirrors real run/ps output
  • 7454a57 feat(site): scaffold the astro docs site with the prototype’s design system, so docs pages can be written against a real shell
  • cd577ab feat(port): forward sandboxes’ forwardPorts from the tui, keeping each instance’s host ports in state so they stay the same across restarts
  • 70553ce feat(services): give a bare service port the same host port when it’s free, instead of letting the runtime pick a random one
  • 043c1de fix(port): clear o_nonblock on accepted sockets, since macos inherits it from the listener and every relay read failed with os error 35
  • a0061d2 fix(port): try the other loopback family when dialing, so dev servers bound only on ::1 (node’s localhost) are reachable
  • 44f357a fix(ssh-agent): mount docker’s magic agent socket on macos, since the launchd socket can’t cross into the vm and failed docker run
  • 5607ad3 fix(worktree): keep worktree-link files in one store per repo, so several sandboxes on the same folder all get the shared files
  • fb94fab feat(dispatch): ensure –env replaces a child’s saved env and every exec applies it, so new values take effect without a rebuild
  • 37fc704 docs(changelog): list the step 13-15 commits
  • 9d7efc8 feat(dispatch): devsbd run rm and run prune, so dispatchers can clear runs instead of letting them pile up
  • 2608a23 fix(autostart): never start or create dispatcher children at boot, so idle children the dispatcher parked stay stopped
  • 54193b6 feat(run): keep –env values in state so rebuilds, including a dispatcher recreating a child, don’t drop them
  • 57237dc fix(rebuild): copy worktree-include files on rebuild so the drift a new pattern raises is actually fixed by rebuilding
  • 2feb352 docs(changelog): unreleased notes for the security hardening and existing-branch worktrees
  • d022e5c feat(run): reuse an existing local or origin branch for a worktree instead of refusing, so instances and dispatcher children can work on PR branches
  • 36f532a fix(port): look up lsof on the container’s PATH again, the –env deny-list already closes the PATH injection and the fixed PATH hid lsof in /usr/local/bin
  • e5404ac fix(devsbd): cap run ls to the newest 50 runs and bounded argv/meta reads, so a container user can’t make run listings unbounded
  • aac1539 fix(dispatch): bound handler threads, child output and runtimes, and rate-limit desktop popups, so a container can’t exhaust the host or flood the desktop
  • 4c1549f fix(dispatch): deny dangerous –env names, keep children from being dispatchers, cap children at 10 by default and run host root execs with a fixed PATH, so a dispatcher can’t gain root in a child or self-replicate
  • 11b9f7a fix(dispatch): validate dispatcher branch names and never template-expand them, so a container can’t read host env through ${localEnv:…}
  • a5948da fix(run): host git runs with hooks and fsmonitor disabled and refuses repos whose local config can run commands, so a sandbox can’t execute code on the host through the shared .git
  • c59d627 docs(automations): plan security fixes for host git, branch values, dispatcher authz and resource limits
  • f44f329 docs(changelog): unreleased notes for automations, and plan steps for the follow-up decisions
  • 5c2e49a docs(automations): user guide, architecture and module map for autostart, notify and dispatchers
  • b080bf1 fix(autostart): keep the boot’s pass when the runtime is unreachable and pin dispatch subprocesses to the parent’s backend, found auditing apple container
  • 7868cb5 feat(dispatch): tracked runs in child instances (devsbd exec, run ls/logs/wait), so dispatchers can start agents and follow their outcome
  • d499764 feat(dispatch): devsbd ensure/ls/stop/rm over a control channel served by the dashboard, failing fast with 75 when no host is attached
  • 32a030d feat(dispatch): dispatcher config and host-side control handler that runs ops as logged subprocesses, so a sandbox can safely own child instances
  • d5a1f6f feat(tui): inbox tab with per-instance unread badges, so notifications stay reviewable after the status line moves on
  • cec26b4 feat(tui): serve devsbd notify streams from dashboard bridges and raise desktop notifications, so container messages reach the user
  • 35f2e7c feat(devsbd): notify queues records in a durable outbox and the daemon flushes them to notify-capable hosts, so no message is lost while no host is attached
  • c3ae6ce feat(devsbd): boot hook in the container command so runtime restarts bring back the helper daemon and postStartCommand without a host
  • 75345d6 feat(autostart): runtime mode sets –restart unless-stopped so docker/podman bring sandboxes back on boot without devsandbox, flipped in place on start
  • 9075dec feat(autostart): bring autostart sandboxes up once per boot from the tui or the first run/start, so automations survive a reboot
  • 74ff407 docs(automations): step plan, with triggers, per-root boot ids and runtime-mode poststart settled
  • 6bb660e docs(automations): design for autostarted sandboxes, notify and dispatcher-driven child instances
  • 0442d37 fix(ci): download release archives outside the checkout so cargo publish sees a clean tree

0.4.0

Worktree instances can now bring along the gitignored files a repo needs to run, like .env files, instead of starting without them.

Added

  • Copy gitignored files into new worktrees. A repo’s .worktreeinclude file is always honored, and worktree-include adds more patterns (gitignore syntax). Matching untracked files are copied from the base checkout into each new worktree and never overwrite existing files.
  • Share gitignored files across all instances. Each worktree-link path is moved to one shared copy under shared-files/<sandbox>/, and every worktree gets a symlink to it. An edit in one instance shows up in all of them. This also works on Apple container, which can’t bind single files. Globs work within one path segment (* / ?), and a leading ./ is accepted.
TOML
[sandbox.web]
folder = "../web"
worktree-include = ["fixtures/*.local.json"]   # copied once, per worktree
worktree-link = [".env", "packages/*/.env"]    # one live copy shared by every instance
  • A CHANGELOG.md, which is also used as the GitHub release notes.

Fixed

  • TUI: pressing s to start an instance refreshes the list shortly afterwards, so the new state appears right away.
Commits
  • 52da316 docs(changelog): readable release notes with examples, written ahead of release via a release skill
  • 9dff820 docs(changelog): backfill changelog and generate it on release so github releases carry notes
  • c6925a0 feat(run): one-segment globs and leading ./ in worktree-link
  • ebd7200 feat(run): carry gitignored files into worktrees via copy or shared link
  • d836a5a fix(tui): when start is pressed, refresh instance list after 500ms
  • 7005680 chore(ci): fix warnings reported

0.3.4

On-demand port forwarding, devcontainer features that need privileges or entrypoints (docker-in-docker works now), and a full config.toml reference.

Added

  • devsandbox port: makes a port inside an instance or one of its services reachable on the host, without publishing it when the container is created and without a restart. Forwards reconnect by themselves after the target restarts or is rebuilt. When the image has lsof, the command shows which process is listening. Unix hosts only.
TERMINAL
devsandbox port api 3000                      # localhost:3000 -> api's :3000
devsandbox port api 8080:3000                 # bind host :8080 instead
devsandbox port api --service postgres 5432   # api's postgres, via the api container
devsandbox port --service redis 6379          # a global service, no instance named
  • TUI Ports tab: lists active forwards; p adds one and d removes one. Quitting never hangs on a forward that is still connecting.
  • Privileged features and feature entrypoints. A feature that declares privileged: true now runs the container --privileged, and feature entrypoints run at every container start, so features such as docker-in-docker work out of the box. privileged can also be set directly on a sandbox. Both are skipped with a warning on Apple container.
TOML
[sandbox.infra]
image = "mcr.microsoft.com/devcontainers/base:ubuntu"
[sandbox.infra.features]
"ghcr.io/devcontainers/features/docker-in-docker:2" = {}
  • config-toml-spec skill: the complete reference for config.toml (every key, merge rules, ${…} variables), written for both people and agents.

Fixed

  • ${devcontainerId} in feature mounts resolves to the instance id. Per-instance volumes are deleted on rm and kept on rebuild.
  • A sandbox mount on the same target now replaces a mount inherited from a template, instead of Docker rejecting the duplicate.
  • Nested mounts are applied parent-first on every runtime, not only on Docker.

Internal

  • run.rs and the TUI app.rs are split into per-concern modules. Docker-dependent tests are now required to pass on CI.
Commits
  • ff124fb fix: don’t specify the model on the skill
  • e45afbd docs: fix broken rustdoc intra-doc links
  • 75e1732 refactor(tui): split app.rs into per-concern modules
  • 704bbaf refactor(run): split run.rs into per-concern modules
  • 155b3de fix(features): resolve ${devcontainerId} and reap per-instance volumes on rm
  • 4f1512c feat(features): run feature entrypoints at container start
  • 7fe0e4f feat(config): honor sandbox-level privileged instead of ignoring it
  • feb8ec7 feat(features): honor feature privileged so docker-in-docker style features work
  • facce5f fix(run): order mounts parent-first before handing them to the runtime, since only docker sorts nested mounts itself and that is undocumented
  • c060d7c docs(skills): add a config-toml-spec skill as the full config.toml reference, so users get one accurate source and devsandbox-cli stops carrying a drifting copy
  • a71ea70 fix(run): let a downstream mount on the same target replace an inherited template mount, since only feature mounts could be overridden and docker rejected the duplicate target
  • aa37f71 test(ci): build the devsbd helper and run the whole workspace on ci, and gate docker/helper tests behind #[docker_test] so they skip locally but fail on ci instead of silently passing
  • 2d1bb66 docs(repo): drop references to internal project names from docs and test fixtures
  • e7c0d57 test(devsbd): retry the prefer-port test with a fresh port when a parallel test grabs the released one first
  • 6df4d8b test(devsbd): retry the embedded helper exec on etxtbsy, since parallel tests’ forks can briefly inherit the just-written binary’s write fd
  • 8bee843 docs(port-forwarding): document the forwarding protocol, flow control and caps routing, plus the port command and ports tab, so docs match what shipped
  • fdcf7e7 feat(tui): run ports-tab forwards on a worker thread that owns them, and make forward teardown prompt so quitting never hangs on a handshake
  • 5ef2538 feat(tui): add a ports tab with a port prompt and p/d shortcuts so forwards can be requested and listed from the dashboard
  • cea5d40 feat(port): show which process listens on a forwarded port via lsof when the image has it
  • dfe7777 feat(port): add a foreground devsandbox port command that forwards instance or service ports until ctrl-c
  • f1e6783 feat(port): resolve forward routes to an instance, a service via a running instance, or the service container itself, re-evaluated on every reconnect so restarts and rebuilds heal
  • 07e5a0f feat(devsbd): add the host forwarder engine that listens locally and tunnels each connection through a self-healing bridge
  • 81de9a5 feat(devsbd): dial forwarded connects in the daemon and negotiate caps end to end so forwards only run against capable helpers and agent-less bridges never take ssh-agent routing
  • 9328728 feat(devsbd): add per-stream credit flow control, half-close and host-initiated streams to the mux so bulk forwarded traffic can’t stall other streams or the keepalive
  • 2cc5523 refactor(devsbd): let mux streams be tcp as well as unix sockets so forwarded ports can reuse the stream routing
  • 95a1904 feat(devsbd): add connect/window/eof/caps frames and close reasons so port forwarding can land without a protocol version bump
  • c038810 docs(port-forwarding): plan on-demand port forwarding over devsbd so dev servers bound to localhost and services are reachable without recreating containers
  • a7a3b7b chore(release): keep the readme’s pinned download links on the released version
  • dc734b1 docs(readme): recommend npm and release archives, which embed devsbd, over the limited cargo install; document the relay as the main ssh-agent path

0.3.3

Release pipeline only: npm packages are now published with npm trusted publishing (OIDC) instead of a long-lived token. No changes to the CLI.

Commits
  • cff095b ci(release): publish to npm with trusted publishing instead of a long-lived token

0.3.2

The npm root package is renamed to devsandboxes, because npm rejected devsandbox as too similar to an existing package. The installed binary is still called devsandbox.

TERMINAL
npm i -g devsandboxes
npx devsandboxes --help
Commits
  • 53cedc3 fix(npm): rename the root package to devsandboxes since npm rejects devsandbox as too similar to dev-sandbox

0.3.1

The CLI is now published to npm, so a Rust toolchain is no longer needed to install it. Prebuilt binaries ship as per-platform packages (linux x64/arm64, macOS arm64, windows x64), and the package also has a typed Node API.

The root package was renamed to devsandboxes in 0.3.2; install that version or later.

Commits
  • 86df202 feat(npm): publish the cli to npm with per-platform binary packages and a typed node api, so npx devsandbox works without a rust toolchain

0.3.0

ssh-agent forwarding no longer needs a socket bind mount. A small static helper (devsbd) is embedded in the release binaries, installed into each container, and relays the host agent over exec stdio. This works on every runtime, including Apple container, and keeps working when the host agent socket changes.

Added

  • Relayed ssh-agent forwarding through the embedded devsbd helper, used by default. Relays detect wedged connections with keepalives and handshake timeouts, and restart the daemon if the container was restarted outside devsandbox. git over ssh works inside postCreateCommand and the other lifecycle commands.
  • devsandbox vscode <name> attaches VS Code to an instance from the CLI, using the same launch path as the TUI’s o key.
TERMINAL
devsandbox vscode web

Changed

  • Release archives (and later the npm packages) embed the helper. cargo install builds don’t, and fall back to the bind mount, which works only on Linux docker/podman.
  • The TUI manages ssh-agent bridges on a worker thread, so the dashboard never blocks on them.
Commits
  • 3401365 docs(readme): say release binaries embed the helper now that the release job builds it
  • 0529cde fix(run): give lifecycle commands the forwarded ssh-agent so ssh git operations in setup commands work
  • 45e7b2b chore(release): build and embed the devsbd helpers in release binaries, and stop excluding src/devsbd from the published crate
  • afc6ddc docs(devsbd): plan the release helper job and agent access for lifecycle commands
  • 8ec0e6b chore(devsbd): fail ci builds that lack the helper, keep helper-less builds incremental, and catch a stale embedded helper in tests
  • 66be187 refactor(tui): own ssh-agent bridges on a worker thread so reconciling never blocks the dashboard, and back off on version mismatch
  • a8071fc feat(devsbd): let the bridge start a missing daemon so containers restarted outside devsandbox keep their relay
  • bc882e8 feat(devsbd): detect wedged relays with keepalive and a handshake timeout so agent requests can’t hang forever
  • 304bef7 feat(devsbd): leave room in the wire protocol for additive changes and bound what a container can make the host do
  • 8012eb8 feat(devsbd): make the relay the default ssh-agent path so forwarding survives agent rotation and needs no socket mount
  • 56e49d8 docs(devsbd): plan relay-first forwarding and the helper hardening found in review
  • 7e5a8b0 feat(devsbd): relay the host ssh-agent through a helper daemon and stdio bridges so forwarding needs no socket mount
  • 4d6f29d docs(agents): run workspace tests so the devsbd helper crate is checked too
  • 9e89b74 feat(devsbd): add the shared host<->helper frame protocol so both sides decode one wire format
  • 95ec425 feat(devsbd): install the helper on run/start so containers carry the build matching the cli
  • 3681527 feat(devsbd): embed a static in-container helper so relays work on every runtime without network or file binds
  • 2a522c5 docs(devsbd): plan an embedded in-container helper for host<->container relays
  • 8840709 feat(vscode): add devsandbox vscode command to attach vs code from the cli, sharing the tui’s launch path

0.2.4

Fixes the Windows build, which broke the 0.2.3 release: 0.2.3 has no published binaries, so this is the first release that ships its changes. Also clarifies the docs.

Commits
  • b7449fc fix: clarify docs
  • fffd63e fix: windows build

0.2.3

ssh-agent forwarding, worktrees that start from the remote’s default branch, and process signalling from the dashboard.

This tag never got a GitHub release (the Windows build failed); its changes shipped in 0.2.4.

Added

  • ssh-agent forwarding. The host SSH_AUTH_SOCK reaches every instance through a per-instance symlink mount, and start re-points it before the container starts. Forwarding therefore survives reboots and re-logins without a rebuild. exec sets SSH_AUTH_SOCK on every session. rm removes an instance’s link and gc removes orphaned ones.
  • Worktrees start from the remote’s default branch. Instead of branching from whatever is checked out in the base repo, run fetches origin and branches from origin/HEAD (or main/master), without touching the base checkout. Use run --base or worktree-base to pick a different start point.
TOML
[sandbox.api]
folder = "../api"
worktree-base = "origin/develop"
  • TUI process rows can be signalled: t sends SIGTERM and K sends SIGKILL to the selected pid. x closes an exited terminal where it is.

Fixed

  • Process listings use pids from the container’s namespace, so signals reach the right process. Images without ps fall back to docker top and are shown as not signalable. The listing no longer includes its own ps process.
Commits
  • 1c56b2f feat(run): start worktrees from a fetched origin default branch
  • 5d339f3 fix(runtime): drop the self ps row from process listings
  • f4a3737 feat(docker): fall back to docker top when the image lacks ps
  • d6a6896 fix(docker): list container-namespace pids so signals hit the right process
  • b72d8ff feat(tui): make process rows signal-only (SIGTERM/SIGKILL)
  • 140977f feat(tui): close exited terminal with x in place
  • 300e420 feat(gc): reap ssh-agent links; rm drops its own
  • a9a8cd2 feat(start): re-point ssh-agent symlink before container start
  • d28302d feat(exec): inject SSH_AUTH_SOCK on every exec
  • 7255429 feat(run): forward host ssh-agent via per-instance symlink mount
  • 9d77713 docs(ssh-agent): plan agent forwarding via re-pointable symlink mount

0.2.2

Renames no longer lose per-instance state, feature mounts are applied, and rebuild --force is added.

Added

  • rebuild --force recreates an instance even when no drift is detected. Use it to pick up devsandbox-side changes that the config hashes can’t see.
TERMINAL
devsandbox rebuild web --force
devsandbox rebuild --all --force
  • Feature-declared mounts are applied, such as the host docker socket from docker-outside-of-docker. A sandbox mount on the same target overrides the feature’s, and mounts the host can’t satisfy are skipped with a warning.
  • Persistent instance ids. Everything tied to an instance (${instance} mounts, worktree dir, shell history, isolated services) is keyed by an id that never changes, so renaming an instance is purely cosmetic.
  • TUI prompt completion is driven by the same spec table as parsing, so every flag completes (including rebuild --force).

Fixed

  • Shell history survives in VS Code terminals: HISTFILE is pinned in the rc files, not only in the container env.
  • rename requires the exact instance name and no longer hangs the TUI.
  • Missing instances can be rebuilt from the TUI.
Commits
  • ad8c40b fix: make sure the subagent has a model specified
  • 947a512 docs(agents): note spec.rs in the tui module map
  • 60676c6 feat(tui): spec-driven tab completion; rebuild –force completes
  • 8fc0f0f refactor(tui): drive prompt parsing from the spec table
  • b1aa1d9 feat(tui): declarative command spec table for the prompt
  • 65a5377 docs(naming): drop plan- prefixes/suffixes, add tui prompt spec plan
  • a12f65c feat: add a –force flag to rebuild to be able to account from devsandbox changes
  • a9c8efa fix(tui): missing instances can now be rebuilt
  • d85edd5 feat(state): persistent instance_id so rename never moves per-instance state
  • 73d8025 feat(features): honor feature-declared mounts
  • 4fa1783 fix(run): pin HISTFILE in the rc files, not just container env
  • 8d62bbd fix(rename): require exact instance name and never write stdout in the tui
  • f9db061 refactor(features): drop unused cache_dir method
  • d0ff413 chore(cargo): exclude release.sh, .agents, .github from crate

0.2.1

Packaging only: the crate gains a description and the MIT license so it can be published on crates.io, and docs are excluded from it.

TERMINAL
cargo install devsandbox
Commits
  • 9c03b41 chore(cargo): add description, mit license, exclude docs from crate

0.2.0

Instance lifecycle commands (start, stop, rebuild, rename), an integrated terminal in the dashboard, JSON output for scripts, drift detection for dockerfiles and services, and Apple container support.

Added

  • Separate lifecycle verbs. run always creates a fresh instance. start / stop resume and suspend existing ones (--all for every instance), and rebuild (alias recreate) applies config changes in place while keeping the worktree, branch and per-instance state.
TERMINAL
devsandbox run web --branch me/login-fix
devsandbox stop --all
devsandbox start web
devsandbox rebuild --all        # recreate every drifted instance
  • Integrated terminal in the TUI: t opens a shell in the selected instance (zsh when available, otherwise bash) in its workspace folder. Terminals open as tabs; [/] cycle between them, x closes one, ctrl-]/F12 leaves the terminal, and mouse scrollback works.
  • Machine-readable output. status --json prints the full dashboard snapshot in a versioned envelope, and ps, ls, stats, inspect accept --json (contract in docs/json-output.md).
TERMINAL
devsandbox status --json | jq '.data'
devsandbox ps --json
  • Service drift and rebuild. Editing a dockerfile now counts as drift, for sandboxes and services alike. service rebuild <name> recreates a service and rewires running sandboxes without restarting them, and service ls lists services like ls lists sandboxes. Drifted services are marked in the TUI and can be rebuilt from there.
TERMINAL
devsandbox service ls
devsandbox service rebuild postgres
  • rename (CLI and r in the TUI), plus logs, inspect, stats subcommands that work the same on every runtime.
  • shell-rc: host shell snippets sourced by the container’s ~/.zshrc/~/.bashrc.
TOML
[template.base]
shell-rc = ["rc/aliases.sh"]
  • Configurable worktree branch via worktree-branch (supports ${instance}) or run --branch.
  • Apple container is supported as a runtime.
  • TUI: coding-agent processes are highlighted in the process tree, and the detail panel shows an agent count.
  • A devsandbox-cli skill that teaches coding agents to drive the CLI.

Fixed

  • Worktrees are created under the config dir instead of the base repo, and a branch name that already exists is rejected with a clear error.
  • The base folder is reused correctly once its direct-mount instance is removed.
Commits
  • 7c5beb0 ci: release on crates.io
  • 5bd48a2 docs: document build-hash drift, service rebuild and service ls
  • 781f636 feat(tui): surface service drift and rebuild services from the dashboard
  • f1bf8b4 feat(services): service ls mirroring ls, sharing the tui’s row builder
  • e45c31e feat(services): rebuild a service without restarting its sandboxes
  • 536dc85 feat(config): hash dockerfile contents so edits register as drift
  • 736dfea feat(tui): probe zsh before bash for integrated terminal
  • c08fc3d feat(rename): rename instances via r in the tui and a cli verb
  • 08cae4e feat: add shell-rc
  • 1e3de35 feat: set integrated terminal cwd for workspaces
  • 7d6afef docs(json): document the json output contract
  • dd7da88 feat(json): –json output for ps, ls, stats, inspect
  • 1274c13 feat(status): machine-readable snapshot via status –json
  • 5647ba3 refactor(snapshot): extract snapshot collection from tui
  • 27dd37e docs(json): plan for machine-readable output
  • 9e8386f chore: add support for apple container as fallback
  • 33b65e1 docs: commit implemented plan
  • e1d0c0e fix(tui): help bar shows t term and the real s verb
  • 1781881 feat(tui): wire the terminal into the event loop and mouse
  • c5587cf feat(tui): render the integrated terminal panel
  • 904856e feat(tui): terminal tabs, focus model and key routing
  • f86f54a feat(prompt): flag and branch-value completion for run
  • b78ae47 feat(tui): pty session core for the integrated terminal
  • 144130c docs(agents): record the conventional-commit format
  • 0cb8abb feat(rebuild): add –all for batch drift fixes
  • 6777b44 docs(skill): run never reuses an instance
  • cdf458f Point drift docs at rebuild instead of rm + run
  • 58e1d07 TUI: s on a stopped drifted instance rebuilds instead of bare-starting
  • 7e7e3c0 Add rebuild (alias: recreate) to fix config drift in place
  • 635ed25 Extract materialize() from run()
  • cb9c929 fix: bug with base folder never reused
  • 05194eb feat: separate instance lifecycle: run creates, start/stop restart
  • f670ac6 Reject a worktree branch that already exists
  • ac72c27 Make worktree branch name configurable
  • d2678a8 Fix worktree created under base repo instead of config dir
  • 44d8c48 cli: add logs, inspect, and stats subcommands
  • a3fca98 feat: add skills/devsandbox-cli/SKILL.md skill to teach an agent how to use the tool
  • 49facee tui: show on-demand agent count in the instance Detail panel
  • 4cc7764 tui: highlight coding-agent processes blue in the process forest
  • 04aaf9e docs: add README.md

0.1.1

First release. devsandbox runs devcontainer-style sandboxes directly on docker/podman from one config.toml, with no devcontainer CLI and no daemon.

Highlights

  • devcontainer semantics in TOML. Sandboxes use devcontainer property names (image, build, mounts, containerEnv, remoteUser, the lifecycle commands, VS Code extensions…). Unknown keys are a hard error. extends composes reusable templates: tables merge, arrays concatenate.
  • Native devcontainer features. Features are fetched from their OCI registry and baked into a derived image, without the devcontainer CLI.
  • Concurrent instances of one repo. A second run of the same folder gets its own git worktree and branch, so instances never share a checkout. Instance names are deterministic (web, web-2, …).
  • Services. Sidecar containers are reachable by name. scope = "isolated" (the default) gives each instance its own copy; scope = "global" shares one per project. gc removes the ones no instance uses.
  • Shared caches and state. caches shares package caches across instances, persist-shell-history keeps a per-instance history, and ${sharedVolumes} / ${instance} place other state per instance.
  • Config drift detection. Each container records a hash of its merged config, so an edited config shows up as drift.
  • VS Code. Attach to an instance as a named workspace, with extra roots from folders and remoteUser honored.
  • TUI dashboard (run devsandbox with no arguments): a tree of sandboxes and instances with live CPU/memory, a process tree for each instance, a services view, a config explorer (original vs resolved, next to docker inspect), logs, and a : command prompt with history and completion.
  • Static release builds for Linux (musl), macOS and Windows.
TOML
[services.postgres]
image = "postgres:16"

[template.base]
caches = ["pnpm", "cargo"]
persist-shell-history = true
[template.base.features]
"ghcr.io/devcontainers/features/node:1" = { version = "lts" }

[sandbox.web]
extends = "base"
folder = "../web"
image = "mcr.microsoft.com/devcontainers/base:ubuntu"
services = ["postgres"]
postCreateCommand = "pnpm install"
folders = { "/workspaces/shared-lib" = "../shared-lib" }
TERMINAL
devsandbox run web             # first instance mounts ../web directly
devsandbox run web             # second instance: web-2, on its own worktree
devsandbox exec -it web-2 zsh
devsandbox rm web-2
Commits
  • afb786a fix: remove unused file
  • 362f295 gc: reap orphaned shell-history files
  • b834316 rm: keep the managed shell-history file
  • 415763a fix: now using devsandboxes, this file is no longer needed :tada:
  • 020ff66 feat: ${sharedVolumes} and ${instance} mount variables
  • 342e928 feat: open sandboxes as named VS Code workspaces via a folders property
  • 377c54b fix(test): add connection timeout
  • 00eaf4d fix: pin the rust version to the container version
  • 4114307 feat: flag sandboxes with no image or build in the dashboard
  • 9831fc8 feat: validate config entries in the dashboard and log run errors
  • 392d028 docs: switch example sandboxes to native features
  • a134871 run: bake devcontainer features into a derived image
  • e326c19 features: fetch OCI feature artifacts natively
  • cf318d4 config: type the devcontainer features property
  • 1b2a410 docs: plan for native devcontainer features support
  • 9260620 fix: failing test on local
  • a4861d7 Add CI workflow to catch build/test breakage before release tags
  • f90232f chore: fix CI release logic
  • 155e30b agents: orchestrator and implementer skills
  • fab60a2 docs: add the first plans
  • f70d803 feat: add basic support for macOS
  • ccc958f tui: split accent purple from selection green
  • a225787 tui: config modal splits into config | docker inspect panes
  • 3569eff tui: process forest under expanded instances
  • f259245 stop: first-class command distinct from rm
  • f50ec78 vscode attach: honor remoteUser in the name config
  • 6b88849 tui: one-key run prefill and VS Code attach from the tree
  • 303cae6 tui: instances tab as a sandbox tree
  • 7bc7e89 tui: drop plan-step references from UI text and comments
  • 6e6b1d3 tui: help overlay, instance logs, header totals, staleness marker
  • 5da536e tui: command prompt with history and tab completion
  • 012860a tui: config explorer modal with original vs resolved toggle
  • 59d3139 tui: services view joined from config, state and docker
  • 5b4f6fa tui: live instances view with background docker collection
  • 7b48117 tui: dashboard scaffold behind bare invocation
  • 6201081 ls: readable table with colors and an empty-state message
  • 19acc23 Recursive/list extends, remote+container user, init, workspace vars, caches
  • 7469421 Add managed per-instance shell history
  • f80ee7c Implement mounts, additive merge, and global/isolated services
  • 0a0df68 services: shared per-project network + service containers, add gc
  • e415d9a run: deterministic naming, reuse, worktrees, config-hash drift; add rm
  • 8f56f9c Add release CI: static builds for linux (musl), macos, windows on version bump
  • a1ee668 Type sandbox config to the devcontainer schema and expand runtime support
  • 722fe21 Add runtime basics: run, ps, exec, host-side state store
  • cad57f9 Add config core: TOML model, extends deep merge, ls command
  • 1fd3f93 initial commit

Source: CHANGELOG.mdEdit this page on GitHub(opens in a new tab)

MENU /
View on GitHub
EXAMPLES
All examplesEvery recipe on one page.Parallel coding agentsSeveral coding agents on one repo, fully isolated from one another.One shared PostgresOne development database for all your sandboxes and their instances.Private services per instanceEvery instance gets its own Postgres and Redis, so agents never touch each other's data.Shared package cachesNew sandboxes stop downloading the same packages again.ssh-agent and git pushAgents push over ssh with your host's keys, which never enter the container.Port forwarding on demandOpen a sandbox's dev server or database from your host, without restarting anything.Attach VS CodeOpen any instance in VS Code with one key.devcontainer FeaturesAdd Node, Rust or the GitHub CLI to any image, in TOML.Babysit pull requestsAn agent keeps your open PRs merged with their base, and pings you when it can't.Port a devcontainer.jsonTurn an existing devcontainer.json into a devsandbox config.