@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.
- package/.github/assets/raft-logo.png +0 -0
- package/LICENSE +21 -0
- package/README.md +167 -2
- package/deploy/benchmark-boot.py +150 -0
- package/deploy/build-image.py +95 -0
- package/deploy/deploy.py +176 -0
- package/deploy/incus-network.conf +4 -0
- package/deploy/install-incus.sh +22 -0
- package/deploy/raft-expire.py +62 -0
- package/deploy/raft-expire.service +8 -0
- package/deploy/raft-expire.timer +8 -0
- package/deploy/raft-network.service +12 -0
- package/deploy/raft-network.sh +40 -0
- package/deploy/verify-controls.py +241 -0
- package/deploy/verify-features.py +519 -0
- package/deploy/verify-fresh-host.py +301 -0
- package/deploy/verify-image-tests.py +139 -0
- package/deploy/verify-image.py +95 -0
- package/deploy/verify-limits.py +110 -0
- package/deploy/verify-live.py +903 -0
- package/deploy/verify-release.py +132 -0
- package/deploy/verify-tools.py +105 -0
- package/docs/benchmarks/amd64-native-ci.json +137 -0
- package/docs/benchmarks/arm64-baseline-a.json +108 -0
- package/docs/benchmarks/arm64-baseline-b.json +108 -0
- package/docs/benchmarks/arm64-candidate-a.json +109 -0
- package/docs/benchmarks/arm64-desktop-local.json +22 -0
- package/docs/benchmarks/arm64-native-ci.json +137 -0
- package/docs/boat-capabilities.md +64 -0
- package/docs/boat-parity-design.md +26 -0
- package/docs/boot-performance.mdx +102 -0
- package/docs/contract.md +93 -0
- package/docs/first-box.mdx +86 -0
- package/docs/guides/background-jobs.mdx +95 -0
- package/docs/guides/backup-recovery.mdx +111 -0
- package/docs/guides/desktop-services.mdx +90 -0
- package/docs/guides/docker.mdx +84 -0
- package/docs/guides/files.mdx +91 -0
- package/docs/guides/long-running-services.mdx +109 -0
- package/docs/guides/meta.json +18 -0
- package/docs/guides/project-setup.mdx +112 -0
- package/docs/guides/scripting-ci.mdx +133 -0
- package/docs/guides/snapshots-forks.mdx +107 -0
- package/docs/guides/troubleshooting.mdx +152 -0
- package/docs/guides/upgrades.mdx +113 -0
- package/docs/incus.example.json +3 -0
- package/docs/index.mdx +40 -0
- package/docs/meta.json +21 -0
- package/docs/operations.mdx +239 -0
- package/docs/publishing.md +48 -0
- package/docs/reference/capacity-limits.mdx +92 -0
- package/docs/reference/cli.md +288 -0
- package/docs/reference/configuration.mdx +77 -0
- package/docs/reference/meta.json +10 -0
- package/docs/releases/0.2.0.md +22 -0
- package/docs/releases/0.2.1.md +14 -0
- package/docs/requirements.mdx +47 -0
- package/docs/reviews/code-review-2026-10-06.md +168 -0
- package/docs/site-development.md +80 -0
- package/docs/verification.mdx +237 -0
- package/images/incus/build.sh +57 -0
- package/images/incus/desktop.sh +21 -0
- package/images/incus/raft-desktop.service +7 -0
- package/package.json +46 -4
- package/raft.py +961 -0
- package/raft_files.py +134 -0
- package/skills/raft-cli/SKILL.md +117 -0
|
@@ -0,0 +1,239 @@
|
|
|
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
|
+
### Install the CLI and obtain deployment scripts
|
|
37
|
+
|
|
38
|
+
On your Linux controller with Python 3.11.8+, install the published CLI:
|
|
39
|
+
|
|
40
|
+
```sh title="CONTROLLER: Install published CLI"
|
|
41
|
+
npm install -g @microck/raft@0.2.0
|
|
42
|
+
raft --version
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Installation does not configure hosts or download images. Clone the matching
|
|
46
|
+
release to obtain the deployment scripts and example configuration:
|
|
47
|
+
|
|
48
|
+
```sh title="CONTROLLER: Obtain deployment scripts"
|
|
49
|
+
git clone --branch v0.2.0 https://github.com/Microck/raft.git raft
|
|
50
|
+
cd raft
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Alternatively, install from that checkout using `uv` instead of npm:
|
|
54
|
+
|
|
55
|
+
```sh title="CONTROLLER: Clone repository and install CLI"
|
|
56
|
+
uv sync --locked
|
|
57
|
+
uv tool install --editable .
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
For the `uv` installation, add `~/.local/bin` to `PATH` and verify the CLI:
|
|
61
|
+
|
|
62
|
+
```sh title="CONTROLLER: Verify CLI installation"
|
|
63
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
64
|
+
raft --help
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
</Step>
|
|
68
|
+
|
|
69
|
+
<Step>
|
|
70
|
+
|
|
71
|
+
### Configure host inventory on the controller
|
|
72
|
+
|
|
73
|
+
The controller reads host definitions from `~/.config/raft/incus.json`. Initialize the configuration directory and template:
|
|
74
|
+
|
|
75
|
+
```sh title="CONTROLLER: Initialize configuration file"
|
|
76
|
+
mkdir -p ~/.config/raft
|
|
77
|
+
cp docs/incus.example.json ~/.config/raft/incus.json
|
|
78
|
+
chmod 600 ~/.config/raft/incus.json
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Verify interactive SSH to establish known host keys:
|
|
82
|
+
|
|
83
|
+
```sh title="CONTROLLER: Verify host SSH access"
|
|
84
|
+
ssh user@host-address "echo Host SSH connected successfully"
|
|
85
|
+
ssh -o BatchMode=yes user@host-address "echo Batch SSH functional"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Edit `~/.config/raft/incus.json`. Set the `ssh` target. Leave `image` empty during initial setup:
|
|
89
|
+
|
|
90
|
+
```json title="CONTROLLER: ~/.config/raft/incus.json"
|
|
91
|
+
{
|
|
92
|
+
"lab": {
|
|
93
|
+
"ssh": "user@host-address",
|
|
94
|
+
"image": ""
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Location names use letters, digits, underscores, or hyphens. The first configured location is the default for `raft new`.
|
|
100
|
+
|
|
101
|
+
</Step>
|
|
102
|
+
|
|
103
|
+
<Step>
|
|
104
|
+
|
|
105
|
+
### Provision the dedicated host
|
|
106
|
+
|
|
107
|
+
Run the automated host deployer from your controller:
|
|
108
|
+
|
|
109
|
+
```sh title="CONTROLLER: Provision host infrastructure"
|
|
110
|
+
python3 deploy/deploy.py --location lab
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
<details>
|
|
114
|
+
<summary>Infrastructure created by the deployer</summary>
|
|
115
|
+
|
|
116
|
+
The deployer runs over SSH and configures the host:
|
|
117
|
+
1. Installs Incus 6.0 LTS and nftables packages.
|
|
118
|
+
2. Creates the isolated `raft` Incus project.
|
|
119
|
+
3. Creates a 60 GiB Btrfs storage pool (`raft-data`).
|
|
120
|
+
4. Creates private bridge interface `rfbr0` (`10.232.0.1/24`) with NAT.
|
|
121
|
+
5. Configures nftables bridge filters to block inter-workspace traffic and restrict host access.
|
|
122
|
+
6. Installs and enables `raft-network.service` and `raft-expire.timer` units.
|
|
123
|
+
|
|
124
|
+
</details>
|
|
125
|
+
|
|
126
|
+
</Step>
|
|
127
|
+
|
|
128
|
+
<Step>
|
|
129
|
+
|
|
130
|
+
### Acquire a native workspace image
|
|
131
|
+
|
|
132
|
+
Workspaces require an unprivileged Debian 13 development image matching the host architecture:
|
|
133
|
+
|
|
134
|
+
<Tabs items={["Download release image", "Build from source"]}>
|
|
135
|
+
|
|
136
|
+
<Tab value="Download release image">
|
|
137
|
+
|
|
138
|
+
Download the release archive, checksum, and manifest from [Raft releases](https://github.com/Microck/raft/releases). These examples pin `v0.2.0`, matching the CLI and deployment checkout above. The `sha256sum` check gates import so corrupted archives are rejected:
|
|
139
|
+
|
|
140
|
+
<Tabs items={["ARM64", "AMD64"]}>
|
|
141
|
+
|
|
142
|
+
<Tab value="ARM64">
|
|
143
|
+
|
|
144
|
+
```sh title="HOST (ARM64): Download, verify, and import release image"
|
|
145
|
+
curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/raft-dev-arm64.tar.gz
|
|
146
|
+
curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/SHA256SUMS
|
|
147
|
+
curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/manifest-arm64.json
|
|
148
|
+
|
|
149
|
+
sha256sum --ignore-missing -c SHA256SUMS && \
|
|
150
|
+
sudo incus --project raft image import ./raft-dev-arm64.tar.gz --alias raft-dev
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
</Tab>
|
|
154
|
+
|
|
155
|
+
<Tab value="AMD64">
|
|
156
|
+
|
|
157
|
+
```sh title="HOST (AMD64): Download, verify, and import release image"
|
|
158
|
+
curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/raft-dev-amd64.tar.gz
|
|
159
|
+
curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/SHA256SUMS
|
|
160
|
+
curl -fsSL -O https://github.com/Microck/raft/releases/download/v0.2.0/manifest-amd64.json
|
|
161
|
+
|
|
162
|
+
sha256sum --ignore-missing -c SHA256SUMS && \
|
|
163
|
+
sudo incus --project raft image import ./raft-dev-amd64.tar.gz --alias raft-dev
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
</Tab>
|
|
167
|
+
|
|
168
|
+
</Tabs>
|
|
169
|
+
|
|
170
|
+
</Tab>
|
|
171
|
+
|
|
172
|
+
<Tab value="Build from source">
|
|
173
|
+
|
|
174
|
+
Run the build script from your controller. It pulls Debian 13 to the host, installs development runtimes and Docker, and publishes alias `raft-dev`:
|
|
175
|
+
|
|
176
|
+
```sh title="CONTROLLER: Build native development image"
|
|
177
|
+
python3 deploy/build-image.py --location lab
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
<Callout type="warn" title="Do not pin the base image fingerprint">
|
|
181
|
+
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.
|
|
182
|
+
</Callout>
|
|
183
|
+
|
|
184
|
+
</Tab>
|
|
185
|
+
|
|
186
|
+
</Tabs>
|
|
187
|
+
|
|
188
|
+
</Step>
|
|
189
|
+
|
|
190
|
+
<Step>
|
|
191
|
+
|
|
192
|
+
### Retrieve and pin the image fingerprint
|
|
193
|
+
|
|
194
|
+
Query the published `raft-dev` image on the host:
|
|
195
|
+
|
|
196
|
+
```sh title="HOST: Retrieve published image fingerprint"
|
|
197
|
+
sudo incus --project raft image list raft-dev --format json | python3 -c '
|
|
198
|
+
import json, sys
|
|
199
|
+
data = json.load(sys.stdin)
|
|
200
|
+
print(next(img["fingerprint"] for img in data if any(a.get("name") == "raft-dev" for a in img.get("aliases", []))))
|
|
201
|
+
'
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Copy the 64-character hex output into `image` in `~/.config/raft/incus.json` on the controller:
|
|
205
|
+
|
|
206
|
+
```json title="CONTROLLER: ~/.config/raft/incus.json"
|
|
207
|
+
{
|
|
208
|
+
"lab": {
|
|
209
|
+
"ssh": "user@host-address",
|
|
210
|
+
"image": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Replace the example string with your actual fingerprint. Raft requires this immutable fingerprint to prevent unexpected image mutations when creating workspaces.
|
|
216
|
+
|
|
217
|
+
</Step>
|
|
218
|
+
|
|
219
|
+
<Step>
|
|
220
|
+
|
|
221
|
+
### Check host status with doctor
|
|
222
|
+
|
|
223
|
+
Run `raft doctor` on your controller to inspect host readiness:
|
|
224
|
+
|
|
225
|
+
```sh title="CONTROLLER: Run doctor diagnostics"
|
|
226
|
+
raft doctor
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
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).
|
|
230
|
+
|
|
231
|
+
</Step>
|
|
232
|
+
|
|
233
|
+
</Steps>
|
|
234
|
+
|
|
235
|
+
## Next step
|
|
236
|
+
|
|
237
|
+
Proceed to [First working box](./first-box.mdx) to launch, verify, and manage your first workspace.
|
|
238
|
+
|
|
239
|
+
For later releases, follow [Upgrades and image changes](./guides/upgrades.mdx).
|
|
@@ -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,92 @@
|
|
|
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
|
+
Change supported CPU/RAM limits when resuming a stopped box or creating a fork:
|
|
54
|
+
|
|
55
|
+
```sh
|
|
56
|
+
raft resume "$box" --ttl 1800 --cpu 2 --memory 4GiB
|
|
57
|
+
copy=$(raft fork "$box" --ttl 1800 --memory 2GiB)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Resume requires a stopped box; fork requires a stopped source. Omitted limits
|
|
61
|
+
stay unchanged on resume and inherit the source limits on fork. Live resizing is
|
|
62
|
+
not exposed by the CLI. See [Snapshots and forks](../guides/snapshots-forks.mdx).
|
|
63
|
+
|
|
64
|
+
## Host resource reserves and active states
|
|
65
|
+
|
|
66
|
+
Raft calculates advisory host safety margins:
|
|
67
|
+
- **CPU reserve**: 1 CPU core reserved for the host OS.
|
|
68
|
+
- **Memory reserve**: The larger of `2 GiB` or `10%` of host RAM.
|
|
69
|
+
|
|
70
|
+
These reserves are advisory estimates for planning, not hard cgroup locks.
|
|
71
|
+
|
|
72
|
+
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.
|
|
73
|
+
|
|
74
|
+
## Enforced admission limits
|
|
75
|
+
|
|
76
|
+
<Callout type="warn" title="Hard limit: four saved boxes per location">
|
|
77
|
+
Raft enforces a hard limit of four saved boxes per location. Both running and stopped containers occupy storage and count against this limit.
|
|
78
|
+
</Callout>
|
|
79
|
+
|
|
80
|
+
Exceeding four saved boxes fails with an admission error:
|
|
81
|
+
```text
|
|
82
|
+
Location already holds four boxes
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Destroy unused containers with `raft destroy <box>` to free slots. Advisory CPU and memory calculations do not block container creation or resumption.
|
|
86
|
+
|
|
87
|
+
## Storage capacity
|
|
88
|
+
|
|
89
|
+
Every Raft host provisions a dedicated 60 GiB Btrfs pool (`raft-data`):
|
|
90
|
+
- **Shared pool**: All containers, snapshots, and Docker layers share this 60 GiB pool.
|
|
91
|
+
- **No strict per-box quotas**: Nested subvolumes prevent strict per-box quota enforcement.
|
|
92
|
+
- **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.
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: CLI command reference
|
|
3
|
+
description: Complete command-line reference for the Raft controller CLI.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# CLI command reference
|
|
7
|
+
|
|
8
|
+
Command-line reference for the `raft` controller tool.
|
|
9
|
+
|
|
10
|
+
Syntax:
|
|
11
|
+
`raft <subcommand> [options] [arguments]`
|
|
12
|
+
|
|
13
|
+
`raft --help` lists commands, `raft <subcommand> --help` lists its arguments,
|
|
14
|
+
and `raft --version` prints the installed version without loading configuration.
|
|
15
|
+
Output is command-specific; see [Scripting and CI](../guides/scripting-ci.mdx).
|
|
16
|
+
|
|
17
|
+
Workspaces are referenced by location-qualified handles formatted as `<location>:<instance-name>` (for example `lab:rf-a1b2c3d4e5f60718`).
|
|
18
|
+
|
|
19
|
+
## Command index
|
|
20
|
+
|
|
21
|
+
| Task | Commands |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| Workspace lifecycle | [`new`](#new), [`stop`](#stop), [`resume`](#resume), [`extend`](#extend), [`destroy`](#destroy) |
|
|
24
|
+
| Execution and inspection | [`exec`](#exec), [`ssh`](#ssh), [`info`](#info), [`usage`](#usage) |
|
|
25
|
+
| Background job | [`status`](#status), [`logs`](#logs), [`cancel`](#cancel) |
|
|
26
|
+
| File transfer | [`upload`](#upload), [`download`](#download) |
|
|
27
|
+
| Snapshots and cloning | [`snapshot`](#snapshot), [`snapshots`](#snapshots), [`restore`](#restore), [`snapshot-delete`](#snapshot-delete), [`fork`](#fork) |
|
|
28
|
+
| Port forwarding and desktop | [`forward`](#forward), [`desktop`](#desktop) |
|
|
29
|
+
| Backup and recovery | [`backup`](#backup), [`recover`](#recover) |
|
|
30
|
+
| Host inspection and maintenance | [`limits`](#limits), [`doctor`](#doctor), [`list`](#list), [`gc`](#gc), [`prune`](#prune) |
|
|
31
|
+
|
|
32
|
+
## Workspace lifecycle commands
|
|
33
|
+
|
|
34
|
+
### `new`
|
|
35
|
+
|
|
36
|
+
Create and launch a new workspace container.
|
|
37
|
+
|
|
38
|
+
`raft new [--location LOCATION] [--ttl TTL] [--cpu {1,2}] [--memory {1GiB,2GiB,4GiB}] [--disposable] [--from LOCATION:BOX/SNAPSHOT]`
|
|
39
|
+
|
|
40
|
+
- `--location LOCATION`: Target host location in `~/.config/raft/incus.json`. Defaults to first configured location.
|
|
41
|
+
- `--ttl TTL`: Lifetime in seconds before automatic stop (`60` to `2592000`). Default: `600`.
|
|
42
|
+
- `--cpu {1,2}`: CPU core count managed by Incus. Default: `1`.
|
|
43
|
+
- `--memory {1GiB,2GiB,4GiB}`: Memory limit. Default: `2GiB`.
|
|
44
|
+
|
|
45
|
+
- `--disposable`: Delete the box and snapshots on stop or expiry. Off by default.
|
|
46
|
+
- `--from LOCATION:BOX/SNAPSHOT`: Copy a named snapshot on its source host instead of the configured image. Normal creation sizing applies; the source can be running. Cannot target a different location.
|
|
47
|
+
|
|
48
|
+
Returns the qualified workspace handle.
|
|
49
|
+
|
|
50
|
+
### `stop`
|
|
51
|
+
|
|
52
|
+
Stop a running workspace.
|
|
53
|
+
|
|
54
|
+
`raft stop <box>`
|
|
55
|
+
|
|
56
|
+
Terminates guest processes and stops the container. Persistent boxes retain filesystem contents, packages and snapshots. Disposable boxes are deleted, including their snapshots.
|
|
57
|
+
|
|
58
|
+
### `resume`
|
|
59
|
+
|
|
60
|
+
Start a stopped workspace with a renewed lifetime deadline.
|
|
61
|
+
|
|
62
|
+
`raft resume <box> --ttl TTL [--cpu {1,2}] [--memory {1GiB,2GiB,4GiB}]`
|
|
63
|
+
|
|
64
|
+
- `--ttl TTL`: Lifetime in seconds from resumption (`60` to `2592000`). Required.
|
|
65
|
+
|
|
66
|
+
CPU/RAM overrides on resume apply before startup. Omitted values stay unchanged.
|
|
67
|
+
A running box is rejected before changing its limits.
|
|
68
|
+
|
|
69
|
+
### `extend`
|
|
70
|
+
|
|
71
|
+
Reset the expiration deadline for a running workspace without restarting it.
|
|
72
|
+
|
|
73
|
+
`raft extend <box> --ttl TTL`
|
|
74
|
+
|
|
75
|
+
- `--ttl TTL`: Lifetime in seconds from the current host timestamp. Required.
|
|
76
|
+
|
|
77
|
+
### `destroy`
|
|
78
|
+
|
|
79
|
+
Permanently delete a workspace container and its snapshots.
|
|
80
|
+
|
|
81
|
+
`raft destroy <box>`
|
|
82
|
+
|
|
83
|
+
Stops the container if running, removes all associated Btrfs snapshots, and releases host storage.
|
|
84
|
+
|
|
85
|
+
## Execution and inspection commands
|
|
86
|
+
|
|
87
|
+
### `exec`
|
|
88
|
+
|
|
89
|
+
Execute a command inside a workspace.
|
|
90
|
+
|
|
91
|
+
`raft exec <box> [--detach] [--] <command ...>`
|
|
92
|
+
|
|
93
|
+
- `--detach`: Run in background as a guest systemd unit. Returns a job ID immediately.
|
|
94
|
+
- `<command ...>`: Command and arguments passed to `execve`. For shell features (pipes, redirects, variables), wrap with `bash -lc '...'`.
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
```sh
|
|
98
|
+
job=$(raft exec "$box" --detach -- make test)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### `ssh`
|
|
102
|
+
|
|
103
|
+
Open an interactive root terminal session inside the workspace over host SSH and Incus exec.
|
|
104
|
+
|
|
105
|
+
`raft ssh <box>`
|
|
106
|
+
|
|
107
|
+
### `info`
|
|
108
|
+
|
|
109
|
+
Display container metadata from Incus in JSON format, including status, IP addresses, resource limits, and timestamps.
|
|
110
|
+
|
|
111
|
+
`raft info <box>`
|
|
112
|
+
|
|
113
|
+
### `usage`
|
|
114
|
+
|
|
115
|
+
Display active resource consumption for a running workspace: cgroup memory, CPU microseconds, CPU affinity, and shared-pool disk space.
|
|
116
|
+
|
|
117
|
+
`raft usage <box>`
|
|
118
|
+
|
|
119
|
+
## Background job commands
|
|
120
|
+
|
|
121
|
+
`status`, `logs` and `cancel` require a job ID returned by `exec --detach` in the selected running workspace. Unknown jobs return an error.
|
|
122
|
+
|
|
123
|
+
Cancelled jobs remain inspectable while their guest journal records exist. Rotating old logs does not prevent inspection of a live job.
|
|
124
|
+
|
|
125
|
+
### `status`
|
|
126
|
+
|
|
127
|
+
Check the execution state of a detached background job.
|
|
128
|
+
|
|
129
|
+
`raft status <box> <job>`
|
|
130
|
+
|
|
131
|
+
Queries `systemctl show` inside the container and outputs `ActiveState`, `SubState`, and `ExecMainStatus`.
|
|
132
|
+
|
|
133
|
+
### `logs`
|
|
134
|
+
|
|
135
|
+
Retrieve journal output from a detached background job using `journalctl --no-pager -o cat -u <job>`.
|
|
136
|
+
|
|
137
|
+
`raft logs <box> <job>`
|
|
138
|
+
|
|
139
|
+
### `cancel`
|
|
140
|
+
|
|
141
|
+
Terminate a running detached background job using `systemctl stop <job>`.
|
|
142
|
+
|
|
143
|
+
`raft cancel <box> <job>`
|
|
144
|
+
|
|
145
|
+
## File transfer commands
|
|
146
|
+
|
|
147
|
+
### `upload`
|
|
148
|
+
|
|
149
|
+
Transfer a local file from the controller to an absolute path inside the workspace.
|
|
150
|
+
|
|
151
|
+
`raft upload <box> <source> <destination> [--recursive]`
|
|
152
|
+
|
|
153
|
+
- `<source>`: Path to local file on controller.
|
|
154
|
+
- `<destination>`: Target absolute path inside container.
|
|
155
|
+
|
|
156
|
+
### `download`
|
|
157
|
+
|
|
158
|
+
Transfer a file from an absolute path inside the workspace to the local controller.
|
|
159
|
+
|
|
160
|
+
`raft download <box> <source> <destination> [--recursive]`
|
|
161
|
+
|
|
162
|
+
- `<source>`: Absolute path to file inside container.
|
|
163
|
+
- `<destination>`: Local file destination path on controller.
|
|
164
|
+
|
|
165
|
+
`--recursive` transfers a directory tree into a new destination directory. It requires a running workspace and Python 3.11.8+ in the guest. Parent directories must exist. Existing destinations are rejected, including empty directories and symlinks. Internal relative links, hardlinks, executable files and empty directories are supported; escaping links and special files are rejected. Archives use temporary controller disk space. Single-file transfers still work on stopped boxes.
|
|
166
|
+
|
|
167
|
+
## Snapshots and cloning commands
|
|
168
|
+
|
|
169
|
+
### `snapshot`
|
|
170
|
+
|
|
171
|
+
Create a named point-in-time snapshot of a stopped workspace. Names use alphanumeric characters, underscores, or hyphens up to 64 characters.
|
|
172
|
+
|
|
173
|
+
`raft snapshot <box> <name>`
|
|
174
|
+
|
|
175
|
+
### `snapshots`
|
|
176
|
+
|
|
177
|
+
List existing snapshots and creation timestamps for a workspace.
|
|
178
|
+
|
|
179
|
+
`raft snapshots <box>`
|
|
180
|
+
|
|
181
|
+
### `snapshot-delete`
|
|
182
|
+
|
|
183
|
+
Delete one named snapshot without deleting the workspace. The box can be running.
|
|
184
|
+
|
|
185
|
+
`raft snapshot-delete <box> <name>`
|
|
186
|
+
|
|
187
|
+
### `restore`
|
|
188
|
+
|
|
189
|
+
Roll back a stopped workspace filesystem to a named snapshot using `--diskonly`, preserving container configuration and MAC address.
|
|
190
|
+
|
|
191
|
+
`raft restore <box> <name>`
|
|
192
|
+
|
|
193
|
+
### `fork`
|
|
194
|
+
|
|
195
|
+
Clone a stopped workspace into an independent container on the same host and start it.
|
|
196
|
+
|
|
197
|
+
`raft fork <box> --ttl TTL [--cpu {1,2}] [--memory {1GiB,2GiB,4GiB}] [--disposable]`
|
|
198
|
+
|
|
199
|
+
- `--ttl TTL`: Lifetime in seconds for the clone. Required.
|
|
200
|
+
|
|
201
|
+
Omitted CPU/RAM values inherit the source limits. The clone is persistent unless `--disposable` is supplied, regardless of the source policy.
|
|
202
|
+
|
|
203
|
+
Returns the qualified handle of the clone.
|
|
204
|
+
|
|
205
|
+
## Port forwarding and desktop commands
|
|
206
|
+
|
|
207
|
+
### `forward`
|
|
208
|
+
|
|
209
|
+
Open a private SSH tunnel between a guest port and controller loopback (`127.0.0.1`).
|
|
210
|
+
|
|
211
|
+
`raft forward <box> --remote REMOTE --local LOCAL`
|
|
212
|
+
|
|
213
|
+
- `--remote REMOTE`: Port number inside the workspace (bound to `0.0.0.0` or container IP).
|
|
214
|
+
- `--local LOCAL`: Port number on controller loopback (`127.0.0.1`).
|
|
215
|
+
|
|
216
|
+
Runs in foreground; press `Ctrl+C` to close.
|
|
217
|
+
|
|
218
|
+
### `desktop`
|
|
219
|
+
|
|
220
|
+
Start `raft-desktop.service` inside the container and open a private tunnel for noVNC.
|
|
221
|
+
|
|
222
|
+
`raft desktop <box> [--local LOCAL]`
|
|
223
|
+
|
|
224
|
+
- `--local LOCAL`: Controller port to bind (default: `6080`). Access at `http://127.0.0.1:<LOCAL>/vnc.html`. Press `Ctrl+C` to close.
|
|
225
|
+
|
|
226
|
+
## Backup and recovery commands
|
|
227
|
+
|
|
228
|
+
### `backup`
|
|
229
|
+
|
|
230
|
+
Export a stopped workspace and its snapshots to a compressed archive on the controller.
|
|
231
|
+
|
|
232
|
+
`raft backup <box> <destination>`
|
|
233
|
+
|
|
234
|
+
- `<destination>`: Output archive path (e.g. `./workspace.tar.gz`). Must not exist. Created with mode `0600`.
|
|
235
|
+
|
|
236
|
+
### `recover`
|
|
237
|
+
|
|
238
|
+
Import a portable backup archive onto a host as a fresh stopped workspace.
|
|
239
|
+
|
|
240
|
+
`raft recover <source> --location LOCATION`
|
|
241
|
+
|
|
242
|
+
- `<source>`: Path to archive file on controller.
|
|
243
|
+
- `--location LOCATION`: Target host location. Required.
|
|
244
|
+
|
|
245
|
+
Returns the qualified handle of the imported stopped workspace.
|
|
246
|
+
|
|
247
|
+
## Host inspection and maintenance commands
|
|
248
|
+
|
|
249
|
+
### `limits`
|
|
250
|
+
|
|
251
|
+
Inspect host resource capacity, active allocations, and recommended workspace limits.
|
|
252
|
+
|
|
253
|
+
`raft limits [--location LOCATION] [--json]`
|
|
254
|
+
|
|
255
|
+
- `--location LOCATION`: Filter report to a specific host.
|
|
256
|
+
- `--json`: Output report as JSON.
|
|
257
|
+
|
|
258
|
+
### `doctor`
|
|
259
|
+
|
|
260
|
+
Inspect host connectivity, Incus version, expiry-worker compatibility, active systemd units (`incus`, `raft-expire.timer`, `raft-network.service`), root filesystem usage, storage pool status, and saved box count across configured hosts. An incompatible worker returns a nonzero exit status and names the failing location without running expiry or modifying boxes.
|
|
261
|
+
|
|
262
|
+
`raft doctor`
|
|
263
|
+
|
|
264
|
+
### `list`
|
|
265
|
+
|
|
266
|
+
List existing workspace containers across configured hosts.
|
|
267
|
+
|
|
268
|
+
`raft list [--location LOCATION]`
|
|
269
|
+
|
|
270
|
+
Prints one JSON object per box (JSONL), with `box`, `state`, and `stopAt` fields.
|
|
271
|
+
An empty inventory prints no records. See [Scripting and CI](../guides/scripting-ci.mdx)
|
|
272
|
+
for output formats, exit codes, readiness, and failure handling.
|
|
273
|
+
|
|
274
|
+
### `gc`
|
|
275
|
+
|
|
276
|
+
Enforce deadlines immediately. Persistent boxes stop; disposable boxes are deleted.
|
|
277
|
+
|
|
278
|
+
`raft gc [--location LOCATION]`
|
|
279
|
+
|
|
280
|
+
### `prune`
|
|
281
|
+
|
|
282
|
+
Preview deletion of persistent stopped boxes older than the selected number of days.
|
|
283
|
+
|
|
284
|
+
`raft prune --older-than DAYS [--location LOCATION] [--yes]`
|
|
285
|
+
|
|
286
|
+
Days must be at least one. Without `--yes`, this only prints JSONL candidates. With `--yes`, it deletes matching boxes and their snapshots under the host lock. Running boxes and boxes without a recorded stop time are excluded. Stop timestamps come from Raft stop/expiry, not box creation. No automatic retention policy is enabled.
|
|
287
|
+
|
|
288
|
+
Disposable creation/fork and prune require the current expiry worker installed through the deployer. A mismatch fails with deployment guidance; the CLI does not install or adapt an older worker.
|