@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.
- package/README.md +23 -10
- package/deploy/deploy.py +112 -19
- package/deploy/install-incus.sh +10 -0
- package/deploy/verify-controls.py +34 -48
- package/deploy/verify-features.py +350 -4
- package/deploy/verify-fresh-host.py +64 -1
- package/deploy/verify-limits.py +325 -71
- package/deploy/verify-live.py +38 -29
- package/deploy/verify-self-update.py +372 -0
- package/docs/boat-capabilities.md +8 -7
- package/docs/boot-performance.mdx +1 -1
- package/docs/contract.md +28 -17
- package/docs/guides/backup-recovery.mdx +1 -1
- package/docs/guides/config-policy.mdx +179 -0
- package/docs/guides/docker.mdx +9 -1
- package/docs/guides/long-running-services.mdx +2 -2
- package/docs/guides/meta.json +1 -0
- package/docs/guides/project-setup.mdx +4 -3
- package/docs/guides/scripting-ci.mdx +25 -10
- package/docs/guides/snapshots-forks.mdx +9 -3
- package/docs/guides/troubleshooting.mdx +18 -6
- package/docs/guides/upgrades.mdx +13 -3
- package/docs/index.mdx +1 -0
- package/docs/operations.mdx +16 -14
- package/docs/reference/capacity.mdx +198 -0
- package/docs/reference/cli.md +166 -29
- package/docs/reference/configuration.mdx +36 -2
- package/docs/reference/meta.json +1 -1
- package/docs/releases/0.4.0.md +15 -0
- package/docs/releases/0.5.0.md +19 -0
- package/docs/requirements.mdx +2 -2
- package/docs/verification.mdx +14 -8
- package/package.json +1 -1
- package/raft.py +1105 -125
- package/skills/raft-cli/SKILL.md +76 -12
- package/docs/reference/capacity-limits.mdx +0 -92
|
@@ -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.
|
package/docs/guides/docker.mdx
CHANGED
|
@@ -64,7 +64,15 @@ docker run --rm my-app
|
|
|
64
64
|
|
|
65
65
|
## Managing disk space
|
|
66
66
|
|
|
67
|
-
Nested container layers and volumes
|
|
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
|
|
32
|
-
|
|
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
|
package/docs/guides/meta.json
CHANGED
|
@@ -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`
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
| `
|
|
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. `
|
|
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,
|
|
38
|
-
See [
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
- **
|
|
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,
|
|
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
|
|
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,
|
|
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
|
-
###
|
|
123
|
+
### Sizing and disk errors
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
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
|
|
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
|
|
package/docs/guides/upgrades.mdx
CHANGED
|
@@ -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 |
|
|
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
|
-
|
|
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.
|
|
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) |
|
package/docs/operations.mdx
CHANGED
|
@@ -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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
170
|
-
curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.
|
|
171
|
-
curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.
|
|
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.
|
|
183
|
-
curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.
|
|
184
|
-
curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.
|
|
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).
|