---
name: warden-zfs
description: Manage ZFS datasets and snapshots for Bastille and the Warden runtime on FreeBSD. Use when configuring clawdie-runtime datasets, validating Bastille ZFS layout, snapshotting the control-plane jail, or planning rollback-safe jail storage.
---

# Warden ZFS

Use this skill for the storage layer under Bastille and Warden.

## Canonical storage model

- zpool: `zroot`
- Bastille ZFS prefix: `clawdie-runtime`
- expected datasets:
  - `zroot/clawdie-runtime`
  - `zroot/clawdie-runtime/jails`
  - `zroot/clawdie-runtime/releases`
  - `zroot/clawdie-runtime/templates`

## Responsibility split

- ZFS: datasets, snapshots, rollback
- Bastille: jail lifecycle on top of those datasets
- Warden: execution model and profile policy

Do not mix ad hoc dataset trees with the Bastille-managed `clawdie-runtime`
namespace.

## Workflow

1. Read `references/dataset-layout.md`
2. If you need snapshot policy or rollback guidance, read `references/snapshots.md`
3. If you need validation or repair steps, read `references/troubleshooting.md`
4. Prefer the scripts in `scripts/` to render checks and snapshot commands

## Dry-run rule (mandatory)

**Every destructive ZFS command MUST be run with `-n` (dry-run) first.** This applies to `zfs destroy`, `zfs rollback`, `zfs send`, `zpool destroy`, and `zpool create`. Only after reviewing the dry-run output and confirming it is correct should the real command be executed.

Note: `zfs snapshot` does NOT support `-n` because snapshots are non-destructive (instant, zero-cost). Just run `zfs snapshot` directly.

Workflow:
1. Run the command with `-nv` (dry-run + verbose) to preview what will happen
2. Show the output to the operator
3. Only then run the same command without `-n` to execute

Example:
```sh
# Step 1: preview
zfs destroy -nv zroot/clawdie-runtime/jails/controlplane@old-snapshot

# Step 2: review output, then execute
zfs destroy -v zroot/clawdie-runtime/jails/controlplane@old-snapshot
```

Commands that are read-only (`zfs list`, `zfs get`, `zpool status`) do not need `-n` since they do not modify state.

## Safe defaults

- keep Bastille on `zroot`
- keep Bastille prefix `clawdie-runtime`
- snapshot before risky jail changes
- do not handcraft parallel jail dataset trees outside Bastille

## Snapshot naming

Sanoid automated snapshots follow: `autosnap_YYYY-MM-DD_HH:MM:SS_<policy>`

Operator-requested snapshots follow: `<description>_YYYY-MM-DD_HH:MM:SS`

Examples:
```
autosnap_2026-03-08_18:46:47_hourly      # sanoid
pre-security_2026-03-08_21:30:00         # operator request
postgres-ready_2026-03-08_19:00:00       # operator milestone
```

Do not use day-first or month-abbreviation formats (e.g., `08-mar-2026`).
Stick to ISO-style `YYYY-MM-DD` for consistent sorting and Sanoid compatibility.

## Expected operator outcomes

- Bastille datasets exist under `zroot/clawdie-runtime`
- control-plane jail can be snapshotted and rolled back safely
- worker strategy can later reuse the same Bastille ZFS namespace
