@microck/raft 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,179 @@
1
+ ---
2
+ title: Defaults and note policies
3
+ description: Set controller defaults for new boxes and require box note fields on a host.
4
+ ---
5
+
6
+ # Defaults and note policies
7
+
8
+ Raft has two kinds of settings:
9
+
10
+ | Setting | Stored in | Applies to | Change with |
11
+ | --- | --- | --- | --- |
12
+ | Defaults for `new` | `~/.config/raft/config.json` on the controller | That controller only | `raft config set` and `raft config unset` |
13
+ | Required box note fields | Incus project `raft` on each host | Every controller that uses the host | `raft policy` |
14
+
15
+ `raft config` prints both: the effective defaults with their source, and each
16
+ location's policy.
17
+
18
+ ```sh title="CONTROLLER: Inspect defaults and policies"
19
+ raft config
20
+ ```
21
+
22
+ ```json title="Example output"
23
+ {
24
+ "file": "/root/.config/raft/config.json",
25
+ "defaults": {
26
+ "location": {
27
+ "value": "lab",
28
+ "source": "config"
29
+ },
30
+ "ttl": {
31
+ "value": 3600,
32
+ "source": "config"
33
+ },
34
+ "cpu": {
35
+ "value": 1,
36
+ "source": "built-in"
37
+ },
38
+ "memory": {
39
+ "value": "2GiB",
40
+ "source": "built-in"
41
+ },
42
+ "disk": {
43
+ "value": "shared",
44
+ "source": "built-in"
45
+ }
46
+ },
47
+ "policy": {
48
+ "lab": {
49
+ "require": [
50
+ "purpose",
51
+ "owner"
52
+ ],
53
+ "changed": "2026-10-21T09:12:44Z by ubuntu from me@laptop"
54
+ }
55
+ }
56
+ }
57
+ ```
58
+
59
+ `raft config` contacts every configured host to read its policy. It fails and
60
+ names the location when a host is unreachable.
61
+
62
+ ## Defaults for new
63
+
64
+ Set a default for any of `location`, `ttl`, `cpu`, `memory` and `disk`:
65
+
66
+ ```sh title="CONTROLLER: Prefer bigger, longer boxes on paris"
67
+ raft config set location paris
68
+ raft config set ttl 3600
69
+ raft config set cpu 4
70
+ raft config set memory 16GiB
71
+ raft config set disk 20GiB
72
+ raft config unset cpu
73
+ ```
74
+
75
+ Each command prints the old and new value. `raft new` takes an explicit flag
76
+ first, then the configured default, then the built-in default (first configured
77
+ location, 600 seconds, 1 CPU, 2 GiB, the shared pool). `resume`, `fork`,
78
+ `extend` and `recover` take no defaults: their TTL and target are deliberate
79
+ choices, and omitted sizes on resume and fork keep the source limits.
80
+
81
+ Size defaults (`cpu`, `memory`, `disk`) apply only where the built-in sizes do:
82
+ to boxes created from the image. A box from `new --from` keeps the snapshot's
83
+ location, CPU, RAM and disk, and a fork keeps its source's, whatever
84
+ `config.json` says. Only a flag changes them:
85
+
86
+ | Command | CPU and RAM | Disk |
87
+ | --- | --- | --- |
88
+ | `new` | Flag, then config, then 1 CPU / 2 GiB | Flag, then config, then the shared pool |
89
+ | `new --from` | Flag, then the snapshot's | `--disk SIZE`, then the snapshot's |
90
+ | `fork` | Flag, then the source's | The source's |
91
+
92
+ A `disk` default needs own box disks on the target host. On a location without
93
+ the `raft-disks` pool, or with too little unreserved space, `raft new` fails with
94
+ the same error as `--disk` and names the configured value as the cause:
95
+
96
+ ```text
97
+ $ raft new --purpose "Run the tests"
98
+ raft: lab has no raft-disks pool, so own box disks are not enabled there; nothing was created. Enable them with python3 deploy/deploy.py --location lab --disk-pool SIZE. The 20GiB own disk comes from defaults.disk in /root/.config/raft/config.json; give --disk shared or --disk SIZE, or run raft config unset disk
99
+ ```
100
+
101
+ `--disk shared` puts one box on the shared pool despite a `disk` default. It is
102
+ refused with `--from`, which keeps the snapshot's disk.
103
+
104
+ The file accepts a value exactly when the same text is accepted as the flag.
105
+ An unknown key, a bad value or malformed JSON makes `raft new` fail with the file
106
+ path and the key; Raft never ignores part of the file. Repair a bad key with
107
+ `raft config set KEY VALUE` or `raft config unset KEY`. Writes are atomic, keep
108
+ mode `0600` and remove the file when no default remains.
109
+
110
+ Scripts and CI jobs that need stable sizing should pass `--location`, `--ttl`,
111
+ `--cpu`, `--memory` and `--disk` explicitly, because defaults differ between
112
+ controllers.
113
+
114
+ ## Required box note fields
115
+
116
+ A host policy makes every box on that host carry the listed [box note](../reference/cli.md#note)
117
+ fields. `purpose` is always part of a note, so a policy must include it:
118
+
119
+ ```sh title="CONTROLLER: Require an owner on lab"
120
+ raft policy lab --require purpose,owner
121
+ raft policy lab --clear
122
+ ```
123
+
124
+ With that policy, a command that would leave a box without an owner fails before
125
+ it changes anything and says how to fix it:
126
+
127
+ ```text
128
+ $ raft new --location lab --purpose "Run the tests"
129
+ raft: location lab requires --owner (host policy). Add it: raft new --purpose "..." --owner "..." [--next "..."]
130
+ ```
131
+
132
+ The policy covers every command that creates a box or changes its note:
133
+
134
+ | Command | Note checked |
135
+ | --- | --- |
136
+ | `new`, including `new --from` | The note given to `new`; a snapshot's note is never inherited |
137
+ | `fork` | The note given to `fork`, otherwise the source box's note |
138
+ | `recover` | The note given to `recover`, otherwise the note in the archive |
139
+ | `note BOX` | The new note; `--clear` is rejected while a policy requires fields |
140
+
141
+ On `fork` and `recover`, `--purpose`, `--owner` and `--next` replace the whole
142
+ note, like `raft note`. Give all the fields the copy needs; omitted fields are
143
+ not taken from the source. `recover` without note flags can only check the
144
+ archived note after import. When it fails, Raft deletes the imported box in the
145
+ same locked step and reports the error. Pass the note to `recover` to fail before
146
+ the upload instead.
147
+
148
+ The controller checks the policy first, so the error arrives before any work.
149
+ The host checks it again in the same locked transaction that writes the note,
150
+ so a policy change cannot race a creation. That transaction checks the
151
+ requested size against the host and reserves own-disk space first; any refusal
152
+ comes before Incus creates the box.
153
+
154
+ ## Audit trail
155
+
156
+ `raft policy` prints the change, for example
157
+ `lab: required note fields none -> purpose,owner`. The host records the last
158
+ change in the project key `user.raft.require.changed` (UTC time, the SSH account
159
+ and the controller's self-reported user and hostname) and writes a `raft` entry
160
+ to the host journal:
161
+
162
+ ```sh title="HOST: Review policy changes"
163
+ sudo incus project get raft user.raft.require
164
+ sudo incus project get raft user.raft.require.changed
165
+ sudo journalctl -t raft
166
+ ```
167
+
168
+ ## Limits
169
+
170
+ Note policies are a guardrail for agents, not a security boundary:
171
+
172
+ - Anyone with SSH and sudo on the host can change or remove the policy, or create
173
+ boxes with native `incus` commands that skip it.
174
+ - Controllers older than the release that added policies do not check them.
175
+ Upgrade every controller that uses a host before relying on its policy.
176
+ - The controller identity in `user.raft.require.changed` is reported by the
177
+ controller and is not verified. The SSH account comes from `sudo`.
178
+ - Raft checks that required fields are present and nonempty, not that their text
179
+ is accurate.
@@ -64,7 +64,15 @@ docker run --rm my-app
64
64
 
65
65
  ## Managing disk space
66
66
 
67
- Nested container layers and volumes reside on the shared 60 GiB `raft-data` Btrfs storage pool.
67
+ Nested container layers and volumes live in the box's filesystem. By default that is the shared 60 GiB `raft-data` Btrfs pool, where one box's Docker data can fill the pool for every box.
68
+
69
+ On hosts with own box disks enabled, give Docker-heavy boxes their own disk. Docker data stays inside it, and builds fail with "No space left on device" at the cap instead of filling the host:
70
+
71
+ ```sh title="CONTROLLER: Create a box with its own 40 GiB disk"
72
+ box=$(raft new --disk 40GiB --ttl 3600 --purpose "Docker image builds")
73
+ ```
74
+
75
+ See [Own box disks](../reference/capacity.mdx#own-box-disks).
68
76
 
69
77
  Prune dangling images without deleting active container data:
70
78
 
@@ -28,8 +28,8 @@ Use `raft new --disposable` for temporary work. A fork is persistent unless
28
28
  `--disposable` is explicitly supplied; recovered boxes are persistent.
29
29
  Back up work before stopping a disposable box.
30
30
 
31
- Persistent stopped boxes still occupy a saved slot and storage. Each host allows
32
- four saved boxes. `raft prune --older-than 7` previews old stopped boxes;
31
+ Persistent stopped boxes still occupy storage. Raft does not cap how many boxes
32
+ a host keeps. `raft prune --older-than 7` previews old stopped boxes;
33
33
  `--yes` applies deletion. There is no automatic persistent-box retention cleanup.
34
34
 
35
35
  ## Read and reset the deadline
@@ -9,6 +9,7 @@
9
9
  "background-jobs",
10
10
  "long-running-services",
11
11
  "scripting-ci",
12
+ "config-policy",
12
13
  "desktop-services",
13
14
  "snapshots-forks",
14
15
  "backup-recovery",
@@ -94,9 +94,10 @@ raft exec "$copy" -- ls -la /workspace/my-project
94
94
  ```
95
95
 
96
96
  The template stays on its source host. Its source box can be running when you
97
- copy the saved snapshot. `new --from` uses normal creation defaults when sizing
98
- is omitted: 1 CPU and 2 GiB RAM. The new box has its own handle, MAC address,
99
- deadline, and saved slot.
97
+ copy the saved snapshot. `new --from` keeps the snapshot's CPU, RAM and disk
98
+ unless `--cpu`, `--memory` or `--disk SIZE` overrides them; `--disk` gives it an
99
+ own disk of a new size. The new box has its own handle, MAC address
100
+ and deadline.
100
101
 
101
102
  The snapshot includes packages, project files, credentials, and enabled services.
102
103
  It does not include process memory. Changing the original after snapshot creation
@@ -19,11 +19,13 @@ Raft does not have a global `--json` option. Parse each command's actual format:
19
19
  | --- | --- |
20
20
  | `new`, `fork`, `recover` | One location-qualified handle |
21
21
  | `exec --detach` | One `rfcmd-` job ID |
22
- | `list` | JSONL: one object per box, with `box`, `state`, `stopAt` |
22
+ | `list` | JSONL: one object per box, with `box`, `state`, `stopAt`, `disk`, `note` |
23
23
  | `info` | One pretty-printed native Incus metadata JSON object |
24
24
  | `usage` | One JSON object containing running resource counters |
25
- | `limits --json` | One JSON object per location (JSONL across multiple hosts) |
25
+ | `capacity --json` | One JSON object per location (JSONL across multiple hosts) |
26
26
  | `locations` | One JSON object per configured location (JSONL) |
27
+ | `config` | One pretty-printed JSON object with `file`, `defaults`, `policy` |
28
+ | `config set/unset`, `policy` | One line describing the change |
27
29
  | `prune` | JSONL candidates or deleted records with `box`, `action` |
28
30
  | `status` | systemd `ActiveState`, `SubState`, `ExecMainStatus` property lines |
29
31
  | `logs` | Guest journal text |
@@ -31,23 +33,35 @@ Raft does not have a global `--json` option. Parse each command's actual format:
31
33
  | Other inspection/maintenance commands | Human-readable text or no output |
32
34
 
33
35
  `list` emits nothing when there are no boxes. `stopAt` is the host Unix deadline
34
- as a string, or an empty string when absent. `info` uses native Incus keys rather
36
+ as a string, or an empty string when absent. `disk` is `"shared"` or the box's
37
+ own disk size, such as `"20GiB"`. `note` is the box note object with
38
+ `purpose` and optional `owner` and `next`, or `null`. `info` uses native Incus keys rather
35
39
  than a normalized Raft schema. `usage` includes `memoryBytes`, `memoryLimit`,
36
40
  `cpuTime`, `effectiveCpus`, `filesystemUsedBytes`, `filesystemAvailableBytes`, and
37
- `filesystemScope`; disk values describe the shared pool, not an individual box.
38
- See [Capacity limits](../reference/capacity-limits.mdx) for capacity calculations.
41
+ `filesystemScope`; disk values describe the shared pool, or the box's own disk
42
+ when it has one. See [Box sizing and capacity](../reference/capacity.mdx) for the
43
+ advisory capacity fields.
39
44
 
40
45
  These examples require `jq` on the controller:
41
46
 
42
47
  ```sh title="CONTROLLER: Parse records without mixing stderr"
43
48
  raft list --location lab | jq -r 'select(.state == "Running") | .box'
44
- raft limits --location lab --json | jq '{location, savedSlots, warnings}'
49
+ raft capacity --location lab --json | jq '{location, headroom, warnings}'
45
50
  raft prune --location lab --older-than 7 | jq -r '.box'
46
51
  ```
47
52
 
53
+ `raft new` fills omitted options from the controller's
54
+ `~/.config/raft/config.json`, so the same script can create different boxes on
55
+ different controllers. Pass `--location`, `--ttl`, `--cpu`, `--memory` and
56
+ `--disk` in CI.
57
+ Hosts can also require box note fields; give `--purpose`, `--owner` and `--next`
58
+ so a policy cannot fail the job. See [Defaults and note policies](./config-policy.mdx).
59
+
48
60
  Do not redirect `2>&1` into machine-readable output. Recovery reports its allocated
49
61
  handle on stderr before import finishes; only a successful completion prints the
50
- final handle on stdout.
62
+ final handle on stdout. Once a day Raft may also print a release notice on
63
+ stderr. Set `RAFT_NO_UPDATE_CHECK=1` in jobs that pin a CLI version or compare
64
+ stderr.
51
65
 
52
66
  ## Exit codes and job results
53
67
 
@@ -57,6 +71,7 @@ final handle on stdout.
57
71
  | Synchronous `exec` | Transport/command return code; preserves payload exit status when transport succeeds |
58
72
  | Recognized runtime/configuration failure | `1`, with `raft: ...` on stderr |
59
73
  | Argument parsing failure | `2`, with argparse diagnostics on stderr |
74
+ | `self-update --check` finds a newer release | `100` |
60
75
  | Caught keyboard interruption | `130` |
61
76
 
62
77
  SSH transport failures may return SSH's status rather than the payload's result.
@@ -72,7 +87,7 @@ running job's initial `ExecMainStatus=0` as completion. See
72
87
  ready. Probe a harmless command before running setup. This Bash example waits up
73
88
  to about a minute plus transport time, executes an uploaded project check, and
74
89
  attempts to stop the persistent box on exit. It assumes `./my-project/check.sh`
75
- exists and the configured host has a free saved slot:
90
+ exists:
76
91
 
77
92
  ```bash title="CONTROLLER or trusted Linux CI runner"
78
93
  set -euo pipefail
@@ -109,10 +124,10 @@ raft upload "$box" ./my-project /workspace/my-project --recursive
109
124
  raft exec "$box" -- bash -lc 'cd /workspace/my-project && bash ./check.sh'
110
125
  ```
111
126
 
112
- The stopped box remains available for inspection and still consumes a saved slot.
127
+ The stopped box remains available for inspection and still uses disk space.
113
128
  Destroy it after collecting needed files, or preview old stopped boxes with prune.
114
129
  For disposable CI boxes, deletion on stop/expiry removes artifacts and snapshots;
115
- download results before cleanup. A new box or a retry still consumes a saved slot.
130
+ download results before cleanup. A new box or a retry uses its own disk space.
116
131
 
117
132
  Command readiness does not mean Docker or a web application is ready. Probe the
118
133
  specific dependency (`docker info`, an HTTP health endpoint, or systemd status)
@@ -75,7 +75,7 @@ echo "Created fork: $copy"
75
75
  - **Instance-only copy**: Uses Incus `--instance-only`. Filesystem data, installed packages, and `/workspace` files are copied; source snapshots are not inherited.
76
76
  - **Immediate start**: `raft fork` launches the new container immediately with the specified TTL while the source remains stopped.
77
77
  - **Fresh network identity**: The fork receives a new container name, a fresh MAC address on `rfbr0`, and an independent IP address.
78
- - **Capacity accounting**: The fork counts toward the location's four-saved-box limit and uses storage from the shared 60 GiB Btrfs pool.
78
+ - **Disk**: The fork keeps the source disk. Both kinds fork in seconds as copy-on-write snapshots: a shared-pool source within the shared 60 GiB Btrfs pool, an own-disk source as a thin snapshot that reserves its full size in `raft-disks`.
79
79
 
80
80
  Destroy the clone when finished to release capacity:
81
81
 
@@ -89,7 +89,13 @@ raft destroy "$copy"
89
89
  copy=$(raft new --from "$box/pre-upgrade" --ttl 600)
90
90
  ```
91
91
 
92
- The source box can be running because Raft copies the saved snapshot. The new box stays on the same host, uses normal creation sizing defaults and has a fresh network identity. Filesystem secrets are copied too.
92
+ The source box can be running because Raft copies the saved snapshot. The new box stays on the same host, keeps the snapshot's CPU, RAM and disk unless `--cpu`, `--memory` or `--disk` overrides them, and has a fresh network identity. Copies are copy-on-write snapshots, so starting many boxes from one prepared snapshot is fast; on own disks each copy still reserves its full size. Filesystem secrets are copied too.
93
+
94
+ Add `--disk SIZE` to copy the snapshot onto an own disk of a new size, for example to give a full box more room:
95
+
96
+ ```sh
97
+ bigger=$(raft new --from "$box/pre-upgrade" --disk 40GiB --ttl 600)
98
+ ```
93
99
 
94
100
  Remove a snapshot you no longer need:
95
101
 
@@ -104,4 +110,4 @@ copy=$(raft fork "$box" --ttl 600 --cpu 2 --memory 4GiB)
104
110
  raft resume "$box" --ttl 600 --memory 2GiB
105
111
  ```
106
112
 
107
- Omitted sizing values keep the source limits. Forks are persistent by default; add `--disposable` to delete a clone when it stops or expires.
113
+ Omitted CPU and RAM values keep the source limits; the disk never changes on fork or resume. Forks are persistent by default; add `--disposable` to delete a clone when it stops or expires.
@@ -22,11 +22,11 @@ raft doctor
22
22
  - Compatibility of the installed expiry worker with the CLI
23
23
  - Active status of host systemd services (`incus`, `raft-expire.timer`, `raft-network.service`)
24
24
  - Host root filesystem disk usage (`df -h /`)
25
- - Status and usage of the `raft-data` Btrfs pool
25
+ - Status and usage of the shared `raft-data` Btrfs pool, and of the `raft-disks` LVM thin pool when own box disks are enabled (otherwise `own box disks: not enabled (redeploy with --disk-pool SIZE)`)
26
26
  - Count of existing saved containers
27
27
 
28
28
  <Callout type="info" title="Doctor scope">
29
- `raft doctor` reports runtime daemon, expiry-worker compatibility, and storage status. It does not validate image fingerprints, inspect nftables bridge rules, enforce admission caps, or emit 5 GiB low-disk warnings.
29
+ `raft doctor` reports runtime daemon, expiry-worker compatibility, and storage status. It does not validate image fingerprints, inspect nftables bridge rules, or emit 5 GiB low-disk warnings. A host without `raft-disks` is healthy: own box disks are opt-in.
30
30
  </Callout>
31
31
 
32
32
  ### Expiry worker differs from the CLI
@@ -120,13 +120,25 @@ The configuration file contains valid JSON that is `{}` or is not an object (an
120
120
  #### "Build an image and configure its full immutable fingerprint before creating boxes"
121
121
  The `image` field is missing or is not a 64-character hexadecimal SHA-256 fingerprint. Supply the full fingerprint from `sudo incus --project raft image list raft-dev --format json`.
122
122
 
123
- ### Admission and capacity errors
123
+ ### Sizing and disk errors
124
124
 
125
- #### "Location already holds four boxes"
126
- Raft enforces a hard limit of four saved boxes per location. Both running and stopped containers count toward this limit. Destroy unused boxes with `raft destroy <box>` before creating, forking, or recovering a box.
125
+ Raft has no box count limit and no size budget. These errors come from the
126
+ request itself or from the host:
127
+
128
+ #### "Requested N CPUs, but this host has M" or "Requested SIZE RAM, but this host has N GiB"
129
+ The request is larger than the whole host. Ask for at most the host's CPUs and RAM; `raft capacity` shows both.
130
+
131
+ #### "LOCATION has no raft-disks pool, so own box disks are not enabled there"
132
+ Own box disks are opt-in and this host was deployed without them. Nothing was created. Enable them with `python3 deploy/deploy.py --location <location> --disk-pool SIZE`, which needs SIZE plus 20 GiB free on the host, or create the box without `--disk`. Boxes without `--disk` keep working meanwhile.
133
+
134
+ #### "own box disks would need N GiB more of raft-disks"
135
+ Each own disk, each snapshot of one, and the cached image reserve their full size in `raft-disks`, so Raft refused the request before creating anything. Destroy own-disk boxes or snapshots you no longer need, choose a smaller `--disk`, or use the shared pool. `raft capacity` shows the unreserved space. See [Reservation accounting](../reference/capacity.mdx#reservation-accounting).
136
+
137
+ #### "No space left on device" inside an own-disk box
138
+ The box filled its own disk. Delete files or Docker data inside the box. For a persistent box you can also move to a larger disk: stop it with `raft stop "$box"` (snapshots need a stopped box), take `raft snapshot "$box" <snapshot>`, then create the copy with `raft new --from "$box/<snapshot>" --disk <larger size>`. Do not stop a `--disposable` box for this: stopping deletes it.
127
139
 
128
140
  #### Low disk warnings
129
- `raft limits` warns when free space on host disk or the `raft-data` pool falls below 5 GiB. This warning is diagnostic and does not halt container operations. Clean up dangling images, old snapshots, or unused containers to free space.
141
+ `raft capacity` warns when free space on host disk or the `raft-data` pool falls below 5 GiB. This warning is diagnostic and does not halt container operations. Clean up dangling images, old snapshots, or unused containers to free space.
130
142
 
131
143
  ### Network and forwarding issues
132
144
 
@@ -10,7 +10,7 @@ parts of Raft. Updating one does not automatically update the others.
10
10
 
11
11
  | Component | Update method | Effect on existing boxes |
12
12
  | --- | --- | --- |
13
- | Controller CLI | Install the chosen npm or Python release | Changes controller commands; does not rewrite guest files |
13
+ | Controller CLI | `raft self-update`, or install the chosen npm or Python release | Changes controller commands; does not rewrite guest files |
14
14
  | Host worker and firewall | Run the chosen release's deployer per host | Replaces host policy code and reapplies filtering |
15
15
  | Pinned development image | Import an image and change the configuration fingerprint | Used for future image-based `new`; existing boxes keep their files |
16
16
 
@@ -33,7 +33,14 @@ stopping one. Schedule redeployment when interruptions are acceptable.
33
33
 
34
34
  ## Update the controller
35
35
 
36
- For an npm installation:
36
+ `raft self-update` installs the latest release through the channel that
37
+ installed the CLI, and `raft self-update --version 0.2.0` installs a chosen
38
+ one. `raft self-update --check` only reports. For npm installs, npm checks the
39
+ registry integrity hash of a package published with provenance. For uv tools,
40
+ Raft downloads the release wheel and refuses it unless its SHA-256 matches the
41
+ release `SHA256SUMS`. See [`self-update`](../reference/cli.md#self-update).
42
+
43
+ To install a release by hand instead, for an npm installation:
37
44
 
38
45
  ```sh title="CONTROLLER: Install a pinned CLI release"
39
46
  npm install -g @microck/raft@0.2.0
@@ -54,7 +61,10 @@ and check which executable your shell uses with `command -v raft`.
54
61
 
55
62
  For an editable source installation, updating the checkout changes the installed
56
63
  tool's source. Keep uncommitted work separate and run `uv sync --locked` for the
57
- chosen checkout. Raft has no `self-update` command.
64
+ chosen checkout. `self-update` refuses source checkouts.
65
+
66
+ Updating the CLI changes no host. Continue below, and run `raft doctor` first: it
67
+ reports each host whose worker differs from the new CLI.
58
68
 
59
69
  ## Redeploy each host
60
70
 
package/docs/index.mdx CHANGED
@@ -33,6 +33,7 @@ Stopping a workspace preserves files and packages on disk. Resuming resets its r
33
33
  | Run detached commands with systemd logs | [Background jobs](./guides/background-jobs.mdx) |
34
34
  | Manage deadlines and restart services after resume | [Lifetime and persistent services](./guides/long-running-services.mdx) |
35
35
  | Automate the CLI and parse its output | [Scripting and CI](./guides/scripting-ci.mdx) |
36
+ | Set defaults for new boxes or require box note fields | [Defaults and note policies](./guides/config-policy.mdx) |
36
37
  | Forward guest ports or open noVNC desktop | [Desktop and private services](./guides/desktop-services.mdx) |
37
38
  | Capture disk state or clone workspaces | [Snapshots and forks](./guides/snapshots-forks.mdx) |
38
39
  | Export portable backups across hosts | [Backup and recovery](./guides/backup-recovery.mdx) |
@@ -15,7 +15,7 @@ Install the CLI from npm on your Linux controller, confirm key-based SSH to the
15
15
  host, then let `raft host add` do the remaining steps:
16
16
 
17
17
  ```sh title="CONTROLLER: Onboard a host"
18
- npm install -g @microck/raft@0.3.0
18
+ npm install -g @microck/raft@0.5.0
19
19
  ssh ubuntu@192.0.2.10 true
20
20
  raft host add lab --ssh ubuntu@192.0.2.10 --dry-run
21
21
  raft host add lab --ssh ubuntu@192.0.2.10
@@ -44,7 +44,7 @@ Connect to your host over SSH, verify its host key fingerprint, and install base
44
44
  ```sh title="HOST: Install baseline system packages"
45
45
  sudo apt-get update
46
46
  sudo apt-get install -y --no-install-recommends \
47
- ca-certificates curl gnupg python3 openssh-server iptables btrfs-progs
47
+ ca-certificates curl gnupg python3 openssh-server iptables btrfs-progs lvm2 python3-yaml
48
48
  ```
49
49
 
50
50
  Verify passwordless `sudo` on the host:
@@ -62,7 +62,7 @@ sudo -n true && echo "Passwordless sudo is functional"
62
62
  On your Linux controller with Python 3.11.8+, install the published CLI:
63
63
 
64
64
  ```sh title="CONTROLLER: Install published CLI"
65
- npm install -g @microck/raft@0.3.0
65
+ npm install -g @microck/raft@0.5.0
66
66
  raft --version
67
67
  ```
68
68
 
@@ -70,7 +70,7 @@ Installation does not configure hosts or download images. Clone the matching
70
70
  release to obtain the deployment scripts and example configuration:
71
71
 
72
72
  ```sh title="CONTROLLER: Obtain deployment scripts"
73
- git clone --branch v0.3.0 https://github.com/Microck/raft.git raft
73
+ git clone --branch v0.5.0 https://github.com/Microck/raft.git raft
74
74
  cd raft
75
75
  ```
76
76
 
@@ -132,15 +132,17 @@ Run the automated host deployer from your controller:
132
132
 
133
133
  ```sh title="CONTROLLER: Provision host infrastructure"
134
134
  python3 deploy/deploy.py --location lab
135
+ # Optional: enable own box disks with a pool of this size, now or on any later redeploy
136
+ python3 deploy/deploy.py --location lab --disk-pool 100GiB
135
137
  ```
136
138
 
137
139
  <details>
138
140
  <summary>Infrastructure created by the deployer</summary>
139
141
 
140
142
  The deployer runs over SSH and configures the host:
141
- 1. Installs Incus 6.0 LTS and nftables packages.
143
+ 1. Installs Incus 6.0 LTS, LVM, nftables and python3-yaml packages.
142
144
  2. Creates the isolated `raft` Incus project.
143
- 3. Creates a 60 GiB Btrfs storage pool (`raft-data`).
145
+ 3. Creates the shared 60 GiB Btrfs storage pool (`raft-data`). With `--disk-pool SIZE`, also creates the LVM thin pool for [own box disks](./reference/capacity.mdx#own-box-disks) (`raft-disks`) of SIZE, allocates its file on the host disk in full and stops it passing discards to that file. Without `--disk-pool` own box disks stay disabled, which is a healthy state. A later redeploy with `--disk-pool` adds the pool; no redeploy resizes either pool.
144
146
  4. Creates private bridge interface `rfbr0` (`10.232.0.1/24`) with NAT.
145
147
  5. Configures nftables bridge filters to block inter-workspace traffic and restrict host access.
146
148
  6. Installs and enables `raft-network.service` and `raft-expire.timer` units.
@@ -159,16 +161,16 @@ Workspaces require an unprivileged Debian 13 development image matching the host
159
161
 
160
162
  <Tab value="Download release image">
161
163
 
162
- Download the release archive, checksum, and manifest from [Raft releases](https://github.com/Microck/raft/releases). These examples pin `v0.3.0`, matching the CLI and deployment checkout above. The `sha256sum` check gates import so corrupted archives are rejected:
164
+ Download the release archive, checksum, and manifest from [Raft releases](https://github.com/Microck/raft/releases). These examples pin `v0.5.0`, matching the CLI and deployment checkout above. The `sha256sum` check gates import so corrupted archives are rejected:
163
165
 
164
166
  <Tabs items={["ARM64", "AMD64"]}>
165
167
 
166
168
  <Tab value="ARM64">
167
169
 
168
170
  ```sh title="HOST (ARM64): Download, verify, and import release image"
169
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.3.0/raft-dev-arm64.tar.gz
170
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.3.0/SHA256SUMS
171
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.3.0/manifest-arm64.json
171
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.5.0/raft-dev-arm64.tar.gz
172
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.5.0/SHA256SUMS
173
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.5.0/manifest-arm64.json
172
174
 
173
175
  sha256sum --ignore-missing -c SHA256SUMS && \
174
176
  sudo incus --project raft image import ./raft-dev-arm64.tar.gz --alias raft-dev
@@ -179,9 +181,9 @@ sha256sum --ignore-missing -c SHA256SUMS && \
179
181
  <Tab value="AMD64">
180
182
 
181
183
  ```sh title="HOST (AMD64): Download, verify, and import release image"
182
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.3.0/raft-dev-amd64.tar.gz
183
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.3.0/SHA256SUMS
184
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.3.0/manifest-amd64.json
184
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.5.0/raft-dev-amd64.tar.gz
185
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.5.0/SHA256SUMS
186
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.5.0/manifest-amd64.json
185
187
 
186
188
  sha256sum --ignore-missing -c SHA256SUMS && \
187
189
  sudo incus --project raft image import ./raft-dev-amd64.tar.gz --alias raft-dev
@@ -260,4 +262,4 @@ Check that Incus, the network service, and the expiration timer are active. For
260
262
 
261
263
  Proceed to [First working box](./first-box.mdx) to launch, verify, and manage your first workspace.
262
264
 
263
- For later releases, follow [Upgrades and image changes](./guides/upgrades.mdx).
265
+ For later releases, run `raft self-update` and follow [Upgrades and image changes](./guides/upgrades.mdx).