@microck/raft 0.0.0-stage → 0.2.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.
Files changed (62) hide show
  1. package/.github/assets/raft-logo.png +0 -0
  2. package/LICENSE +21 -0
  3. package/README.md +162 -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 +461 -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 +82 -0
  34. package/docs/guides/background-jobs.mdx +92 -0
  35. package/docs/guides/backup-recovery.mdx +111 -0
  36. package/docs/guides/desktop-services.mdx +87 -0
  37. package/docs/guides/docker.mdx +84 -0
  38. package/docs/guides/files.mdx +91 -0
  39. package/docs/guides/meta.json +14 -0
  40. package/docs/guides/snapshots-forks.mdx +107 -0
  41. package/docs/guides/troubleshooting.mdx +127 -0
  42. package/docs/incus.example.json +3 -0
  43. package/docs/index.mdx +36 -0
  44. package/docs/meta.json +21 -0
  45. package/docs/operations.mdx +224 -0
  46. package/docs/publishing.md +48 -0
  47. package/docs/reference/capacity-limits.mdx +83 -0
  48. package/docs/reference/cli.md +279 -0
  49. package/docs/reference/configuration.mdx +77 -0
  50. package/docs/reference/meta.json +10 -0
  51. package/docs/releases/0.2.0.md +22 -0
  52. package/docs/requirements.mdx +47 -0
  53. package/docs/reviews/code-review-2026-10-06.md +168 -0
  54. package/docs/site-development.md +80 -0
  55. package/docs/verification.mdx +231 -0
  56. package/images/incus/build.sh +57 -0
  57. package/images/incus/desktop.sh +21 -0
  58. package/images/incus/raft-desktop.service +7 -0
  59. package/package.json +46 -4
  60. package/raft.py +955 -0
  61. package/raft_files.py +134 -0
  62. package/skills/raft-cli/SKILL.md +117 -0
