@microck/raft 0.2.1 → 0.4.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.
package/docs/contract.md CHANGED
@@ -23,6 +23,7 @@ Raft manages persistent unprivileged native-architecture Incus system containers
23
23
  - **Strict infrastructure validation**: Deployment refuses conflicting storage or network names and incomplete infrastructure rather than overwriting or repairing state.
24
24
  - **Storage pool sizing**: Existing owned Btrfs pools must retain the configured 60 GiB size; drift is rejected without automatic resizing.
25
25
  - **Firewall precedence**: Redeployment reapplies firewall rules. Incus startup requires successful firewall application before the daemon starts, including socket activation.
26
+ - **Guided onboarding**: `raft host add LOCATION --ssh TARGET` probes the target read-only and refuses unsupported hosts, including macOS, before any change. It prints the exact plan and applies it only after an interactive yes or `--yes`; `--dry-run` stops after the plan. It runs the same deployer as `deploy/deploy.py`, imports an image only after its SHA-256 matches (release `SHA256SUMS` or the local archive digest) and its architecture matches the host, then registers the location with the image fingerprint. It finishes with `doctor` and a disposable create/exec/destroy smoke test; if either fails, it removes the new location from the configuration and leaves host changes for a rerun. It uses existing key-based SSH and never collects passwords or changes SSH, other firewall rules or IP configuration. It requires the deployment files from the npm package or a source checkout.
26
27
  - **CLI coverage**: The single-operator workflow spans lifecycle, execution, files, snapshots, jobs, tunnels, backup, and capacity inspection; see the [command reference](./reference/cli.md).
27
28
 
28
29
  ## Configuration and lifecycle defaults
@@ -32,13 +33,17 @@ Raft manages persistent unprivileged native-architecture Incus system containers
32
33
  | Host identification | Configuration requires named SSH targets using letters, digits, underscores, or hyphens. |
33
34
  | Qualified handles | Every handle includes location and instance name (`<location>:<name>`). |
34
35
  | Image pinning | Workspace creation requires the immutable 64-hex image fingerprint, not an updating alias. |
35
- | Lifecycle defaults | Defaults to first configured location, 1 CPU, 2 GiB RAM, and 600-second lifetime. Supported choices are 1 or 2 CPUs, 1/2/4 GiB RAM, and 60 to 2,592,000 seconds (30 days) TTL. |
36
+ | Lifecycle defaults | Defaults to first configured location, 1 CPU, 2 GiB RAM, and 600-second lifetime. Supported choices are 1 or 2 CPUs, 1/2/4/8 GiB RAM, and 60 to 2,592,000 seconds (30 days) TTL. |
36
37
  | Admission limit | Hard ceiling of four saved boxes per host, including stopped boxes. |
37
38
  | TTL calculation | Running TTL begins after launch completes, using the host clock. Cold image preparation does not consume TTL. |
38
39
  | Host locking | Creation, lifecycle transitions, and expiry are serialized by host file lock (`/run/lock/raft-incus.lock`). Noninteractive transactions close controller stdin so commands cannot wait for piped input while holding this lock. Exec, SSH, and file-transfer paths remain unchanged. Piped input to `raft exec` is unsupported. Native Incus admin calls bypass CLI policy. |
39
40
 
40
41
  ## Capacity and admission inspection
41
42
 
43
+ `raft locations` lists configured locations as JSONL with `location`, `default` and `note` fields without contacting hosts. `raft note LOCATION TEXT` and `raft note LOCATION --clear` edit the optional `note` field in the controller configuration atomically, preserving location order and mode `0600`. Notes are advisory guidance; Raft shows them in `locations`, `limits` and on stderr from `new`, and never enforces them.
44
+
45
+ Boxes accept an optional note with a fixed template: `purpose` (required), `owner` and `next`, all nonempty strings. `raft new --purpose TEXT [--owner TEXT] [--next TEXT]` and `raft note BOX ...` store it as JSON in the box's `user.raft.note` Incus key; `raft note BOX --clear` removes it. Each edit replaces the whole note. `raft list` reports it as `note`, or `null`. `new` always writes the key, so `new --from` never inherits a snapshot's note; forks and recovered backups keep it. Box notes are advisory and never change lifecycle policy.
46
+
42
47
  `raft limits [--location NAME] [--json]` inspects hosts without modifying state. It reports effective CPUs, total and available RAM, saved and active box counts, active resource limits, and disk space.
