Skip to main content
Every node offers three directories, and the one a recipe mounts declares the data’s lifetime. rm reads the space, not the recipe: convention with teeth.
Screens verified by tests/test_spaces.py.

The three spaces

spacethe questionon rm
volume/does this outlive the box?kept — deleting always takes its own --volume
held/does something outside the box point at it?asks first when it holds files
tmp/does anybody but the box care?removed silently
dabs never reads tmp to decide anything — it is the box’s scratch and nobody else’s business.

Name them in a recipe

$NODE_* variables name the box’s own spaces; $PARENT_* name the spaces of the place the box stands on (your project, a worktree). They substitute in source paths only — they are not environment variables inside the box.
recipes:
  spacey:
    description: demo box writing into all three spaces
    image: shell
    command: [sh]
    sources:
      - mount: .
        path: /work
      - mkmount: $NODE_VOLUME/cache      # survives rm, until --volume
        path: /cache
      - mkmount: $NODE_HELD/results      # rm asks before taking it
        path: /results
      - mkmount: $NODE_TMP/scratch       # rm reaps silently
        path: /scratch
      - mkmount: $PARENT_VOLUME/keepers  # comes back on the NEXT box
        path: /keepers
After the box writes into each, dabs ls shows a dot per space that holds files:
local (bwrap, this machine)
  NODE                KIND     VOL  HELD  TMP  STATE  WHERE
  myproj-________     project  ●                      ~/myproj
  └─ spacey-________  box      ●    ●     ●    live   ~/.dabs/nodes/spacey-________  (shell-____________)

What rm does with each

The preview spells out every space’s fate before you consent:
╭─────────────────────────────────────────────────────────────╮
│ Removing spacey-________ reaps 1 node(s):                   │
│   NODE             KIND  VOL  HELD  TMP  STATE              │
│   spacey-________  box   ●    ●     ●    live               │
│ ⚠ 1 node(s) hold a held space — deleted on proceed          │
│ 1 node(s) hold volume data — kept (pass --volume to delete) │
│ 1 node(s) hold tmp scratch — always cleared                 │
│ Proceed?                                                    │
╰─────────────────────────────────────────────────────────────╯
rm -y then stops the box, takes held (that is what -y consented to), clears tmp — and keeps the volume, telling you how to take it when you mean it:
✓ shell-____________ stopped
⚠ volume kept: …/.dabs/nodes/spacey-________/volume   (dabs rm spacey-________ -y --volume to reap it)
A box whose volume still holds files stays listed (STATE gone) so the data has an owner. rm <box> -y --volume finishes the job.

$PARENT_VOLUME: what comes back next time

A box’s own volume dies with its node; the parent place persists. So a box that wants data back on the next box writes it to $PARENT_VOLUME. Write keep-me there, reap the box (--volume and all), boot a fresh one:
dabs exec spacey-________ 'cat /keepers/f; cat /cache/f'
keep-me
cat: can't open '/cache/f': No such file or directory
The parent’s volume reloaded; the old box’s own cache is gone. This is exactly how a Claude box keeps its session transcripts across re-ups.
$NODE_EPHEMERAL / $PARENT_EPHEMERAL are permanent aliases of $NODE_HELD / $PARENT_HELD (the held space’s former name) — old recipes keep working.