Skip to main content
A recipe’s sources: list places things into the box at a path. Exactly one of four kinds names each source’s origin — and the kind decides whether the box’s writes ever reach your disk.
Screens verified by tests/test_sources.py.
kindwhat lands in the boxyour files
mount:a live bind — writes hit the hostshared with the box
mkmount:a live bind; the host dir is created (0700) if absentshared; provisioned on first use
copy:a snapshot taken at box startuntouched
worktree:a fresh git branch off HEAD, mounted liveuntouched — the branch is elsewhere
Add ro: true to any mount to make it read-only inside the box.

Copy vs mount, demonstrated

This recipe hands the box a snapshot of the project and a read-only reference directory:
recipes:
  snapshot:
    description: a copied /work plus a read-only reference mount
    image: shell
    command: [sh]
    sources:
      - copy: .
        path: /work
      - mount: ./data
        path: /ref
        ro: true
The box writes a file into its copy:
dabs exec snapshot-________ 'echo boxed > /work/new.txt; ls /work'
dabs.yaml  data       new.txt
Your directory never sees it:
ls
dabs.yaml  data
And the read-only mount refuses writes outright:
reference data
sh: can't create /ref/nope: Read-only file system

What a copy leaves behind

A copy: . source snapshots your cwd into a workdir node (its bytes live in that node’s held space), and the boot says so:
✓ kept: workdir …/.dabs/nodes/myproj-________/held/work
The snapshot survives the box on purpose — it may hold work the agent produced. Review it at the printed path, then reap it like anything else: dabs rm <workdir-id> -y.
Repeated copy:/scratchbox boots mint a new workdir node each time. They accumulate; sweep them periodically or dabs ls fills with stale snapshots. See Caveats.

mkmount: provisioning on first boot

mkmount: is mount: that creates its host origin if missing — say it where you mean “provision this”. The canonical use: a login/config dir a harness fills on first run, shared by every later box that names it:
- mkmount: ~/.dabs/shared/claude
  path: /root/.claude
The first box boots with an empty dir, you log in once inside, and every later box mounting that path is already logged in. A plain mount: whose origin is missing is treated as a typo and refused:
dabs: recipe "broken": mount source /…/does-not-exist does not exist (use mkmount: to create it)

Where a snapshot lands: at:

For worktree: and copy: sources, at: says where the bytes land in the new node’s own spaces — e.g. at: $NODE_HELD/worktree (the default), so rm asks before reaping the checkout. See Spaces.