43
48
 
44
49
  - **Reserve calculations**: Recommends running boxes after reserving 1 CPU and the larger of 2 GiB or 10% host RAM.
@@ -19,10 +19,11 @@ 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`, `note` |
23
23
  | `info` | One pretty-printed native Incus metadata JSON object |
24
24
  | `usage` | One JSON object containing running resource counters |
25
25
  | `limits --json` | One JSON object per location (JSONL across multiple hosts) |
26
+ | `locations` | One JSON object per configured location (JSONL) |
26
27
  | `prune` | JSONL candidates or deleted records with `box`, `action` |
27
28
  | `status` | systemd `ActiveState`, `SubState`, `ExecMainStatus` property lines |
28
29
  | `logs` | Guest journal text |
@@ -30,7 +31,8 @@ Raft does not have a global `--json` option. Parse each command's actual format:
30
31
  | Other inspection/maintenance commands | Human-readable text or no output |
31
32
 
32
33
  `list` emits nothing when there are no boxes. `stopAt` is the host Unix deadline
33
- as a string, or an empty string when absent. `info` uses native Incus keys rather
34
+ as a string, or an empty string when absent. `note` is the box note object with
35
+ `purpose` and optional `owner` and `next`, or `null`. `info` uses native Incus keys rather
34
36
  than a normalized Raft schema. `usage` includes `memoryBytes`, `memoryLimit`,
35
37
  `cpuTime`, `effectiveCpus`, `filesystemUsedBytes`, `filesystemAvailableBytes`, and
36
38
  `filesystemScope`; disk values describe the shared pool, not an individual box.
@@ -9,6 +9,30 @@ Set up a dedicated Ubuntu host, install the Raft CLI, provision Incus, acquire a
9
9
 
10
10
  Review prerequisites in [System requirements](./requirements.mdx) before starting.
11
11
 
12
+ ## Guided onboarding
13
+
14
+ Install the CLI from npm on your Linux controller, confirm key-based SSH to the
15
+ host, then let `raft host add` do the remaining steps:
16
+
17
+ ```sh title="CONTROLLER: Onboard a host"
18
+ npm install -g @microck/raft@0.4.0
19
+ ssh ubuntu@192.0.2.10 true
20
+ raft host add lab --ssh ubuntu@192.0.2.10 --dry-run
21
+ raft host add lab --ssh ubuntu@192.0.2.10
22
+ ```
23
+
24
+ The dry run checks the host without changing it and prints the plan. The second
25
+ command shows the same plan, asks for confirmation, provisions Incus, imports the
26
+ checksum-verified release image, adds `lab` to `~/.config/raft/incus.json`, and
27
+ finishes with `raft doctor` and a disposable smoke test. Unsupported hosts,
28
+ including macOS, are rejected before any change; run an Ubuntu VM and onboard
29
+ that instead. See [`host add`](./reference/cli.md#host-add) for image options.
30
+
31
+ ## Manual setup
32
+
33
+ The steps below are what `raft host add` automates. Follow them to inspect or
34
+ customize each stage.
35
+
12
36
  <Steps>
13
37
 
14
38
  <Step>
@@ -38,7 +62,7 @@ sudo -n true && echo "Passwordless sudo is functional"
38
62
  On your Linux controller with Python 3.11.8+, install the published CLI:
39
63
 
40
64
  ```sh title="CONTROLLER: Install published CLI"
41
- npm install -g @microck/raft@0.2.0
65
+ npm install -g @microck/raft@0.4.0
42
66
  raft --version
