@microck/raft 0.0.0-stage → 0.2.1

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.
Files changed (67) hide show
  1. package/.github/assets/raft-logo.png +0 -0
  2. package/LICENSE +21 -0
  3. package/README.md +167 -2
  4. package/deploy/benchmark-boot.py +150 -0
  5. package/deploy/build-image.py +95 -0
  6. package/deploy/deploy.py +176 -0
  7. package/deploy/incus-network.conf +4 -0
  8. package/deploy/install-incus.sh +22 -0
  9. package/deploy/raft-expire.py +62 -0
  10. package/deploy/raft-expire.service +8 -0
  11. package/deploy/raft-expire.timer +8 -0
  12. package/deploy/raft-network.service +12 -0
  13. package/deploy/raft-network.sh +40 -0
  14. package/deploy/verify-controls.py +241 -0
  15. package/deploy/verify-features.py +519 -0
  16. package/deploy/verify-fresh-host.py +301 -0
  17. package/deploy/verify-image-tests.py +139 -0
  18. package/deploy/verify-image.py +95 -0
  19. package/deploy/verify-limits.py +110 -0
  20. package/deploy/verify-live.py +903 -0
  21. package/deploy/verify-release.py +132 -0
  22. package/deploy/verify-tools.py +105 -0
  23. package/docs/benchmarks/amd64-native-ci.json +137 -0
  24. package/docs/benchmarks/arm64-baseline-a.json +108 -0
  25. package/docs/benchmarks/arm64-baseline-b.json +108 -0
  26. package/docs/benchmarks/arm64-candidate-a.json +109 -0
  27. package/docs/benchmarks/arm64-desktop-local.json +22 -0
  28. package/docs/benchmarks/arm64-native-ci.json +137 -0
  29. package/docs/boat-capabilities.md +64 -0
  30. package/docs/boat-parity-design.md +26 -0
  31. package/docs/boot-performance.mdx +102 -0
  32. package/docs/contract.md +93 -0
  33. package/docs/first-box.mdx +86 -0
  34. package/docs/guides/background-jobs.mdx +95 -0
  35. package/docs/guides/backup-recovery.mdx +111 -0
  36. package/docs/guides/desktop-services.mdx +90 -0
  37. package/docs/guides/docker.mdx +84 -0
  38. package/docs/guides/files.mdx +91 -0
  39. package/docs/guides/long-running-services.mdx +109 -0
  40. package/docs/guides/meta.json +18 -0
  41. package/docs/guides/project-setup.mdx +112 -0
  42. package/docs/guides/scripting-ci.mdx +133 -0
  43. package/docs/guides/snapshots-forks.mdx +107 -0
  44. package/docs/guides/troubleshooting.mdx +152 -0
  45. package/docs/guides/upgrades.mdx +113 -0
  46. package/docs/incus.example.json +3 -0
  47. package/docs/index.mdx +40 -0
  48. package/docs/meta.json +21 -0
  49. package/docs/operations.mdx +239 -0
  50. package/docs/publishing.md +48 -0
  51. package/docs/reference/capacity-limits.mdx +92 -0
  52. package/docs/reference/cli.md +288 -0
  53. package/docs/reference/configuration.mdx +77 -0
  54. package/docs/reference/meta.json +10 -0
  55. package/docs/releases/0.2.0.md +22 -0
  56. package/docs/releases/0.2.1.md +14 -0
  57. package/docs/requirements.mdx +47 -0
  58. package/docs/reviews/code-review-2026-10-06.md +168 -0
  59. package/docs/site-development.md +80 -0
  60. package/docs/verification.mdx +237 -0
  61. package/images/incus/build.sh +57 -0
  62. package/images/incus/desktop.sh +21 -0
  63. package/images/incus/raft-desktop.service +7 -0
  64. package/package.json +46 -4
  65. package/raft.py +961 -0
  66. package/raft_files.py +134 -0
  67. package/skills/raft-cli/SKILL.md +117 -0