@@ -0,0 +1,127 @@
1
+ ---
2
+ title: Troubleshooting
3
+ description: Diagnose configuration, host infrastructure, network filtering, and container lifecycle issues.
4
+ ---
5
+
6
+ # Troubleshooting
7
+
8
+ Diagnostic procedures and solutions for common deployment, configuration, and runtime issues.
9
+
10
+ ## Controller diagnostics
11
+
12
+ ### Preflight checks with `raft doctor`
13
+
14
+ Run `raft doctor` on your controller to inspect host health across configured locations:
15
+
16
+ ```sh title="CONTROLLER: Run environment health check"
17
+ raft doctor
18
+ ```
19
+
20
+ `raft doctor` connects to each host over SSH and reports:
21
+ - Incus version running in project `raft`
22
+ - Active status of host systemd services (`incus`, `raft-expire.timer`, `raft-network.service`)
23
+ - Host root filesystem disk usage (`df -h /`)
24
+ - Status and usage of the `raft-data` Btrfs pool
25
+ - Count of existing saved containers
26
+
27
+ <Callout type="info" title="Doctor scope">
28
+ `raft doctor` reports runtime daemon and storage status. It does not validate image fingerprints, inspect nftables bridge rules, enforce admission caps, or emit 5 GiB low-disk warnings.
29
+ </Callout>
30
+
31
+ ### Listing active and stopped containers
32
+
33
+ Inspect workspace states across locations:
34
+
35
+ ```sh title="CONTROLLER: List workspaces across all hosts"
36
+ raft list
37
+
38
+ # Filter by a single location
39
+ raft list --location lab
40
+ ```
41
+
42
+ If a host is unreachable over SSH, the CLI reports a connection failure. An unreachable host does not mean containers are absent or deleted.
43
+
44
+ ## Host-side diagnostics
45
+
46
+ Connect to the host over SSH to inspect underlying services when commands fail.
47
+
48
+ ### Inspecting Incus project status
49
+
50
+ Verify container states and storage pools in project `raft`:
51
+
52
+ ```sh title="HOST: Inspect containers and storage pools"
53
+ sudo incus --project raft list
54
+ sudo incus --project raft storage info raft-data
55
+ ```
56
+
57
+ ### Checking host systemd units
58
+
59
+ Raft installs two systemd units on the host:
60
+ - `raft-network.service`: Applies firewall and bridge isolation rules before Incus starts.
61
+ - `raft-expire.timer` / `raft-expire.service`: Runs the expiration worker every 15 seconds to stop expired workspaces.
62
+
63
+ Inspect unit logs with `journalctl`:
64
+
65
+ ```sh title="HOST: Check Raft systemd unit logs"
66
+ sudo systemctl status raft-network.service
67
+ sudo systemctl status raft-expire.timer
68
+ sudo journalctl -u raft-expire.service -n 50 --no-pager
69
+ ```
70
+
71
+ <Callout type="warn" title="Host journals and guest secrets">
72
+ Host system journals can capture command lines, arguments, and environment variables executed in guest containers. Treat host logs as sensitive records that may contain guest credentials.
73
+ </Callout>
74
+
75
+ ### Manually triggering garbage collection
76
+
77
+ The host timer stops expired containers every 15 seconds. Trigger an immediate expiration run from the controller:
78
+
79
+ ```sh title="CONTROLLER: Run expiration workers manually"
80
+ raft gc
81
+ ```
82
+
83
+ Expired containers transition from `Running` to `Stopped`. Expiration never deletes container files or snapshots.
84
+
85
+ ## Common error scenarios
86
+
87
+ ### Configuration errors
88
+
89
+ #### Missing configuration file error
90
+ If `~/.config/raft/incus.json` does not exist, the CLI raises `FileNotFoundError`. Create the configuration directory and file. Set mode `0600` on the file only.
91
+
92
+ #### "Configure at least one host in ~/.config/raft/incus.json"
93
+ The configuration file contains valid JSON that is `{}` or is not an object (an empty file raises a JSON decoding error instead). Ensure the file defines at least one location with a non-empty `ssh` string.
94
+
95
+ #### "Build an image and configure its full immutable fingerprint before creating boxes"
96
+ 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`.
97
+
98
+ ### Admission and capacity errors
99
+
100
+ #### "Location already holds four boxes"
101
+ Raft enforces a hard limit of four saved boxes per location. Both running and stopped containers count toward this limit. Destroy unused boxes with `raft destroy <box>` before creating, forking, or recovering a box.
102
+
103
+ #### Low disk warnings
104
+ `raft limits` warns when free space on host disk or the `raft-data` pool falls below 5 GiB. This warning is diagnostic and does not halt container operations. Clean up dangling images, old snapshots, or unused containers to free space.
105
+
106
+ ### Network and forwarding issues
107
+
108
+ #### Port forward connects but returns connection refused
109
+ Verify that the guest service binds to `0.0.0.0` or the container's `eth0` IP address. Services bound strictly to `127.0.0.1` inside the container cannot receive traffic routed through the bridge.
110
+
111
+ #### Workspaces cannot reach other workspaces on the same host
112
+ This is intended behavior. Raft configures nftables bridge filtering on `rfbr0` to prevent communication between workspaces on the same bridge.
113
+
114
+ ### Host crash during recovery
115
+
116
+ If the controller crashes or SSH drops while `raft recover` is transferring an archive:
117
+ 1. Check the `Recovery handle: <location>:<name>` output printed to stderr when `raft recover` started.
118
+ 2. Inspect the reported handle with `raft list --location <location>` and confirm container status before removing staging.
119
+ 3. Identify the exact staging file (`/tmp/raft-recovery.XXXXXXXX`) on the host by owner and timestamp.
120
+ 4. After confirming container status, remove only that owned file:
121
+ ```sh title="HOST: Remove verified owned staging file"
122
+ sudo -n rm -f -- /tmp/raft-recovery.XXXXXXXX
123
+ ```
124
+
125
+ <Callout type="warn" title="Do not delete by wildcard">
126
+ Never run `rm /tmp/raft-recovery.*`. It can delete files from another recovery.
127
+ </Callout>
@@ -0,0 +1,3 @@
1
+ {
2
+ "lab": {"ssh": "your-ssh-host", "image": ""}
3
+ }
package/docs/index.mdx ADDED
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: Overview
3
+ description: Persistent Linux workspaces powered by Incus.
4
+ ---
5
+
6
+ # Raft
7
+
8
+ Raft is an independent, self-hosted open-source alternative to [boat.dev](https://boat.dev).
9
+ It provides persistent Linux workspaces on ARM64 and AMD64 hardware,
10
+ supporting command execution, Docker, file transfer, snapshots, forks, and desktop access.
11
+ Raft is not affiliated with Boat.
12
+
13
+ ## Getting started
14
+
15
+ Read [System requirements](./requirements.mdx), follow [Installation and host setup](./operations.mdx), and create your [First working box](./first-box.mdx).
16
+
17
+ ```sh title="CONTROLLER: Create and use a workspace"
18
+ box=$(raft new --location lab --ttl 600)
19
+ raft exec "$box" -- node --version
20
+ raft ssh "$box"
21
+ raft stop "$box"
22
+ ```
23
+
24
+ Stopping a workspace preserves files and packages on disk. Resuming resets its running lifetime.
25
+
26
+ ## Task guides
27
+
28
+ | Task | Guide |
29
+ | :--- | :--- |
30
+ | Run nested containers and build images | [Docker in workspaces](./guides/docker.mdx) |
31
+ | Transfer files between controller and workspace | [File transfer](./guides/files.mdx) |
32
+ | Run detached commands with systemd logs | [Background jobs](./guides/background-jobs.mdx) |
33
+ | Forward guest ports or open noVNC desktop | [Desktop and private services](./guides/desktop-services.mdx) |
34
+ | Capture disk state or clone workspaces | [Snapshots and forks](./guides/snapshots-forks.mdx) |
35
+ | Export portable backups across hosts | [Backup and recovery](./guides/backup-recovery.mdx) |
36
+ | Inspect host services and diagnose errors | [Troubleshooting](./guides/troubleshooting.mdx) |
package/docs/meta.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "title": "Raft",
3
+ "pages": [
4
+ "index",
5
+ "---Start here---",
6
+ "requirements",
7
+ "operations",
8
+ "first-box",
9
+ "guides",
10
+ "reference",
11
+ "contract",
12
+ "---Comparison---",
13
+ "boat-capabilities",
14
+ "boot-performance",
15
+ "---Contributing---",
16
+ "boat-parity-design",
17
+ "verification",
18
+ "publishing",
19
+ "site-development"
20
+ ]
21
+ }
@@ -0,0 +1,224 @@
1
+ ---
2
+ title: Installation and host setup
3
+ description: Install the Raft controller CLI, provision a dedicated host, and acquire the native workspace image.
4
+ ---
5
+
6
+ # Installation and host setup
7
+
8
+ Set up a dedicated Ubuntu host, install the Raft CLI, provision Incus, acquire a workspace image, and verify readiness.
9
+
10
+ Review prerequisites in [System requirements](./requirements.mdx) before starting.
11
+
12
+ <Steps>
13
+
14
+ <Step>
15
+
16
+ ### Install prerequisites on the workspace host
17
+
18
+ Connect to your host over SSH, verify its host key fingerprint, and install baseline packages:
19
+
20
+ ```sh title="HOST: Install baseline system packages"
21
+ sudo apt-get update
22
+ sudo apt-get install -y --no-install-recommends \
23
+ ca-certificates curl gnupg python3 openssh-server iptables btrfs-progs
24
+ ```
25
+
26
+ Verify passwordless `sudo` on the host:
27
+
28
+ ```sh title="HOST: Verify passwordless sudo"
29
+ sudo -n true && echo "Passwordless sudo is functional"
30
+ ```
31
+
32
+ </Step>
33
+
34
+ <Step>
35
+
36
+ ### Clone repository and install controller CLI
37
+
38
+ On your controller machine, clone the repository and install the `raft` CLI tool using `uv`:
39
+
40
+ ```sh title="CONTROLLER: Clone repository and install CLI"
41
+ git clone https://github.com/Microck/raft.git raft
42
+ cd raft
43
+ uv sync --locked
44
+ uv tool install --editable .
45
+ ```
46
+
47
+ Add `~/.local/bin` to `PATH` and verify the CLI:
48
+
49
+ ```sh title="CONTROLLER: Verify CLI installation"
50
+ export PATH="$HOME/.local/bin:$PATH"
51
+ raft --help
52
+ ```
53
+
54
+ </Step>
55
+
56
+ <Step>
57
+
58
+ ### Configure host inventory on the controller
59
+
60
+ The controller reads host definitions from `~/.config/raft/incus.json`. Initialize the configuration directory and template:
61
+
62
+ ```sh title="CONTROLLER: Initialize configuration file"
63
+ mkdir -p ~/.config/raft
64
+ cp docs/incus.example.json ~/.config/raft/incus.json
65
+ chmod 600 ~/.config/raft/incus.json
66
+ ```
67
+
68
+ Verify interactive SSH to establish known host keys:
69
+
70
+ ```sh title="CONTROLLER: Verify host SSH access"
71
+ ssh user@host-address "echo Host SSH connected successfully"
72
+ ssh -o BatchMode=yes user@host-address "echo Batch SSH functional"
73
+ ```
74
+
75
+ Edit `~/.config/raft/incus.json`. Set the `ssh` target. Leave `image` empty during initial setup:
76
+
77
+ ```json title="CONTROLLER: ~/.config/raft/incus.json"
78
+ {
79
+ "lab": {
80
+ "ssh": "user@host-address",
81
+ "image": ""
82
+ }
83
+ }
84
+ ```
85
+
86
+ Location names use letters, digits, underscores, or hyphens. The first configured location is the default for `raft new`.
87
+
88
+ </Step>
89
+
90
+ <Step>
91
+
92
+ ### Provision the dedicated host
93
+
94
+ Run the automated host deployer from your controller:
95
+
96
+ ```sh title="CONTROLLER: Provision host infrastructure"
97
+ python3 deploy/deploy.py --location lab
98
+ ```
99
+
100
+ <details>
101
+ <summary>Infrastructure created by the deployer</summary>
102
+
103
+ The deployer runs over SSH and configures the host:
104
+ 1. Installs Incus 6.0 LTS and nftables packages.
105
+ 2. Creates the isolated `raft` Incus project.
106
+ 3. Creates a 60 GiB Btrfs storage pool (`raft-data`).
107
+ 4. Creates private bridge interface `rfbr0` (`10.232.0.1/24`) with NAT.
108
+ 5. Configures nftables bridge filters to block inter-workspace traffic and restrict host access.
109
+ 6. Installs and enables `raft-network.service` and `raft-expire.timer` units.
110
+
111
+ </details>
112
+
113
+ </Step>
114
+
115
+ <Step>
116
+
117
+ ### Acquire a native workspace image
118
+
119
+ Workspaces require an unprivileged Debian 13 development image matching the host architecture:
120
+
121
+ <Tabs items={["Download release image", "Build from source"]}>
122
+
123
+ <Tab value="Download release image">
124
+
125
+ Download the release archive, checksum, and manifest from [Raft releases](https://github.com/Microck/raft/releases). The `sha256sum` check gates import so corrupted archives are rejected:
126
+
127
+ <Tabs items={["ARM64", "AMD64"]}>
128
+
129
+ <Tab value="ARM64">
130
+
131
+ ```sh title="HOST (ARM64): Download, verify, and import release image"
132
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.1.0/raft-dev-arm64.tar.gz
133
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.1.0/SHA256SUMS
134
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.1.0/manifest-arm64.json
135
+
136
+ sha256sum --ignore-missing -c SHA256SUMS && \
137
+ sudo incus --project raft image import ./raft-dev-arm64.tar.gz --alias raft-dev
138
+ ```
139
+
140
+ </Tab>
141
+
142
+ <Tab value="AMD64">
143
+
144
+ ```sh title="HOST (AMD64): Download, verify, and import release image"
145
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.1.0/raft-dev-amd64.tar.gz
146
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.1.0/SHA256SUMS
147
+ curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.1.0/manifest-amd64.json
148
+
149
+ sha256sum --ignore-missing -c SHA256SUMS && \
150
+ sudo incus --project raft image import ./raft-dev-amd64.tar.gz --alias raft-dev
151
+ ```
152
+
153
+ </Tab>
154
+
155
+ </Tabs>
156
+
157
+ </Tab>
158
+
159
+ <Tab value="Build from source">
160
+
161
+ Run the build script from your controller. It pulls Debian 13 to the host, installs development runtimes and Docker, and publishes alias `raft-dev`:
162
+
163
+ ```sh title="CONTROLLER: Build native development image"
164
+ python3 deploy/build-image.py --location lab
165
+ ```
166
+
167
+ <Callout type="warn" title="Do not pin the base image fingerprint">
168
+ During the build, the script downloads an upstream Debian base image and logs its intermediate fingerprint. Do not copy that initial fingerprint; retrieve and pin the final `raft-dev` image fingerprint in the next step.
169
+ </Callout>
170
+
171
+ </Tab>
172
+
173
+ </Tabs>
174
+
175
+ </Step>
176
+
177
+ <Step>
178
+
179
+ ### Retrieve and pin the image fingerprint
180
+
181
+ Query the published `raft-dev` image on the host:
182
+
183
+ ```sh title="HOST: Retrieve published image fingerprint"
184
+ sudo incus --project raft image list raft-dev --format json | python3 -c '
185
+ import json, sys
186
+ data = json.load(sys.stdin)
187
+ print(next(img["fingerprint"] for img in data if any(a.get("name") == "raft-dev" for a in img.get("aliases", []))))
188
+ '
189
+ ```
190
+
191
+ Copy the 64-character hex output into `image` in `~/.config/raft/incus.json` on the controller:
192
+
193
+ ```json title="CONTROLLER: ~/.config/raft/incus.json"
194
+ {
195
+ "lab": {
196
+ "ssh": "user@host-address",
197
+ "image": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
198
+ }
199
+ }
200
+ ```
201
+
202
+ Replace the example string with your actual fingerprint. Raft requires this immutable fingerprint to prevent unexpected image mutations when creating workspaces.
203
+
204
+ </Step>
205
+
206
+ <Step>
207
+
208
+ ### Check host status with doctor
209
+
210
+ Run `raft doctor` on your controller to inspect host readiness:
211
+
212
+ ```sh title="CONTROLLER: Run doctor diagnostics"
213
+ raft doctor
214
+ ```
215
+
216
+ Check that Incus, the network service, and the expiration timer are active. For the full report contents, see [Controller diagnostics](./guides/troubleshooting.mdx#controller-diagnostics).
217
+
218
+ </Step>
219
+
220
+ </Steps>
221
+
222
+ ## Next step
223
+
224
+ Proceed to [First working box](./first-box.mdx) to launch, verify, and manage your first workspace.
@@ -0,0 +1,48 @@
1
+ ---
2
+ title: Publishing
3
+ ---
4
+
5
+ # Publishing a clean source release
6
+
7
+ Public releases must contain only reusable code, examples, and documentation. Keep controller configuration, SSH keys, instance archives, journals, and private reports outside the repository.
8
+
9
+ ## Release assets and image publishing
10
+
11
+ Release `main` through the `native images` workflow with a semantic `release_tag`, such as `v0.2.0`. The tag must match both Python and npm versions. Clean upstream Debian bases provide the images.
12
+
13
+ The workflow builds and inspects Python and npm packages, tests their installed executables outside the checkout, and publishes `@microck/raft` using the repository's `NPM_TOKEN` secret with npm provenance. Only then does it create the GitHub release with the wheel, source archive, npm tarball and verified image assets. Published versions are immutable; after partial publication, fix the failure and use a new patch version.
14
+
15
+ ```sh
16
+ gh workflow run images.yml --ref main -f release_tag=v0.2.0
17
+ ```
18
+
19
+ - **Validation gating**: Publication requires native lifecycle and full-image recovery jobs alongside fresh AMD64 KVM provisioning and reboot verification.
20
+ - **Asset immutability**: The workflow refuses existing tags and never overwrites release assets.
21
+ - **Provenance records**: Release assets include `SHA256SUMS`, architecture manifests, package inventories, and boot reports. Manifests identify the source commit, CI run, and Incus image fingerprint.
22
+
23
+ ## Repository history hygiene
24
+
25
+ Scan **all Git history**, branches, and tags before changing repository visibility. A clean working tree does not remove sensitive data from commit history.
26
+
27
+ - **Manual audit**: Secret scanners do not catch every identifier. Inspect old commits for host addresses, usernames, local paths, and private reports.
28
+ - **Fresh repository snapshot**: If commit history contains sensitive data, publish a clean snapshot in a fresh public repository. History rewrites can leave orphaned commits accessible in GitHub cache (see GitHub guidance on [removing sensitive data](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/removing-sensitive-data-from-a-repository)).
29
+ - **Keep source private**: Do not make an existing private repository public merely because its current working tree is clean.
30
+
31
+ ## Release verification commands
32
+
33
+ Run security scanning, linting, formatting checks, and package builds before tagging:
34
+
35
+ ```sh
36
+ gitleaks dir --redact --no-banner .
37
+ gitleaks git --redact --no-banner .
38
+ uv sync --locked
39
+ uvx ruff check raft.py deploy/*.py
40
+ uvx ruff format --check raft.py deploy/*.py
41
+ uv build
42
+ ```
43
+
44
+ ## Image distribution rules
45
+
46
+ Do not upload local workspace backups or development exports as release assets. Distributable images must be built from clean upstream bases, inspected for credentials, and accompanied by fingerprints, manifests, and licenses.
47
+
48
+ Review the package contents and capability table before release. State container isolation accurately without claiming VM isolation or exact Boat compatibility. Raft is an independent alternative to boat.dev and is not affiliated with that service.
@@ -0,0 +1,83 @@
1
+ ---
2
+ title: Capacity limits and planning
3
+ description: Host resource reserves, sizing profiles, advisory calculations, and admission limits.
4
+ ---
5
+
6
+ # Capacity limits and planning
7
+
8
+ Raft provides capacity inspection and allocation policies designed to keep dedicated hosts stable while running concurrent workspaces.
9
+
10
+ ## Inspecting capacity with `raft limits`
11
+
12
+ Run `raft limits` to view host resources and workspace allocation calculations:
13
+
14
+ ```sh title="CONTROLLER: View host capacity report"
15
+ raft limits
16
+
17
+ # Output JSON for programmatic inspection
18
+ raft limits --location lab --json
19
+ ```
20
+
21
+ The output displays host CPU count, total RAM, configured limits, available RAM, disk headroom, and advisory recommendations across all six sizing profiles:
22
+
23
+ ### Illustrative host sizing
24
+
25
+ Illustrative totals for 1 CPU / 2 GiB boxes on an empty host, using exact usable RAM values:
26
+
27
+ | Host CPUs | Host RAM | Recommended running | Enforced saved maximum |
28
+ | ---: | ---: | ---: | ---: |
29
+ | 2 | 8 GiB | 1 | 4 |
30
+ | 4 | 8 GiB | 3 | 4 |
31
+ | 4 | 16 GiB | 3 | 4 |
32
+ | 8 | 32 GiB | 4 | 4 |
33
+
34
+ Operating systems report less usable RAM than advertised hardware size. Near sizing boundaries, `raft limits` may calculate fewer recommended boxes. Available RAM and host processes can further reduce running capacity. These values are point-in-time advisory estimates, not hardware reservations.
35
+
36
+ ### Column definitions and algorithms
37
+
38
+ | Column | Calculation |
39
+ | --- | --- |
40
+ | Total running | `min(4, floor(CPU budget / box CPU), floor(Memory budget / box memory))` |
41
+ | More running | Bounded by remaining CPU, memory, available RAM, and active slots |
42
+ | New boxes | `min(more running, max(0, 4 - saved boxes))` |
43
+
44
+ **Total running** assumes an empty host. **More running** accounts for current allocations, `MemAvailable - Memory reserve`, and `4 - active boxes`. Active limits are not subtracted twice.
45
+
46
+ ## Sizing profiles
47
+
48
+ Select CPU and memory limits when launching with `raft new`:
49
+
50
+ - `--cpu`: `1` or `2` shared vCPUs managed by Incus (default: `1`).
51
+ - `--memory`: `1GiB`, `2GiB`, or `4GiB` cgroup limit (default: `2GiB`).
52
+
53
+ Resizing existing containers or modifying limits upon resume is not supported.
54
+
55
+ ## Host resource reserves and active states
56
+
57
+ Raft calculates advisory host safety margins:
58
+ - **CPU reserve**: 1 CPU core reserved for the host OS.
59
+ - **Memory reserve**: The larger of `2 GiB` or `10%` of host RAM.
60
+
61
+ These reserves are advisory estimates for planning, not hard cgroup locks.
62
+
63
+ All non-`Stopped` states (such as `Running` or `Frozen`) count as active allocations. Stopped containers release CPU and RAM allocations while retaining their saved slot and disk storage.
64
+
65
+ ## Enforced admission limits
66
+
67
+ <Callout type="warn" title="Hard limit: four saved boxes per location">
68
+ Raft enforces a hard limit of four saved boxes per location. Both running and stopped containers occupy storage and count against this limit.
69
+ </Callout>
70
+
71
+ Exceeding four saved boxes fails with an admission error:
72
+ ```text
73
+ Location already holds four boxes
74
+ ```
75
+
76
+ Destroy unused containers with `raft destroy <box>` to free slots. Advisory CPU and memory calculations do not block container creation or resumption.
77
+
78
+ ## Storage capacity
79
+
80
+ Every Raft host provisions a dedicated 60 GiB Btrfs pool (`raft-data`):
81
+ - **Shared pool**: All containers, snapshots, and Docker layers share this 60 GiB pool.
82
+ - **No strict per-box quotas**: Nested subvolumes prevent strict per-box quota enforcement.
83
+ - **Disk warnings**: `raft limits` warns when free space on host disk or `raft-data` falls below 5 GiB. Warnings are diagnostic and do not block operations. `raft doctor` reports storage usage without emitting 5 GiB warnings.