43
67
  ```
44
68
 
@@ -46,7 +70,7 @@ Installation does not configure hosts or download images. Clone the matching
46
70
  release to obtain the deployment scripts and example configuration:
47
71
 
48
72
  ```sh title="CONTROLLER: Obtain deployment scripts"
49
- git clone --branch v0.2.0 https://github.com/Microck/raft.git raft
73
+ git clone --branch v0.4.0 https://github.com/Microck/raft.git raft
50
74
  cd raft
51
75
  ```
52
76
 
@@ -135,16 +159,16 @@ Workspaces require an unprivileged Debian 13 development image matching the host
135
159
 
136
160
  <Tab value="Download release image">
137
161
 
138
- Download the release archive, checksum, and manifest from [Raft releases](https://github.com/Microck/raft/releases). These examples pin `v0.2.0`, matching the CLI and deployment checkout above. The `sha256sum` check gates import so corrupted archives are rejected:
162
+ Download the release archive, checksum, and manifest from [Raft releases](https://github.com/Microck/raft/releases). These examples pin `v0.4.0`, matching the CLI and deployment checkout above. The `sha256sum` check gates import so corrupted archives are rejected:
139
163
 
140
164
  <Tabs items={["ARM64", "AMD64"]}>
141
165
 
142
166
  <Tab value="ARM64">
143
167
 
144
168
  ```sh title="HOST (ARM64): Download, verify, and import release image"
145
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/raft-dev-arm64.tar.gz
146
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/SHA256SUMS
147
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/manifest-arm64.json
169
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.4.0/raft-dev-arm64.tar.gz
170
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.4.0/SHA256SUMS
171
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.4.0/manifest-arm64.json
148
172
 
149
173
  sha256sum --ignore-missing -c SHA256SUMS && \
150
174
  sudo incus --project raft image import ./raft-dev-arm64.tar.gz --alias raft-dev
@@ -155,9 +179,9 @@ sha256sum --ignore-missing -c SHA256SUMS && \
155
179
  <Tab value="AMD64">
156
180
 
157
181
  ```sh title="HOST (AMD64): Download, verify, and import release image"
158
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/raft-dev-amd64.tar.gz
159
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/SHA256SUMS
160
- curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/manifest-amd64.json
182
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.4.0/raft-dev-amd64.tar.gz
183
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.4.0/SHA256SUMS
184
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.4.0/manifest-amd64.json
161
185
 
162
186
  sha256sum --ignore-missing -c SHA256SUMS && \
163
187
  sudo incus --project raft image import ./raft-dev-amd64.tar.gz --alias raft-dev
@@ -18,7 +18,7 @@ raft limits
18
18
  raft limits --location lab --json
19
19
  ```
20
20
 
21
- The output displays host CPU count, total RAM, configured limits, available RAM, disk headroom, and advisory recommendations across all six sizing profiles:
21
+ The output displays host CPU count, total RAM, configured limits, available RAM, disk headroom, and advisory recommendations across all eight sizing profiles:
22
22
 
23
23
  ### Illustrative host sizing
24
24
 
@@ -48,7 +48,7 @@ Operating systems report less usable RAM than advertised hardware size. Near siz
48
48
  Select CPU and memory limits when launching with `raft new`:
49
49
 
50
50
  - `--cpu`: `1` or `2` shared vCPUs managed by Incus (default: `1`).
51
- - `--memory`: `1GiB`, `2GiB`, or `4GiB` cgroup limit (default: `2GiB`).
51
+ - `--memory`: `1GiB`, `2GiB`, `4GiB`, or `8GiB` cgroup limit (default: `2GiB`). An 8 GiB box needs at least 10 GiB of usable host RAM before `raft limits` recommends running one; like other recommendations, this is advisory and not enforced.
52
52
 
53
53
  Change supported CPU/RAM limits when resuming a stopped box or creating a fork:
54
54
 