@@ -0,0 +1,77 @@
1
+ ---
2
+ title: Configuration reference
3
+ description: Schema, fields, and security requirements for controller configuration.
4
+ ---
5
+
6
+ # Configuration reference
7
+
8
+ The Raft controller CLI reads host inventory and image mappings from `~/.config/raft/incus.json`.
9
+
10
+ ## File location and security
11
+
12
+ ```text
13
+ ~/.config/raft/incus.json
14
+ ```
15
+
16
+ Ensure the configuration file is accessible only by your user account:
17
+
18
+ ```sh title="CONTROLLER: Set strict file permissions"
19
+ chmod 600 ~/.config/raft/incus.json
20
+ ```
21
+
22
+ ## Schema format
23
+
24
+ The configuration file maps location names to host definition objects:
25
+
26
+ ```json title="Example ~/.config/raft/incus.json"
27
+ {
28
+ "lab": {
29
+ "ssh": "ubuntu@192.0.2.10",
30
+ "image": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
31
+ },
32
+ "remote-builder": {
33
+ "ssh": "builder.example.com",
34
+ "image": ""
35
+ }
36
+ }
37
+ ```
38
+
39
+ ### Location keys
40
+
41
+ - **Naming format**: Matches `^[a-zA-Z0-9][a-zA-Z0-9_-]*$`. Starts with an alphanumeric character and contains ASCII letters, digits, underscores, or hyphens.
42
+ - **Default location**: The first location listed is the default for `raft new`. `raft list`, `raft limits`, `raft gc`, and `raft prune` accept `--location` filters. Without a filter they check all configured hosts; `raft doctor` always checks all configured hosts.
43
+
44
+ ### Host entry fields
45
+
46
+ | Field | Type | Required | Description |
47
+ | :--- | :--- | :--- | :--- |
48
+ | `ssh` | `string` | Yes | Non-empty SSH connection string (IP, `user@host`, or SSH host alias from `~/.ssh/config`). |
49
+ | `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. |
50
+
51
+ <Callout type="warn" title="Replace placeholder image fingerprint">
52
+ 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.
53
+
54
+ ARM64 and AMD64 images produce different fingerprints; do not reuse an ARM64 hash on an AMD64 host.
55
+ </Callout>
56
+
57
+ ## SSH configuration and connection behavior
58
+
59
+ Primary remote commands (`raft ssh`, `upload`, `download`, and control commands) run OpenSSH with `BatchMode=yes` and `-o ConnectTimeout=10` to fail fast on unreachable hosts. The timeout applies to initial connection establishment, not session or transfer duration.
60
+
61
+ Other operations, recovery routines, and tunnels use standard SSH defaults without an explicit timeout flag.
62
+
63
+ Configure host connections in `~/.ssh/config` for batch authentication:
64
+
65
+ ```text title="~/.ssh/config"
66
+ Host lab-host
67
+ HostName 192.0.2.10
68
+ User ubuntu
69
+ IdentityFile ~/.ssh/id_ed25519
70
+ IdentitiesOnly yes
71
+ ```
72
+
73
+ Verify passwordless batch SSH before running Raft commands:
74
+
75
+ ```sh title="CONTROLLER: Verify passwordless batch connection"
76
+ ssh -o BatchMode=yes lab-host "echo SSH connection verified"
77
+ ```
@@ -0,0 +1,10 @@
1
+ {
2
+ "title": "Reference",
3
+ "collapsible": true,
4
+ "defaultOpen": false,
5
+ "pages": [
6
+ "cli",
7
+ "configuration",
8
+ "capacity-limits"
9
+ ]
10
+ }
@@ -0,0 +1,22 @@
1
+ ---
2
+ title: Raft 0.2.0
3
+ ---
4
+
5
+ # Raft 0.2.0
6
+
7
+ Install the CLI with `npm install -g @microck/raft` on Linux with Python 3.11.8+, or use the Python wheel/source archive attached to this release. The command remains `raft`.
8
+
9
+ - Disposable boxes delete themselves on stop or expiry. Persistent boxes keep their files by default.
10
+ - `prune --older-than DAYS` previews stopped boxes eligible for deletion. Add `--yes` to apply it.
11
+ - Create boxes from named snapshots and delete snapshots directly.
12
+ - Upload and download directories, with no-overwrite publication and rejection of unsafe links, special files, incomplete streams and source-replacement races.
13
+ - Resize CPU/RAM on resume or fork. Omitted limits are preserved.
14
+ - `raft --version` reports the installed release.
15
+
16
+ Redeploy the host worker from this release before using disposable creation or retention cleanup. npm installation does not configure hosts or install Python; follow the [setup guide](https://raft.micr.dev/docs/operations).
17
+
18
+ ARM64 and AMD64 development images, manifests, package inventories and startup reports accompany this release. Verify `SHA256SUMS` before importing the archive for your host architecture.
19
+
20
+ Both architectures pass native lifecycle, desktop, development-tool, capacity, directory-transfer and full-image recovery checks. Fresh provisioning, image import, firewall ordering and reboot pass on a disposable AMD64 host.
21
+
22
+ Raft is an independent open-source alternative to boat.dev. Containers share the host kernel. Physical storage capacity, ARM64 host reboot and cross-architecture recovery are outside the verified scope.
@@ -0,0 +1,14 @@
1
+ ---
2
+ title: Raft 0.2.1
3
+ ---
4
+
5
+ # Raft 0.2.1
6
+
7
+ - `raft doctor` now rejects an incompatible host expiry worker and identifies the location that needs deployment. It does not run expiry or change boxes.
8
+ - Lifecycle transactions close controller stdin so allocation cannot wait for piped input while holding the host lock.
9
+
10
+ Install on Linux with Python 3.11.8+ using `npm install -g @microck/raft@0.2.1`, or install the attached Python wheel. Updating the CLI does not update host workers. Deploy the matching checkout to hosts that fail the readiness check.
11
+
12
+ Exec, SSH, and file-transfer behavior is unchanged. Piped input to `raft exec` is unsupported.
13
+
14
+ ARM64 and AMD64 development images accompany this release. Verify `SHA256SUMS` before importing an image for your host architecture.
@@ -0,0 +1,47 @@
1
+ ---
2
+ title: System requirements
3
+ description: Hardware, operating system, network, and tool requirements for running Raft.
4
+ ---
5
+
6
+ # System requirements
7
+
8
+ Raft coordinates unprivileged Linux containers across two environments: dedicated Incus workspace hosts, and a controller running the Raft CLI.
9
+
10
+ ## Workspace host requirements
11
+
12
+ Each workspace host must be a dedicated physical machine or virtual instance:
13
+
14
+ | Component | Requirement | Notes |
15
+ | :--- | :--- | :--- |
16
+ | **Architecture** | ARM64 or AMD64 | Workspaces run native binaries matching the host CPU architecture. |
17
+ | **Operating system** | Ubuntu 22.04 LTS or 24.04 LTS | The deployer installs Incus 6.0 LTS and nftables bridge isolation rules. |
18
+ | **CPU** | At least 2 CPU cores | Capacity calculations reserve 1 CPU core for the host OS and services (an advisory estimate, not an enforced host reservation). |
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
+ | **Free disk** | At least 80 GiB on host disk | The deployer provisions a 60 GiB dedicated Btrfs pool (`raft-data`). |
21
+
22
+ Host CPU and RAM reserves are advisory estimates for `raft limits`, not enforced cgroup locks.
23
+
24
+ <Callout type="warn" title="Dedicated host requirement">
25
+ Do not deploy Raft on hosts with existing Incus storage pools or shared virtualization workloads. Deployment configures dedicated nftables rules, storage pools, and system services.
26
+ </Callout>
27
+
28
+ ### Host access and permissions
29
+
30
+ - **SSH access**: SSH access with passwordless `sudo` (`sudo -n true`).
31
+ - **Security model**: Single operator with root access. Deploy only on trusted hardware and networks.
32
+ - **Network connectivity**: Outbound internet for Debian packages, Incus releases, and container images. Inbound SSH from the controller. No public Incus listener or cloud account needed.
33
+
34
+ ## Controller machine requirements
35
+
36
+ The controller is your local workstation, developer machine, or CI runner:
37
+
38
+ | Component | Requirement | Notes |
39
+ | :--- | :--- | :--- |
40
+ | **Operating system** | Linux | Controller CLI commands invoke standard Linux utilities. |
41
+ | **Python** | Python 3.11.8 or newer | Required to run the `raft` CLI tool. |
42
+ | **Toolchain** | `git`, `uv`, OpenSSH client (`ssh`), `scp` | `uv` manages the Python environment; OpenSSH provides the transport. |
43
+ | **Network** | Network route to host SSH targets | Batch SSH commands must connect without interactive password prompts. |
44
+
45
+ ## Next step
46
+
47
+ Proceed to [Installation and setup](./operations.mdx) to install the CLI, configure inventory, provision Incus, and acquire a workspace image.
@@ -0,0 +1,168 @@
1
+ # Full-code review: 2026-10-06
2
+
3
+ Reviewed snapshot: `198bb1dfad60630b47a8fe40f4cd69e7d5c60162`.
4
+ Scope: the full CLI, deployment scripts, image build, verification tools, services,
5
+ workflows, packaging and agent skill. Two independent read-only reviews used
6
+ `docs/contract.md` as the specification and the applicable coding instructions as
7
+ standards. Findings below refer to this pinned snapshot, before documentation-site changes.
8
+
9
+ This is a code review, not a claim that every runtime path passed a new E2E run.
10
+ No host configuration changed during the original review.
11
+ The six distinct findings have since been fixed; see the follow-up verification below.
12
+
13
+ ## Standards
14
+
15
+ ### P1: stopped-state checks happen outside the lifecycle lock
16
+
17
+ Locations: [raft.py](../../raft.py), snapshot line 661, restore line 669,
18
+ fork line 675 in the reviewed snapshot.
19
+
20
+ Each command checks the previously fetched box status before acquiring the host
21
+ lifecycle lock. A concurrent resume can start the source before snapshot, restore
22
+ or copy runs. This violates the documented stopped-source requirement and the
23
+ correctness rule that validation and enforcement must agree.
24
+
25
+ Check the current state inside the same locked transaction as the operation.
26
+ The backup path already follows this approach. Evidence: code inspection of the
27
+ status read, branch and lock boundary; the race was not reproduced on a live host.
28
+
29
+ ### P1: repeat deployment does not apply changed firewall rules
30
+
31
+ Locations: [deploy/deploy.py](../../deploy/deploy.py), lines 145-146;
32
+ [raft-network.service](../../deploy/raft-network.service), line 9.
33
+
34
+ Deployment overwrites the firewall script, then calls `systemctl enable --now`.
35
+ The oneshot service has `RemainAfterExit=yes`. Starting an already active service
36
+ does not rerun its script, so updated rules remain unapplied while deployment
37
+ reports that the private bridge is ready. Apply and verify the installed rules
38
+ on redeployment. Evidence: code and unit inspection; no host service was restarted.
39
+
40
+ ### P1: firewall ordering does not gate guest autostart
41
+
42
+ Location: [raft-network.service](../../deploy/raft-network.service), line 3.
43
+
44
+ The firewall service runs after Incus. Incus can restore running guests before
45
+ filtering is installed. No dependency gates guest startup on the firewall.
46
+ The existing reboot checks wait for the firewall to become active before probing,
47
+ so they cannot detect exposure during startup. Install persistent filtering before
48
+ guest autostart or explicitly gate startup on it. This is a structural risk found
49
+ by inspection, not a demonstrated boot-time exploit.
50
+
51
+ ### P2: native E2E workflow misses deployment and expiry changes
52
+
53
+ Location: [images.yml](../../.github/workflows/images.yml), push path filter.
54
+
55
+ Changes to `deploy/deploy.py`, `deploy/raft-expire.py` and the expiry service/timer
56
+ do not trigger native fresh-host, reboot and TTL checks. Package checks do not
57
+ exercise those host behaviors. Include these sources in the native workflow's
58
+ trigger list. Evidence: comparison of source ownership with the workflow paths.
59
+
60
+ No separate heuristic refactor was warranted by the review.
61
+
62
+ ## Spec
63
+
64
+ ### P1: stopped-source requirement can be bypassed by concurrent resume
65
+
66
+ Locations: [raft.py](../../raft.py), snapshot, restore and fork branches.
67
+
68
+ Contract: “Snapshots, restores and forks require a stopped source for consistent disk contents.”
69
+
70
+ The stopped check is outside the lifecycle lock. A concurrent resume can invalidate
71
+ it before disk operations begin. Validate stopped state inside the locked operation.
72
+ Evidence: code inspection; no live race experiment.
73
+
74
+ ### P1: redeployment can leave the network contract unenforced
75
+
76
+ Locations: [deploy/deploy.py](../../deploy/deploy.py) and
77
+ [raft-network.service](../../deploy/raft-network.service).
78
+
79
+ Contract: “firewall rules reject guest access to host services, cloud metadata, private address ranges.”
80
+
81
+ `enable --now` does not reapply changed firewall rules when the persistent oneshot
82
+ is already active. A redeployed host can retain obsolete filtering. Reapply and
83
+ verify rules before reporting success. Evidence: code and service inspection.
84
+
85
+ ### P2: clean-image inspection misses non-root SSH credentials
86
+
87
+ Location: [verify-image.py](../../deploy/verify-image.py), credential check near line 35.
88
+
89
+ Contract: “The development image omits ... account credentials.”
90
+
91
+ The inspector rejects `rootfs/root/.ssh/` but accepts a private-key path such as
92
+ a private key beneath a non-root user’s `.ssh` directory. It also skips nonregular archive entries
93
+ before inspecting credential paths. A real temporary tar fixture containing the
94
+ non-root private-key path passed verification and printed `Clean image verified`.
95
+ This proves a gap in the inspector, not that a published image contains credentials.
96
+ Inspect SSH credential paths across user homes and relevant archive entry types.
97
+
98
+ ### P2: an oversized existing owned pool passes deployment validation
99
+
100
+ Location: [deploy/deploy.py](../../deploy/deploy.py), pool validation near line 108.
101
+
102
+ Contract: “Btrfs storage is bounded to a 60 GiB pool per host.”
103
+
104
+ Validation checks the Btrfs driver but not the configured pool size. An existing
105
+ owned pool configured above 60 GiB passes the shown checks. Reject contract drift
106
+ with explicit recovery instructions rather than silently altering storage.
107
+ Evidence: code inspection, not a destructive live pool-resize test.
108
+
109
+ ## Counts
110
+
111
+ Standards: four findings, with P1 lifecycle consistency and firewall enforcement
112
+ as the highest severity. Spec: four findings, with P1 lifecycle consistency and
113
+ firewall enforcement as the highest severity. The two axes intentionally retain
114
+ overlapping findings rather than combining their counts.
115
+
116
+ ## Documentation-site follow-up
117
+
118
+ The new site received separate standards and spec follow-up reviews. Three site
119
+ issues were corrected before delivery: bare Markdown links opening raw text,
120
+ a repository workflow link resolving to an absent local route, and missing
121
+ `Vary: Accept` on HTML responses. Canonical document links now begin with `./`,
122
+ source-file links use GitHub, and both negotiated representations set the header.
123
+
124
+ Verification: production build, strict TypeScript and Oxlint passed. A real
125
+ production-server test checked nine pages, 16 local link targets, search results,
126
+ Markdown exports, JSON downloads, the root redirect and unknown-page 404s.
127
+ Browser checks covered desktop and 390-pixel mobile layouts, sidebar navigation,
128
+ Ctrl+K search and result navigation. Computed body colors were black and white;
129
+ each page had one H1 and browser error inspection was empty. Wide parity tables
130
+ scroll within their container rather than widening the mobile page.
131
+
132
+ The simplification pass ran in the parent context across reuse, quality and
133
+ efficiency. No behavior-preserving refactor was warranted. No runtime review
134
+ finding above was fixed at that stage, and site checks did not modify
135
+ workspace hosts.
136
+
137
+ ## Runtime-fix follow-up
138
+
139
+ All six distinct findings are addressed by the follow-up patch. Both review axes
140
+ reported no additional actionable findings after inspecting the fixes and tests.
141
+
142
+ | Finding | Fix | Verification |
143
+ | --- | --- | --- |
144
+ | Stopped-source race | Ownership and state are checked inside the same locked transaction as snapshot, restore, fork and backup | Real queued snapshot/restore/fork operations reject a source started first under the host lock on two ARM64 hosts |
145
+ | Stale rules on redeploy | Reloadable firewall oneshot explicitly reapplies owned chains transactionally, preserving unrelated chains and the running Incus daemon | Isolated first/repeat application passed; fresh-host CI checks injected stale-rule removal, unrelated-chain retention and unchanged daemon start timestamp |
146
+ | Guest autostart before filtering | Incus requires the firewall and starts after successful installation, including socket activation | Installed dependency verified on two hosts; fresh-host CI tests failed filtering and post-reboot start timestamps |
147
+ | Missing native CI triggers | All `deploy/**` changes trigger the native workflow | Workflow path inspection; the fix push triggers both native architectures and the fresh-host job |
148
+ | Non-root image credentials | Check credential paths and link targets before skipping nonregular entries | 15 real tar cases passed, including generated non-root private keys, symlinks, hardlinks, FIFO entries and clean controls |
149
+ | Oversized owned storage | Reject an existing pool whose configured size differs from `60GiB`, with recovery guidance and no resize | Existing 60 GiB pools pass; fresh-host CI grows a disposable pool to 61 GiB and verifies rejection without resizing |
150
+
151
+ The extended lifecycle suite passed on two configured ARM64 hosts. Each completed
152
+ all 31 developer-tool checks, six sizing combinations, the deterministic race
153
+ cases, Docker, files, jobs, snapshots, forks, restore, private tunnels, desktop,
154
+ network restrictions and scheduled TTL stop/resume. Each removed its fixtures.
155
+ A cross-host control run also passed portable backup/recovery, snapshot rollback,
156
+ permissions, cancellation and usage checks with a 385,265,470-byte archive.
157
+ Both hosts had zero remaining test boxes afterward. Lint, formatting, compilation
158
+ and release archive/privacy checks passed.
159
+
160
+ The [fix verification run](https://github.com/Microck/raft/actions/runs/37496577945)
161
+ passed native ARM64 and AMD64 builds, extended lifecycle with daemon restart,
162
+ capacity transitions, full development-image backup/recovery, startup measurements
163
+ and clean image inspection for commit `27b08c143dc1837d9cfb95f3c3a784318879b284`.
164
+ The disposable AMD64 KVM host passed stale-rule removal without an Incus restart,
165
+ failed-firewall rejection for normal and socket startup, real host reboot with
166
+ filtering before Incus, and rejection of a 61 GiB pool without resizing it.
167
+ ARM64 host reboot remains unverified. Package and documentation-site workflows
168
+ also passed. No published release assets or configured image fingerprints changed.
@@ -0,0 +1,80 @@
1
+ ---
2
+ title: Documentation site
3
+ ---
4
+
5
+ # Documentation site
6
+
7
+ The Fumadocs site in `website/` compiles Markdown and MDX files in
8
+ `docs/` directly. Edit those files to update both GitHub documentation and the site.
9
+ `docs/meta.json` and directory `meta.json` files control sidebar order and groups. Review reports stay outside the site navigation.
10
+
11
+ ## Run locally
12
+
13
+ Use Node.js 24 and pnpm 11.25.0. Python 3 serves the local static preview.
14
+
15
+ ```sh
16
+ cd website
17
+ pnpm install --frozen-lockfile
18
+ pnpm dev --hostname 127.0.0.1 --port 4300
19
+ ```
20
+
21
+ Open `http://127.0.0.1:4300/docs/`. The site includes navigation, full-text search,
22
+ code highlighting and a table of contents. Light mode is the default; the theme switch also offers dark mode.
23
+
24
+ ## Check and build
25
+
26
+ ```sh
27
+ pnpm lint
28
+ pnpm types:check
29
+ pnpm build
30
+ pnpm test
31
+ pnpm start
32
+ ```
33
+
34
+ ### Build output
35
+
36
+ The build copies benchmark reports and example configuration from `docs/` into the static export. Generated assets and Next.js output stay outside version control. The site is a separate package from the Python CLI.
37
+
38
+ ### Site tests
39
+
40
+ `pnpm test` checks the exported pages, links, search, Markdown downloads, and 404s over controller loopback. It does not contact workspace hosts.
41
+
42
+ Set `RAFT_DOCS_TEST_PORT` if port 4391 is occupied.
43
+
44
+ ## Machine-readable documentation
45
+
46
+ - `/llms.txt` lists the Markdown files for every documentation page.
47
+ - `/llms-full.txt` exports the complete documentation.
48
+ - `/llms.mdx/docs/contract/content.md` exports the contract as Markdown.
49
+
50
+ GitHub Pages serves fixed files. Use the explicit Markdown URLs; documentation
51
+ pages return HTML regardless of the `Accept` header.
52
+
53
+ ## Hosting
54
+
55
+ The public site is [raft.micr.dev](https://raft.micr.dev/). GitHub Pages serves
56
+ `website/out/` at the domain root. Search runs in the browser using a build-time index.
57
+
58
+ - Pull requests build and test the site. Pushes to `main` deploy the verified export.
59
+ - Set the repository Pages publishing source to **GitHub Actions**.
60
+ - For a fork, replace `website/public/CNAME` and the `metadataBase` URL in
61
+ `website/app/layout.tsx` with your domain, then configure Pages and DNS.
62
+
63
+ For a local production preview, `pnpm start` serves the same export at
64
+ `http://127.0.0.1:4300/`. Any static HTTP host can serve the exported files.
65
+ Publishing the documentation does not provide public
66
+ ingress to Raft workspaces.
67
+
68
+ ## Typography
69
+
70
+ | Element | Font |
71
+ | --- | --- |
72
+ | Page titles | Funnel Display |
73
+ | Body text | Inter |
74
+ | Code blocks | Monospace |
75
+
76
+ The pairing follows boat.dev. Upright Latin WOFF2 files from Fontsource live in `website/public/fonts/`; their SIL Open Font Licenses are in `boat-fonts-license.txt`.
77
+
78
+ `next/font/local` serves the bundled fonts without third-party requests.
79
+
80
+ Light mode uses Boat's white background, dark text, and thin borders. The theme switch also offers dark mode.
@@ -0,0 +1,237 @@
1
+ ---
2
+ title: Verification
3
+ ---
4
+
5
+ # Verification
6
+
7
+ Raft is verified using real disposable Incus workspaces, guest processes, files, Docker builds, and network connections. Tests contain no mocked services and verify Raft behavior rather than conformance with the hosted Boat service.
8
+
9
+ ## Verification boundaries
10
+
11
+ Tests do not certify arbitrary long-running workloads, host loss, multi-tenant security boundaries, or exact Boat image/version parity. ARM64 host reboot and physical storage capacity are not certified by the AMD64 VM test. Containers share the host kernel and do not offer VM isolation.
12
+
13
+ Storage accounting covers shared pools rather than per-box quotas. Large backup transfers hold the host lifecycle lock, which delays expiration checks.
14
+
15
+ ## Coverage matrix
16
+
17
+ | Implemented workflow | E2E assertion |
18
+ | --- | --- |
19
+ | Create/list/info/delete | Qualified handle, native state, ownership and cleanup |
20
+ | Root terminal/exec | Real PTY, workspace directory, literal arguments and exit code |
21
+ | Resource sizing | Effective CPU affinity and cgroup memory limit |
22
+ | Host capacity | Mixed-size budgets, stopped/frozen boxes, low RAM/disk and four saved slots; live create/stop/resume/destroy changes in CPU/memory allocations |
23
+ | Background jobs | Journal output, nonzero exit status, cancellation of child processes, inspection after cancellation or log rotation, and rejection of unknown job IDs |
24
+ | TTL/extend/resume | Host-enforced stop, manual `gc`, retained files, uninterrupted lifetime extension and 30-day resume |
25
+ | Usage | Real cgroup counters and explicitly shared-pool disk scope |
26
+ | File transfer | Binary content, paths containing spaces, stopped-workspace transfers, preservation of the destination after a failed download, and real controller write-error rejection |
27
+ | Docker | Real image build and container execution |
28
+ | Snapshot/restore/fork | File rollback, independent copy, retained network identity and deterministic concurrent-resume rejection under the real host lock |
29
+ | Backup/recovery | Stopped source, no overwrite, snapshots, content, permissions and fresh MAC |
30
+ | Private forwarding | Guest HTTP response through controller loopback tunnel |
31
+ | Desktop | WebSocket/RFB negotiation, framebuffer, pointer, keyboard, clipboard and reconnect |
32
+ | Network separation | Positive service control, negative IPv4/IPv6 peer, host and metadata probes |
33
+ | Development image | Real developer-user tool checks, immutable selection and credential-path archive rejection |
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
+ | 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 |
37
+
38
+ ## Command and option audit (2026-10-06)
39
+
40
+ <details>
41
+ <summary>Coverage, regressions and remaining limits</summary>
42
+
43
+ The audit checked help for all 26 commands and expanded the live suites to cover defaults, invalid arguments, manual garbage collection, maximum lifetimes and unknown job IDs. Two ARM64 hosts passed extended lifecycle tests, all six sizing profiles and all 31 development-tool checks.
44
+
45
+ After the fixes, live lifecycle and same-host recovery passed again. Cross-host recovery passed with a 384,827,472-byte archive. Test boxes and archives were removed without rebooting or reconfiguring the existing hosts.
46
+
47
+ The audit found and fixed two CLI bugs:
48
+
49
+ - Unknown background jobs appeared to succeed. Job inspection now distinguishes unknown IDs, loaded jobs and canceled jobs with retained journal entries. Tests cover inspection after cancellation and journal rotation.
50
+ - SSH could report success after a controller write failed. Downloads and backups now copy the stream through Python so local write errors reach the caller. Real `/dev/full` tests verify both receiving paths. The same Incus/SSH producer falsely succeeded with the previous implementation and raised `ENOSPC` with the fix.
51
+
52
+ This covers implemented workflows and selected option combinations, not every possible workload or environment. A full-image export exceeded the local controller's free disk space and was interrupted. Full-sized archive validation runs on disposable CI hosts with sufficient scratch space.
53
+
54
+ The [final runtime validation](https://github.com/Microck/raft/actions/runs/37542355027) tests commit `ac618e64`. ARM64 and AMD64 both passed extended lifecycle tests with daemon restarts, capacity transitions and full development-image backup/recovery. [Packaging checks](https://github.com/Microck/raft/actions/runs/37542355021) passed on Python 3.11 and 3.14, and [documentation checks](https://github.com/Microck/raft/actions/runs/37542355020) passed.
55
+
56
+ </details>
57
+
58
+ ## New workspace controls
59
+
60
+ `python3 deploy/verify-features.py --location lab` verifies `doctor` readiness against the installed worker, recursive trees and unsafe-archive rejection, templates from a running source, snapshot deletion, CPU/RAM overrides, disposable timer deletion, and retention preview/apply. It uses only its own fixtures. Run the archive checks without `--location` to avoid allocating boxes.
61
+
62
+ The live suite also holds a controller stdin pipe open while running real remote
63
+ readers through both captured and streamed lifecycle transactions. Both must
64
+ receive EOF without consuming controller input, and a subsequent transaction
65
+ must acquire the same host lock. This covers allocation stalls caused by Incus
66
+ waiting for stdin while the expiration worker waits for the lifecycle lock.
67
+
68
+ For an older deployment whose services should stay unchanged, `--isolated-worker` first verifies that `doctor` rejects the stale installed worker with a location-specific deployment diagnostic, then uses the current worker under a private temporary path. This checks worker logic without installing it; automatic timer integration is verified on disposable CI hosts. Current deployments use the normal verification mode; the isolated mode rejects them before allocation.
69
+
70
+ ## Workspace controls validation (2026-10-07)
71
+
72
+ <details>
73
+ <summary>Native feature tests and transfer regressions</summary>
74
+
75
+ At runtime commit `a7fef1f5`, ARM64 and AMD64 passed the new workspace-control suite in [native CI](https://github.com/Microck/raft/actions/runs/37603576786). It verifies installed-timer deletion, retention preview and exclusions, live-source snapshot templates, snapshot deletion, and effective CPU/RAM limits after resume and fork.
76
+
77
+ Directory tests cover binary files, permissions, relative symlinks, hardlinks, empty directories and existing destinations. Unix sockets and unsafe archive entries are rejected. A real Incus transfer truncated between complete archive entries fails without publishing a partial directory.
78
+
79
+ Both architectures passed extended lifecycle tests, full development-image recovery, startup measurements and clean-template inspection. Fresh provisioning, image import, firewall ordering and reboot passed on a disposable AMD64 KVM host.
80
+
81
+ The source-race fix also passed a real concurrent writer test that swaps files and directories for external symlinks. Accepted archives retain only the selected files. Two configured ARM64 hosts passed the final feature suite with a staged worker, without changing installed services. Local fixtures and staged files were removed.
82
+
83
+ </details>
84
+
85
+ ## Running verification suites
86
+
87
+ Test commands require configured hosts and immutable image fingerprints. Suites destroy only the fixtures they create.
88
+
89
+ Allocation and archive inspection:
90
+ - `python3 deploy/verify-limits.py`: Checks allocation policies without a host. Add `--location lab` to verify live create, stop, resume, and destroy reporting.
91
+ - `python3 deploy/verify-image-tests.py`: Inspects archive structures, including symlinks, hardlinks, FIFOs, and credential detection.
92
+
93
+ Live host suites (expand the check you need):
94
+
95
+ <details>
96
+ <summary>Lifecycle</summary>
97
+
98
+ ```sh
99
+ python3 deploy/verify-live.py --location lab --extended
100
+ ```
101
+
102
+ </details>
103
+
104
+ <details>
105
+ <summary>Daemon restart on a dedicated test host</summary>
106
+
107
+ ```sh
108
+ python3 deploy/verify-live.py --location lab --restart-incus
109
+ ```
110
+
111
+ </details>
112
+
113
+ <details>
114
+ <summary>Cross-host recovery</summary>
115
+
116
+ ```sh
117
+ python3 deploy/verify-controls.py --source lab --target second-host --directory /path/to/archive-storage
118
+ ```
119
+
120
+ </details>
121
+
122
+ <details>
123
+ <summary>Same-host recovery</summary>
124
+
125
+ ```sh
126
+ python3 deploy/verify-controls.py --source lab --target lab --directory /path/to/archive-storage
127
+ ```
128
+
129
+ </details>
130
+
131
+ <details>
132
+ <summary>Full development-image recovery</summary>
133
+
134
+ ```sh
135
+ python3 deploy/verify-controls.py --source lab --target second-host --directory /path/to/archive-storage --development-image
136
+ ```
137
+
138
+ </details>
139
+
140
+ `verify-tools.py <qualified-box>` executes 31 tool checks as the unprivileged `developer` user inside a running workspace. It compiles and runs code across installed runtimes and package managers without external agent authentication.
141
+
142
+ ## Extended lifecycle results (2026-10-05)
143
+
144
+ <details>
145
+ <summary>Test evidence and run details</summary>
146
+
147
+ The extended lifecycle suite and strengthened tool checks passed on two ARM64 Ubuntu hosts with Incus 6.0.6 on 2026-10-05.
148
+
149
+ Both hosts executed all six sizing combinations, all 31 developer tool checks, Docker builds, file transfers, stopped snapshots, forks, restore, jobs, and private HTTP and desktop tunnels.
150
+
151
+ Desktop testing validated keyboard input, exact clipboard synchronization, pointer coordinates, framebuffer updates, and secondary client connections. Headed Chromium testing passed with sandboxing enabled, confirming canvas rendering at 1280x720 over the noVNC interface.
152
+
153
+ **Admission and backup runs**
154
+
155
+ The four-saved-box admission test passed, including rejection of a fifth create, fork, or recovery attempt. Full development-image recovery succeeded with a 5,254,224,965-byte archive, verifying inventories, tool checks, permissions, fresh MACs, and snapshot rollback.
156
+
157
+ The execution wrapper reported exit 143 after the success message; independent native inventory and archive checks confirmed that all fixtures and temporary archives were removed.
158
+
159
+ Debian fixture backup and recovery passed across the following tests:
160
+ - Same-host recovery: 385,278,177-byte archive with snapshot rollback and distinct MAC/IP identities.
161
+ - Reverse cross-host recovery: 385,282,465-byte archive.
162
+ - Forward cross-host admission run: 385,272,379-byte archive (exit code 0).
163
+
164
+ All recovery fixtures were destroyed; existing operator hosts were not rebooted or reconfigured.
165
+
166
+ </details>
167
+
168
+ ## Review-fix validation (2026-10-06)
169
+
170
+ <details>
171
+ <summary>Test evidence and run details</summary>
172
+
173
+ On 2026-10-06, the extended suite passed on two configured ARM64 hosts after redeploying review fixes. Both ran all 31 tool checks, six sizing profiles, and deterministic race rejection when a source container started before obtaining the host lock. Cross-host recovery passed with a 385,265,470-byte archive.
174
+
175
+ Both hosts had no remaining test boxes; existing hosts were not rebooted or reconfigured.
176
+
177
+ The image inspector passed 15 test cases, including non-root SSH keys, symlinks, hardlinks, and clean controls. Network testing verified initial and repeated firewall application while preserving unrelated chains.
178
+
179
+ The [review-fix native run](https://github.com/Microck/raft/actions/runs/37496577945) passed all native steps on ARM64 and AMD64: development-image builds, extended lifecycle with daemon restarts, capacity transitions, and clean-image inspection. Recovery archives were 3,844,136,311 bytes on ARM64 and 3,985,872,081 bytes on AMD64.
180
+
181
+ **Disposable KVM host tests**
182
+
183
+ Fresh provisioning on a disposable AMD64 KVM host confirmed:
184
+ - Repeat deployment removed injected rules while leaving Incus daemon start timestamps unchanged.
185
+ - Failed firewall installation blocked both normal and socket-activated Incus startup.
186
+ - Host reboot verified firewall rules became active before Incus started.
187
+ - A disposable pool resized to 61 GiB caused deployment to fail and preserved the existing size.
188
+
189
+ ARM64 host reboot and physical disk capacity remain outside this verification.
190
+
191
+ </details>
192
+
193
+ ## Native image validation (2026-10-06)
194
+
195
+ <details>
196
+ <summary>Test evidence and run details</summary>
197
+
198
+ On 2026-10-06, the [native CI run](https://github.com/Microck/raft/actions/runs/37447415532) built ARM64 and AMD64 images on Ubuntu 24 and passed extended lifecycle tests on both. Workspaces executed all six sizing combinations, 31 tool checks, sandboxed Chromium, noVNC desktop sessions, and peer isolation checks. Timings are recorded in [Boot performance](./boot-performance.mdx).
199
+
200
+ In the [release-validation run](https://github.com/Microck/raft/actions/runs/37469540182), native image jobs passed full development-image recovery on both architectures. Package inventories matched and all 31 tool checks passed. Archives measured 3,844,135,523 bytes on ARM64 and 3,985,894,517 bytes on AMD64.
201
+
202
+ </details>
203
+
204
+ ## Fresh-host and reboot validation
205
+
206
+ <details>
207
+ <summary>Test evidence and run details</summary>
208
+
209
+ The [release run](https://github.com/Microck/raft/actions/runs/37469540182) validated fresh-host setup on a disposable AMD64 KVM instance running Ubuntu 24. The test verified canonical deployment, repeat deployment, image import, and lifecycle execution.
210
+
211
+ Following a VM reboot, changed kernel boot IDs and active services were verified alongside retained files, snapshots, container states, Docker, and bridge isolation. The host test uses a sparse 100 GiB virtual disk and accessible KVM.
212
+
213
+ It preserves the production deployer's 80 GiB free-disk check but does not prove physical storage capacity. ARM64 host reboot, host loss, and cross-architecture recovery remain unverified. The VM, SSH keys, and controller configuration were disposable; existing operator hosts were not rebooted or reconfigured.
214
+
215
+ </details>
216
+
217
+ ## Packaging and release checks
218
+
219
+ <details>
220
+ <summary>Test evidence and run details</summary>
221
+
222
+ Package builds run via `uv build`. Source and wheel archives are inspected to exclude version-control files and local caches. Clean wheel installation is verified outside the worktree alongside Ruff, syntax checks, and secret scans.
223
+
224
+ Packaging workflows passed on Python 3.11 and 3.14. Guidelines for visibility changes and repository hygiene are detailed in [Publishing](./publishing.md).
225
+
226
+ </details>
227
+
228
+ ## Capacity command validation (2026-10-06)
229
+
230
+ <details>
231
+ <summary>Test evidence and run details</summary>
232
+
233
+ On 2026-10-06, `raft limits` passed read-only text and JSON inspection and live lifecycle tests on two ARM64 Ubuntu hosts. Tests verified allocation adjustments during create, stop, resume, and destroy cycles.
234
+
235
+ Allocation fixtures validated mixed sizing budgets, frozen containers, low RAM and disk conditions, host reserves, and admission limits. The four-stopped-box admission test verified that creation, forking, and recovery reject a fifth container when slots are exhausted. These are conservative allocation checks, not workload saturation benchmarks.
236
+
237
+ </details>