@@ -27,6 +27,7 @@ Workspaces are referenced by location-qualified handles formatted as `<location>
27
27
  | Snapshots and cloning | [`snapshot`](#snapshot), [`snapshots`](#snapshots), [`restore`](#restore), [`snapshot-delete`](#snapshot-delete), [`fork`](#fork) |
28
28
  | Port forwarding and desktop | [`forward`](#forward), [`desktop`](#desktop) |
29
29
  | Backup and recovery | [`backup`](#backup), [`recover`](#recover) |
30
+ | Locations | [`host add`](#host-add), [`locations`](#locations), [`note`](#note) |
30
31
  | Host inspection and maintenance | [`limits`](#limits), [`doctor`](#doctor), [`list`](#list), [`gc`](#gc), [`prune`](#prune) |
31
32
 
32
33
  ## Workspace lifecycle commands
@@ -35,17 +36,19 @@ Workspaces are referenced by location-qualified handles formatted as `<location>
35
36
 
36
37
  Create and launch a new workspace container.
37
38
 
38
- `raft new [--location LOCATION] [--ttl TTL] [--cpu {1,2}] [--memory {1GiB,2GiB,4GiB}] [--disposable] [--from LOCATION:BOX/SNAPSHOT]`
39
+ `raft new [--location LOCATION] [--ttl TTL] [--cpu {1,2}] [--memory {1GiB,2GiB,4GiB,8GiB}] [--disposable] [--from LOCATION:BOX/SNAPSHOT] [--purpose TEXT [--owner TEXT] [--next TEXT]]`
39
40
 
40
41
  - `--location LOCATION`: Target host location in `~/.config/raft/incus.json`. Defaults to first configured location.
41
42
  - `--ttl TTL`: Lifetime in seconds before automatic stop (`60` to `2592000`). Default: `600`.
42
43
  - `--cpu {1,2}`: CPU core count managed by Incus. Default: `1`.
43
- - `--memory {1GiB,2GiB,4GiB}`: Memory limit. Default: `2GiB`.
44
+ - `--memory {1GiB,2GiB,4GiB,8GiB}`: Memory limit. Default: `2GiB`.
44
45
 
45
46
  - `--disposable`: Delete the box and snapshots on stop or expiry. Off by default.
46
47
  - `--from LOCATION:BOX/SNAPSHOT`: Copy a named snapshot on its source host instead of the configured image. Normal creation sizing applies; the source can be running. Cannot target a different location.
48
+ - `--purpose TEXT`, `--owner TEXT`, `--next TEXT`: Record a [box note](#note). `--owner` and `--next` need `--purpose`. Without them the box starts with no note, including boxes created `--from` a noted snapshot.
47
49
 
48
- Returns the qualified workspace handle.
50
+ Returns the qualified workspace handle. If the location has a note, `new` prints
51
+ it to stderr; stdout still contains only the handle.
49
52
 
50
53
  ### `stop`
51
54
 
@@ -59,7 +62,7 @@ Terminates guest processes and stops the container. Persistent boxes retain file
59
62
 
60
63
  Start a stopped workspace with a renewed lifetime deadline.
61
64
 
62
- `raft resume <box> --ttl TTL [--cpu {1,2}] [--memory {1GiB,2GiB,4GiB}]`
65
+ `raft resume <box> --ttl TTL [--cpu {1,2}] [--memory {1GiB,2GiB,4GiB,8GiB}]`
63
66
 
64
67
  - `--ttl TTL`: Lifetime in seconds from resumption (`60` to `2592000`). Required.
65
68
 
@@ -194,7 +197,7 @@ Roll back a stopped workspace filesystem to a named snapshot using `--diskonly`,
194
197
 
195
198
  Clone a stopped workspace into an independent container on the same host and start it.
196
199
 
197
- `raft fork <box> --ttl TTL [--cpu {1,2}] [--memory {1GiB,2GiB,4GiB}] [--disposable]`
200
+ `raft fork <box> --ttl TTL [--cpu {1,2}] [--memory {1GiB,2GiB,4GiB,8GiB}] [--disposable]`
198
201
 
199
202
  - `--ttl TTL`: Lifetime in seconds for the clone. Required.
200
203
 
@@ -244,6 +247,78 @@ Import a portable backup archive onto a host as a fresh stopped workspace.
244
247
 
245
248
  Returns the qualified handle of the imported stopped workspace.
246
249
 
250
+ ## Location commands
251
+
252
+ ### `host add`
253
+
254
+ Check, provision and register a new workspace host, then prove it works.
255
+
256
+ `raft host add LOCATION --ssh TARGET [--image-release TAG | --image-archive PATH | --build] [--dry-run] [--yes]`
257
+
258
+ - `LOCATION`: New location name. Existing locations are rejected.
259
+ - `--ssh TARGET`: Key-based SSH target, such as `ubuntu@192.0.2.10` or an `~/.ssh/config` alias. Accept the host key interactively first.
260
+ - `--image-release TAG`: Download `raft-dev-<arch>.tar.gz` from this release on the host and verify it against the release `SHA256SUMS`. Default: the CLI's own version.
261
+ - `--image-archive PATH`: Upload and import a local image archive instead.
262
+ - `--build`: Build the image on the host from Debian 13 instead.
263
+ - `--dry-run`: Probe the host and print the plan without changing anything.
264
+ - `--yes`: Apply the plan without the interactive prompt. Without a terminal, `--yes` is required.
265
+
266
+ Before any change, a read-only probe checks SSH, passwordless sudo, Ubuntu 22.04 or
267
+ 24.04, ARM64 or AMD64, python3, at least 2 CPUs, 8 GiB RAM and, for a fresh host,
268
+ 80 GiB free disk. Unsupported targets, including macOS, fail with the reasons and
269
+ no changes. The command then runs the deployer, imports the image only if its
270
+ checksum and architecture match, adds the location with the pinned fingerprint,
271
+ and runs `doctor` plus a disposable create/exec/destroy smoke test. If the final
272
+ checks fail, it removes the location again; host changes remain and a rerun
273
+ reuses them.
274
+
275
+ ```sh title="CONTROLLER: Onboard a host"
276
+ raft host add lab --ssh ubuntu@192.0.2.10 --dry-run
277
+ raft host add lab --ssh ubuntu@192.0.2.10
278
+ ```
279
+
280
+ The deployment files ship in the npm package and source checkouts. The Python
281
+ wheel contains only the CLI; use npm or a checkout to onboard hosts.
282
+
283
+ ### `locations`
284
+
285
+ List configured locations without contacting hosts.
286
+
287
+ `raft locations`
288
+
289
+ Prints one JSON object per location (JSONL) in configuration order, with
290
+ `location`, `default` and `note` fields. `default` is true for the location that
291
+ `new` uses without `--location`. `note` is empty when none is set.
292
+
293
+ ### `note`
294
+
295
+ Set or clear the note of a location or a box.
296
+
297
+ `raft note LOCATION TEXT`
298
+ `raft note LOCATION --clear`
299
+ `raft note BOX --purpose TEXT [--owner TEXT] [--next TEXT]`
300
+ `raft note BOX --clear`
301
+
302
+ Location notes are operator guidance stored in `~/.config/raft/incus.json`. Use
303
+ them to record which host is preferred or what a host should not run. Raft shows
304
+ them in `locations`, `limits` and `new`.
305
+
306
+ Box notes tell other agents and people why a box exists. They follow one
307
+ template, stored on the box as JSON in the Incus key `user.raft.note`:
308
+
309
+ | Field | Meaning | Example |
310
+ | --- | --- | --- |
311
+ | `purpose` (required) | What the box is for | `Integration tests for PR 42` |
312
+ | `owner` | Who uses it | `codex session in ~/work/app` |
313
+ | `next` | What happens to it next | `Destroy after the tests pass` |
314
+
315
+ Each call replaces the whole note, on running or stopped boxes. `raft list`
316
+ shows it as `note`. Forks and recovered backups keep the source box's note;
317
+ update it if the copy has a different job.
318
+
319
+ Notes are advisory; Raft never enforces them. Empty values are rejected; use
320
+ `--clear` to remove a note.
321
+
247
322
  ## Host inspection and maintenance commands
248
323
 
249
324
  ### `limits`
@@ -255,6 +330,8 @@ Inspect host resource capacity, active allocations, and recommended workspace li
255
330
  - `--location LOCATION`: Filter report to a specific host.
256
331
  - `--json`: Output report as JSON.
257
332
 
333
+ Each report includes the location's `note`, if any.
334
+
258
335
  ### `doctor`
259
336
 
260
337
  Inspect host connectivity, Incus version, expiry-worker compatibility, active systemd units (`incus`, `raft-expire.timer`, `raft-network.service`), root filesystem usage, storage pool status, and saved box count across configured hosts. An incompatible worker returns a nonzero exit status and names the failing location without running expiry or modifying boxes.
@@ -267,7 +344,8 @@ List existing workspace containers across configured hosts.
267
344
 
268
345
  `raft list [--location LOCATION]`
269
346
 
270
- Prints one JSON object per box (JSONL), with `box`, `state`, and `stopAt` fields.
347
+ Prints one JSON object per box (JSONL), with `box`, `state`, `stopAt` and `note` fields.
348
+ `note` is the box note object, or `null` when the box has none.
271
349
  An empty inventory prints no records. See [Scripting and CI](../guides/scripting-ci.mdx)
272
350
  for output formats, exit codes, readiness, and failure handling.
273
351
 
@@ -27,7 +27,8 @@ The configuration file maps location names to host definition objects:
27
27
  {
28
28
  "lab": {
29
29
  "ssh": "ubuntu@192.0.2.10",
30
- "image": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
30
+ "image": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
31
+ "note": "Preferred for builds"
31
32
  },
32
33
  "remote-builder": {
33
34
  "ssh": "builder.example.com",
@@ -47,6 +48,19 @@ The configuration file maps location names to host definition objects:
47
48
  | :--- | :--- | :--- | :--- |
48
49
  | `ssh` | `string` | Yes | Non-empty SSH connection string (IP, `user@host`, or SSH host alias from `~/.ssh/config`). |
49
50
  | `image` | `string` | For `raft new` only | Full 64-character hexadecimal SHA-256 fingerprint (`^[a-f0-9]{64}$`). An empty string `""` is allowed during initial host provisioning. |
51
+ | `note` | `string` | No | Operator guidance for choosing this location, such as `Preferred for builds` or `Do not use for Docker`. Raft shows notes but never enforces them. |
52
+
53
+ Set or clear notes with `raft note` instead of editing the file by hand:
54
+
55
+ ```sh title="CONTROLLER: Record location guidance"
56
+ raft note lab "Preferred for builds"
57
+ raft note spare "Do not use for Docker builds"
58
+ raft locations
59
+ raft note spare --clear
60
+ ```
61
+
62
+ `raft note` rewrites the file with two-space indentation, keeps location order and
63
+ mode `0600`. `raft locations`, `raft limits` and `raft new` show the note.
50
64
 
51
65
  <Callout type="warn" title="Replace placeholder image fingerprint">
52
66
  The `image` field requires the full 64-character hash for `raft new`; aliases such as `raft-dev` are rejected. Replace the example hash with the actual imported image fingerprint for that host.
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Raft 0.3.0
3
+ ---
4
+
5
+ # Raft 0.3.0
6
+
7
+ - `raft host add LOCATION --ssh TARGET` onboards a host from the CLI. It checks the host read-only first and rejects unsupported targets such as macOS, shows the exact plan, and asks before changing anything (`--dry-run` previews, `--yes` skips the prompt). It provisions Incus, imports a checksum-verified release image (or `--image-archive` / `--build`), pins the fingerprint, and finishes with `doctor` and a disposable smoke test. A failed final check removes the new location again.
8
+ - Boxes can use 8 GiB RAM: `new`, `resume` and `fork` accept `--memory 8GiB`, and `raft limits` reports recommendations for it. An 8 GiB box needs at least 10 GiB of usable host RAM before `limits` recommends running one.
9
+ - Locations accept an optional `note` for operator guidance, such as which host is preferred or what a host should not run. `raft note LOCATION TEXT` sets it and `raft note LOCATION --clear` removes it.
10
+ - `raft locations` lists configured locations as JSONL with their default status and note, without contacting hosts.
11
+ - `raft limits` includes each location's note, and `raft new` prints the target location's note to stderr. Notes are advisory and never block allocation.
12
+
13
+ Install on Linux with Python 3.11.8+ using `npm install -g @microck/raft@0.3.0`, or install the attached Python wheel. Notes live in the controller configuration; host workers are unchanged and need no redeployment. Host onboarding needs the npm package or a source checkout because the Python wheel does not ship the deployment files.
14
+
15
+ ARM64 and AMD64 development images accompany this release. Verify `SHA256SUMS` before importing an image for your host architecture.
@@ -0,0 +1,15 @@
1
+ ---
2
+ title: Raft 0.4.0
3
+ ---
4
+
5
+ # Raft 0.4.0
6
+
7
+ - Boxes accept a note that says why they exist, so agents and people can tell which boxes are in use. The note follows one template: `purpose` (required), `owner` and `next`.
8
+ - `raft new --purpose TEXT [--owner TEXT] [--next TEXT]` notes a box when it is created. `raft note BOX --purpose ...` replaces a box's note and `raft note BOX --clear` removes it. `raft note LOCATION ...` still edits location notes.
9
+ - `raft list` adds a `note` field to each box: the note object, or `null`.
10
+ - Forks and recovered backups keep the source box's note. `new --from` starts without one.
11
+ - The `raft-cli` agent skill asks agents to note every box they create and to read notes before resuming, stopping, forking or destroying a box.
12
+
13
+ Install on Linux with Python 3.11.8+ using `npm install -g @microck/raft@0.4.0`, or install the attached Python wheel. Box notes live in each box's Incus configuration; host workers are unchanged and need no redeployment.
14
+
15
+ ARM64 and AMD64 development images accompany this release. Verify `SHA256SUMS` before importing an image for your host architecture.
@@ -19,6 +19,8 @@ Each workspace host must be a dedicated physical machine or virtual instance:
19
19
  | **RAM** | At least 8 GiB | Sizing calculations reserve the larger of 2 GiB or 10% of host RAM (an advisory estimate, not an enforced host reservation). |
20
20
  | **Free disk** | At least 80 GiB on host disk | The deployer provisions a 60 GiB dedicated Btrfs pool (`raft-data`). |
21
21
 
22
+ `raft host add` checks these requirements read-only before it changes a host.
23
+
22
24
  Host CPU and RAM reserves are advisory estimates for `raft limits`, not enforced cgroup locks.
23
25
 
24
26
  <Callout type="warn" title="Dedicated host requirement">
@@ -33,7 +33,7 @@ Storage accounting covers shared pools rather than per-box quotas. Large backup
33
33
  | Development image | Real developer-user tool checks, immutable selection and credential-path archive rejection |
34
34
  | Deployment/firewall | Repeat deployment removes stale rules without restarting Incus; failed filtering blocks normal and socket startup; boot timestamps prove filtering precedes Incus |
35
35
  | Storage contract | Disposable oversized pool is rejected without automatic resizing |
36
- | CLI options and errors | Help for all 28 commands; default location, TTL and desktop port; all six sizing profiles; global and per-location reports; invalid TTLs, sizes, required arguments, handles, paths, snapshot names, jobs and ports |
36
+ | CLI options and errors | Help for all 31 commands and `host add`; default location, TTL and desktop port; all eight sizing profiles; global and per-location reports; invalid TTLs, sizes, required arguments, handles, paths, snapshot names, jobs and ports |
37
37
 
38
38
  ## Command and option audit (2026-10-06)
39
39
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microck/raft",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "description": "Self-hosted Linux workspaces powered by Incus, an open-source alternative to boat.dev",
5
5
  "license": "MIT",
6
6
  "repository": {