@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,109 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hostArchitecture": "aarch64",
|
|
3
|
+
"hostCpus": 2,
|
|
4
|
+
"guestCpus": 1,
|
|
5
|
+
"guestMemory": "2GiB",
|
|
6
|
+
"sshRoundTripSeconds": 0.372,
|
|
7
|
+
"scope": "Cached local image; includes controller CLI and SSH; no host reboot or download",
|
|
8
|
+
"samples": [
|
|
9
|
+
{
|
|
10
|
+
"create": {
|
|
11
|
+
"cliReturnSeconds": 1.0,
|
|
12
|
+
"commandReadySeconds": 2.197,
|
|
13
|
+
"dockerReadySeconds": 3.939,
|
|
14
|
+
"desktopStartSeconds": 2.814
|
|
15
|
+
},
|
|
16
|
+
"resume": {
|
|
17
|
+
"cliReturnSeconds": 1.124,
|
|
18
|
+
"commandReadySeconds": 2.208,
|
|
19
|
+
"dockerReadySeconds": 3.792,
|
|
20
|
+
"desktopStartSeconds": 2.165
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"create": {
|
|
25
|
+
"cliReturnSeconds": 1.447,
|
|
26
|
+
"commandReadySeconds": 2.803,
|
|
27
|
+
"dockerReadySeconds": 4.58,
|
|
28
|
+
"desktopStartSeconds": 2.375
|
|
29
|
+
},
|
|
30
|
+
"resume": {
|
|
31
|
+
"cliReturnSeconds": 1.275,
|
|
32
|
+
"commandReadySeconds": 2.381,
|
|
33
|
+
"dockerReadySeconds": 4.123,
|
|
34
|
+
"desktopStartSeconds": 2.449
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"create": {
|
|
39
|
+
"cliReturnSeconds": 1.19,
|
|
40
|
+
"commandReadySeconds": 2.234,
|
|
41
|
+
"dockerReadySeconds": 4.302,
|
|
42
|
+
"desktopStartSeconds": 2.607
|
|
43
|
+
},
|
|
44
|
+
"resume": {
|
|
45
|
+
"cliReturnSeconds": 1.284,
|
|
46
|
+
"commandReadySeconds": 2.376,
|
|
47
|
+
"dockerReadySeconds": 3.973,
|
|
48
|
+
"desktopStartSeconds": 2.082
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
],
|
|
52
|
+
"imageVolumeCachedAtStart": true,
|
|
53
|
+
"guestArchitecture": "arm64",
|
|
54
|
+
"imageSizeBytes": 1920185312,
|
|
55
|
+
"summary": {
|
|
56
|
+
"create": {
|
|
57
|
+
"cliReturnSeconds": {
|
|
58
|
+
"median": 1.19,
|
|
59
|
+
"min": 1.0,
|
|
60
|
+
"max": 1.447,
|
|
61
|
+
"p95": 1.447
|
|
62
|
+
},
|
|
63
|
+
"commandReadySeconds": {
|
|
64
|
+
"median": 2.234,
|
|
65
|
+
"min": 2.197,
|
|
66
|
+
"max": 2.803,
|
|
67
|
+
"p95": 2.803
|
|
68
|
+
},
|
|
69
|
+
"dockerReadySeconds": {
|
|
70
|
+
"median": 4.302,
|
|
71
|
+
"min": 3.939,
|
|
72
|
+
"max": 4.58,
|
|
73
|
+
"p95": 4.58
|
|
74
|
+
},
|
|
75
|
+
"desktopStartSeconds": {
|
|
76
|
+
"median": 2.607,
|
|
77
|
+
"min": 2.375,
|
|
78
|
+
"max": 2.814,
|
|
79
|
+
"p95": 2.814
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"resume": {
|
|
83
|
+
"cliReturnSeconds": {
|
|
84
|
+
"median": 1.275,
|
|
85
|
+
"min": 1.124,
|
|
86
|
+
"max": 1.284,
|
|
87
|
+
"p95": 1.284
|
|
88
|
+
},
|
|
89
|
+
"commandReadySeconds": {
|
|
90
|
+
"median": 2.376,
|
|
91
|
+
"min": 2.208,
|
|
92
|
+
"max": 2.381,
|
|
93
|
+
"p95": 2.381
|
|
94
|
+
},
|
|
95
|
+
"dockerReadySeconds": {
|
|
96
|
+
"median": 3.973,
|
|
97
|
+
"min": 3.792,
|
|
98
|
+
"max": 4.123,
|
|
99
|
+
"p95": 4.123
|
|
100
|
+
},
|
|
101
|
+
"desktopStartSeconds": {
|
|
102
|
+
"median": 2.165,
|
|
103
|
+
"min": 2.082,
|
|
104
|
+
"max": 2.449,
|
|
105
|
+
"p95": 2.449
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"method": "Guest-local systemctl start to HTTP 200 and RFB greeting; excludes SSH overhead",
|
|
3
|
+
"guestCpus": 1,
|
|
4
|
+
"guestMemory": "2GiB",
|
|
5
|
+
"restartsPerImage": 3,
|
|
6
|
+
"baseline": {
|
|
7
|
+
"samplesSeconds": [
|
|
8
|
+
2.275,
|
|
9
|
+
1.423,
|
|
10
|
+
1.431
|
|
11
|
+
],
|
|
12
|
+
"medianSeconds": 1.431
|
|
13
|
+
},
|
|
14
|
+
"candidate": {
|
|
15
|
+
"samplesSeconds": [
|
|
16
|
+
1.042,
|
|
17
|
+
0.525,
|
|
18
|
+
0.537
|
|
19
|
+
],
|
|
20
|
+
"medianSeconds": 0.537
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hostArchitecture": "aarch64",
|
|
3
|
+
"hostCpus": 4,
|
|
4
|
+
"guestCpus": 1,
|
|
5
|
+
"guestMemory": "2GiB",
|
|
6
|
+
"sshRoundTripSeconds": 0.187,
|
|
7
|
+
"scope": "Cached local image; includes controller CLI and SSH; no host reboot or download",
|
|
8
|
+
"samples": [
|
|
9
|
+
{
|
|
10
|
+
"create": {
|
|
11
|
+
"cliReturnSeconds": 0.425,
|
|
12
|
+
"commandReadySeconds": 0.866,
|
|
13
|
+
"dockerReadySeconds": 2.28,
|
|
14
|
+
"desktopStartSeconds": 1.136
|
|
15
|
+
},
|
|
16
|
+
"resume": {
|
|
17
|
+
"cliReturnSeconds": 0.549,
|
|
18
|
+
"commandReadySeconds": 0.98,
|
|
19
|
+
"dockerReadySeconds": 2.335,
|
|
20
|
+
"desktopStartSeconds": 1.116
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"create": {
|
|
25
|
+
"cliReturnSeconds": 0.427,
|
|
26
|
+
"commandReadySeconds": 0.871,
|
|
27
|
+
"dockerReadySeconds": 2.324,
|
|
28
|
+
"desktopStartSeconds": 1.12
|
|
29
|
+
},
|
|
30
|
+
"resume": {
|
|
31
|
+
"cliReturnSeconds": 0.556,
|
|
32
|
+
"commandReadySeconds": 0.988,
|
|
33
|
+
"dockerReadySeconds": 2.626,
|
|
34
|
+
"desktopStartSeconds": 1.119
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"create": {
|
|
39
|
+
"cliReturnSeconds": 0.427,
|
|
40
|
+
"commandReadySeconds": 0.869,
|
|
41
|
+
"dockerReadySeconds": 1.92,
|
|
42
|
+
"desktopStartSeconds": 1.115
|
|
43
|
+
},
|
|
44
|
+
"resume": {
|
|
45
|
+
"cliReturnSeconds": 0.524,
|
|
46
|
+
"commandReadySeconds": 0.95,
|
|
47
|
+
"dockerReadySeconds": 2.783,
|
|
48
|
+
"desktopStartSeconds": 1.087
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"create": {
|
|
53
|
+
"cliReturnSeconds": 0.4,
|
|
54
|
+
"commandReadySeconds": 0.825,
|
|
55
|
+
"dockerReadySeconds": 2.795,
|
|
56
|
+
"desktopStartSeconds": 1.102
|
|
57
|
+
},
|
|
58
|
+
"resume": {
|
|
59
|
+
"cliReturnSeconds": 0.526,
|
|
60
|
+
"commandReadySeconds": 0.951,
|
|
61
|
+
"dockerReadySeconds": 2.817,
|
|
62
|
+
"desktopStartSeconds": 1.304
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"create": {
|
|
67
|
+
"cliReturnSeconds": 0.407,
|
|
68
|
+
"commandReadySeconds": 0.832,
|
|
69
|
+
"dockerReadySeconds": 1.993,
|
|
70
|
+
"desktopStartSeconds": 1.097
|
|
71
|
+
},
|
|
72
|
+
"resume": {
|
|
73
|
+
"cliReturnSeconds": 0.516,
|
|
74
|
+
"commandReadySeconds": 0.943,
|
|
75
|
+
"dockerReadySeconds": 2.141,
|
|
76
|
+
"desktopStartSeconds": 1.101
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"imageVolumeCachedAtStart": true,
|
|
81
|
+
"guestArchitecture": "arm64",
|
|
82
|
+
"imageSizeBytes": 1921699952,
|
|
83
|
+
"summary": {
|
|
84
|
+
"create": {
|
|
85
|
+
"cliReturnSeconds": {
|
|
86
|
+
"median": 0.425,
|
|
87
|
+
"min": 0.4,
|
|
88
|
+
"max": 0.427,
|
|
89
|
+
"p95": 0.427
|
|
90
|
+
},
|
|
91
|
+
"commandReadySeconds": {
|
|
92
|
+
"median": 0.866,
|
|
93
|
+
"min": 0.825,
|
|
94
|
+
"max": 0.871,
|
|
95
|
+
"p95": 0.871
|
|
96
|
+
},
|
|
97
|
+
"dockerReadySeconds": {
|
|
98
|
+
"median": 2.28,
|
|
99
|
+
"min": 1.92,
|
|
100
|
+
"max": 2.795,
|
|
101
|
+
"p95": 2.795
|
|
102
|
+
},
|
|
103
|
+
"desktopStartSeconds": {
|
|
104
|
+
"median": 1.115,
|
|
105
|
+
"min": 1.097,
|
|
106
|
+
"max": 1.136,
|
|
107
|
+
"p95": 1.136
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"resume": {
|
|
111
|
+
"cliReturnSeconds": {
|
|
112
|
+
"median": 0.526,
|
|
113
|
+
"min": 0.516,
|
|
114
|
+
"max": 0.556,
|
|
115
|
+
"p95": 0.556
|
|
116
|
+
},
|
|
117
|
+
"commandReadySeconds": {
|
|
118
|
+
"median": 0.951,
|
|
119
|
+
"min": 0.943,
|
|
120
|
+
"max": 0.988,
|
|
121
|
+
"p95": 0.988
|
|
122
|
+
},
|
|
123
|
+
"dockerReadySeconds": {
|
|
124
|
+
"median": 2.626,
|
|
125
|
+
"min": 2.141,
|
|
126
|
+
"max": 2.817,
|
|
127
|
+
"p95": 2.817
|
|
128
|
+
},
|
|
129
|
+
"desktopStartSeconds": {
|
|
130
|
+
"median": 1.116,
|
|
131
|
+
"min": 1.087,
|
|
132
|
+
"max": 1.304,
|
|
133
|
+
"p95": 1.304
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Boat feature parity
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Boat capability coverage
|
|
6
|
+
|
|
7
|
+
Raft is an independent open-source alternative to [boat.dev](https://boat.dev).
|
|
8
|
+
✅ supported · ❌ unavailable through the compared product · ❓ not established by Boat's documentation.
|
|
9
|
+
|
|
10
|
+
A shared checkmark means both offer the workflow, not identical behavior or isolation.
|
|
11
|
+
|
|
12
|
+
Tests verify Raft behavior, not hosted Boat infrastructure. See [verification](./verification.mdx) for evidence.
|
|
13
|
+
|
|
14
|
+
| Capability | Boat | Raft | Raft limits and differences |
|
|
15
|
+
| --- | :---: | :---: | --- |
|
|
16
|
+
| Self-hosted runtime on operator hardware | ❌ | ✅ | Boat is a hosted service. Raft runs Incus on hosts controlled by the operator |
|
|
17
|
+
| Native ARM64 workspaces | ❌ | ✅ | Boat documents all machine sizes as x86_64; Raft also supports native ARM64 |
|
|
18
|
+
| Create/list/inspect/delete | ✅ | ✅ | Qualified handles, explicit locations, native Incus metadata. Single operator; no organization/account API |
|
|
19
|
+
| Stop/resume | ✅ | ✅ | Stops processes, retains disk; resume requires TTL. Shared-kernel containers, not Boat VMs |
|
|
20
|
+
| Running lifetime | ✅ | ✅ | Host-enforced TTL; extend resets deadline without restart. Long locked transactions delay expiry checks |
|
|
21
|
+
| Sizing | ✅ | ✅ | 1/2 CPUs and 1/2/4 GiB choices. Resume/fork support sizing overrides; Boat offers larger sizes |
|
|
22
|
+
| Host capacity recommendations | ❌ | ✅ | Raft reports host CPU/RAM budgets, active allocations, saved slots and disk headroom. Boat limits describe hosted plan credits and concurrency, not operator-host sizing. Raft enforces four saved boxes per host |
|
|
23
|
+
| Hosted credit and start-rate limits | ✅ | ❌ | Boat reports plan credits, concurrency and start rates. Raft has no billing service |
|
|
24
|
+
| Resize on resume/fork | ✅ | ✅ | Optional CPU/RAM overrides, retaining omitted limits |
|
|
25
|
+
| Usage | ✅ | ✅ | Cgroup memory, CPU counters, shared pool space. No billing meter or per-box disk accounting |
|
|
26
|
+
| Root terminal | ✅ | ✅ | PTY over host SSH and native Incus exec. No directly exposed guest SSH endpoint |
|
|
27
|
+
| Synchronous exec | ✅ | ✅ | Literal argv and preserved exit code. Shell syntax requires an explicit shell |
|
|
28
|
+
| Detached exec | ✅ | ✅ | Guest systemd unit, status, logs, cancellation. No process checkpoint or restart after stop |
|
|
29
|
+
| File transfer | ✅ | ✅ | Binary file upload/download. Recursive tree transfer for running boxes; no directory merges |
|
|
30
|
+
| Named snapshots | ✅ | ✅ | Create/list/restore with stopped source, checked inside the lifecycle lock. No running snapshot consistency guarantee |
|
|
31
|
+
| Deploy/delete named snapshots | ✅ | ✅ | `new --from LOCATION:BOX/SNAPSHOT` and `snapshot-delete`; templates stay on their source host |
|
|
32
|
+
| Fork | ✅ | ✅ | Same-host stopped filesystem copy. Copies filesystem secrets; no cross-host fork command |
|
|
33
|
+
| Portable recovery | ❓ | ✅ | Raft exports/imports whole native instances including snapshots. Boat documents snapshot file downloads; importing a portable whole-instance archive is not established. Raft backup storage is manual; no scheduled off-host uploads |
|
|
34
|
+
| Docker | ✅ | ✅ | Guest daemon with real image builds. Container nesting uses host kernel |
|
|
35
|
+
| Native development images | ✅ | ✅ | Boat: Ubuntu x86_64 machines. Raft: published native Debian ARM64/AMD64 images with pinned fingerprints and clean-archive inspection |
|
|
36
|
+
| Development tools | ✅ | ✅ | 31 real developer-user checks. Debian/Chromium, not identical Ubuntu/Chrome versions; VS Code, Ghostty and GitHub CLI are not bundled; version-manager coverage differs |
|
|
37
|
+
| Private forward | ✅ | ✅ | Foreground authenticated SSH tunnel to controller loopback. Guest service must listen on its managed NIC; no reverse forwarding |
|
|
38
|
+
| Public workspace hosting | ✅ | ❌ | Missing. Deferred; requires a defined ingress and access policy |
|
|
39
|
+
| Desktop | ✅ | ✅ | On-demand noVNC, framebuffer, pointer and browser transport checks. See verification for desktop interaction coverage |
|
|
40
|
+
| Browser-only streaming | ✅ | ❌ | Missing. Full desktop only; browser confinement deferred |
|
|
41
|
+
| Network separation | ❓ | ✅ | Raft tests host/private/metadata and IPv4/IPv6 peer restrictions with startup-gated filtering. Boat's equivalent filtering policy is not established; its VM boundary is listed separately |
|
|
42
|
+
| Bounded host storage pool | ❓ | ✅ | Raft configures a shared 60 GiB Btrfs host pool and rejects size drift. Boat does not document an operator-managed host storage pool |
|
|
43
|
+
| Strict per-box disk allocation | ✅ | ❌ | Nested subvolumes prevent claiming strict quotas; no independent Boat-sized disks |
|
|
44
|
+
| VM kernel isolation | ✅ | ❌ | VM deployment not implemented |
|
|
45
|
+
| Nested KVM / Android emulator acceleration | ✅ | ❌ | Boat documents KVM on standard hosts; overflow cloud VMs lack it. Raft has no guest KVM device or VM mode |
|
|
46
|
+
| External guest SSH | ✅ | ❌ | Boat supports a public IP or an SSH endpoint; public IPv4 is not guaranteed. Raft uses host-mediated private access |
|
|
47
|
+
| Named environments and secrets | ✅ | ❌ | Files/configuration can be prepared explicitly. No environment registry or secret-injection service |
|
|
48
|
+
| Organizations | ✅ | ❌ | Boat shares billing and plan limits; resources stay with their creator. Raft is single-operator |
|
|
49
|
+
| Lifecycle webhooks | ✅ | ❌ | Missing. Deferred until an external consumer needs them |
|
|
50
|
+
| Automatic deletion/retention | ✅ | ✅ | Disposable delete-on-stop/expiry; opt-in stopped-box retention cleanup with preview |
|
|
51
|
+
| Dashboard | ✅ | ❌ | Missing. CLI and private desktop cover current workflow |
|
|
52
|
+
| SDK/API keys | ✅ | ❌ | Host SSH authentication. No remote Raft service API |
|
|
53
|
+
| CLI-wide JSON mode | ✅ | ❌ | Boat supports JSON/JSONL for most commands; argument errors may use stderr. Raft provides JSON for list/info/usage/limits and JSONL for prune |
|
|
54
|
+
| Snapshot file browsing/download | ✅ | ❌ | Full native archives only; no Raft snapshot tree or selected-file interface |
|
|
55
|
+
| CLI self-update/completions | ✅ | ❌ | Standard npm or Python package installation/help. No CLI update command or dynamic shell completions |
|
|
56
|
+
| Managed agents | ✅ | ❌ | Not implemented. No prompt/events/steer/interrupt/conversation API |
|
|
57
|
+
|
|
58
|
+
## Reference check
|
|
59
|
+
|
|
60
|
+
Checked on 2026-10-07 against Boat's [CLI reference](https://docs.boat.dev/cli-reference), [machine capabilities](https://docs.boat.dev/machines), [SSH/file access](https://docs.boat.dev/ssh-access), [snapshots](https://docs.boat.dev/snapshots), [data retention](https://docs.boat.dev/data-retention), and [billing and limits](https://docs.boat.dev/billing).
|
|
61
|
+
|
|
62
|
+
The new workspace-control suite passed on ARM64 and AMD64 at runtime commit `a7fef1f5` in [run 37603576786](https://github.com/Microck/raft/actions/runs/37603576786). The baseline native architecture and disposable AMD64 host tests passed in [run 37496577945](https://github.com/Microck/raft/actions/runs/37496577945). This audit compares documented features, not a live differential test against Boat. Managed agent workflows remain out of scope.
|
|
63
|
+
|
|
64
|
+
Recursive transfers, resume/fork sizing, snapshot templates/deletion and disposable lifecycle are implemented. Directory transfers require a running box; templates stay on the source host. Public hosting, VM isolation, organization sharing, and remote APIs require additional infrastructure. noVNC tests do not establish parity for Boat frame rate, browser confinement or application set.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Design decisions
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Architecture and references
|
|
6
|
+
|
|
7
|
+
Raft is an independent open-source alternative to [boat.dev](https://boat.dev). It pairs an operator CLI with open-source infrastructure without redistributing Boat software or credentials.
|
|
8
|
+
|
|
9
|
+
## Runtime
|
|
10
|
+
|
|
11
|
+
[Incus](https://github.com/lxc/incus) manages unprivileged system containers, images, snapshots, clones, and file transfers on native ARM64 and AMD64 Ubuntu hosts with Debian 13 guest images. Containers share the host kernel. KVM is not required, and VM deployment is not implemented.
|
|
12
|
+
|
|
13
|
+
Host SSH authenticates the operator. Incus listens strictly on its local Unix socket without a public management API. Guest systemd manages detached jobs and the on-demand Xvfb, Openbox, and noVNC desktop. A host timer enforces running TTL.
|
|
14
|
+
|
|
15
|
+
## Image distribution
|
|
16
|
+
|
|
17
|
+
Build clean images from upstream Debian bases using the provided builder, then pin the resulting 64-hex fingerprint in controller configuration. The native image workflow attaches temporary templates after build and E2E checks pass (see [Boot performance](./boot-performance.mdx)).
|
|
18
|
+
|
|
19
|
+
Reference projects and comparison surfaces:
|
|
20
|
+
|
|
21
|
+
- [OpenSandbox](https://github.com/opensandbox-group/OpenSandbox): Self-hosted sandbox patterns and runtime images.
|
|
22
|
+
- [Incus first steps](https://linuxcontainers.org/incus/docs/main/tutorial/first_steps/): Container lifecycle, image servers, and snapshots.
|
|
23
|
+
- [Incus backups](https://linuxcontainers.org/incus/docs/main/howto/instances_backup/): Portable container export format.
|
|
24
|
+
- [Boat CLI reference](https://docs.boat.dev/cli-reference): Parity comparison surface. Raft tests verify Raft behavior, not Boat service conformance.
|
|
25
|
+
|
|
26
|
+
See [Runtime contract](./contract.md), [Boat feature parity](./boat-capabilities.md), and [Verification](./verification.mdx) for behavior and limits.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Boot performance
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Native images and boot performance
|
|
6
|
+
|
|
7
|
+
Raft builds Debian 13 system container images for native ARM64 and AMD64 hosts. The installer, base image, and Node archive match the host CPU architecture. These are separate native images, not emulation layers.
|
|
8
|
+
|
|
9
|
+
Timings depend on software, image state, hardware, host load, and network transport. These measurements do not compare Raft with hosted Boat.
|
|
10
|
+
|
|
11
|
+
## Native CI comparison
|
|
12
|
+
|
|
13
|
+
[Verified native run](https://github.com/Microck/raft/actions/runs/37447415532), 2026-10-06. Five samples per architecture on 4-CPU runners with 1 guest CPU and 2 GiB guest RAM:
|
|
14
|
+
|
|
15
|
+
| Controller-visible median | ARM64 runner | AMD64 runner |
|
|
16
|
+
| --- | ---: | ---: |
|
|
17
|
+
| Create returns handle | 0.425 s | 0.659 s |
|
|
18
|
+
| Create accepts a command | 0.866 s | 1.375 s |
|
|
19
|
+
| Create has Docker ready | 2.280 s | 3.035 s |
|
|
20
|
+
| Resume accepts a command | 0.951 s | 1.489 s |
|
|
21
|
+
| Resume has Docker ready | 2.626 s | 3.049 s |
|
|
22
|
+
| Desktop starts after create | 1.115 s | 1.592 s |
|
|
23
|
+
| Compressed image | 1.79 GiB | 1.86 GiB |
|
|
24
|
+
|
|
25
|
+
Raw samples and p95 distributions are recorded in [ARM64 report](benchmarks/arm64-native-ci.json) and [AMD64 report](benchmarks/amd64-native-ci.json). Measurements reflect runner hardware and transport differences, not inherent architecture performance.
|
|
26
|
+
|
|
27
|
+
Native CI validates builds, workspaces, and clean exports. Imports must match host CPU architecture; cross-architecture recovery is not supported.
|
|
28
|
+
|
|
29
|
+
## Baseline measured on 2026-10-06
|
|
30
|
+
|
|
31
|
+
Three-sample medians from two 2-CPU ARM64 hosts with pre-warmed image volumes. Raw samples are preserved in [host A](benchmarks/arm64-baseline-a.json) and [host B](benchmarks/arm64-baseline-b.json).
|
|
32
|
+
|
|
33
|
+
| Controller-visible operation | ARM64 host A | ARM64 host B |
|
|
34
|
+
| --- | ---: | ---: |
|
|
35
|
+
| Create returns handle | 0.864 s | 2.906 s |
|
|
36
|
+
| Create accepts a command | 1.918 s | 6.936 s |
|
|
37
|
+
| Create has Docker ready | 3.941 s | 10.753 s |
|
|
38
|
+
| Resume returns | 1.056 s | 3.889 s |
|
|
39
|
+
| Resume accepts a command | 1.851 s | 8.213 s |
|
|
40
|
+
| Resume has Docker ready | 3.917 s | 11.653 s |
|
|
41
|
+
| Desktop starts after create | 2.602 s | 7.631 s |
|
|
42
|
+
| Desktop starts after resume | 2.430 s | 7.669 s |
|
|
43
|
+
| Separate host SSH request | 0.397 s | 3.556 s |
|
|
44
|
+
|
|
45
|
+
Network transport latency dominates the delta between hosts. This is not an ARM64 versus AMD64 benchmark, nor a comparison against hosted Boat.
|
|
46
|
+
|
|
47
|
+
## Measured changes on the same ARM64 host
|
|
48
|
+
|
|
49
|
+
- **SSH key generation**: Generates only the advertised Ed25519 host key on first boot, reducing SSH unit startup on host A from 1.898 s to 0.202 s.
|
|
50
|
+
- **Desktop polling**: Waits for an active X server response rather than sleeping for a fixed second.
|
|
51
|
+
- **Image footprint**: Omits coding-agent packages. The candidate ARM64 image is 1,920,185,312 bytes versus 2,626,705,196 bytes for baseline (about 27% smaller).
|
|
52
|
+
- **Volume pre-warming**: Pre-warming the storage volume avoids an initial 82.1 s unpack delay on first use.
|
|
53
|
+
|
|
54
|
+
Three samples per image on host A give the following comparison. The [candidate report](benchmarks/arm64-candidate-a.json) preserves all samples.
|
|
55
|
+
|
|
56
|
+
| Measurement | Baseline | Candidate |
|
|
57
|
+
| --- | ---: | ---: |
|
|
58
|
+
| Compressed image | 2.45 GiB | 1.79 GiB |
|
|
59
|
+
| SSH unit, first sample | 1.898 s | 0.202 s |
|
|
60
|
+
| Create has Docker ready, median | 3.941 s | 4.302 s |
|
|
61
|
+
| Resume has Docker ready, median | 3.917 s | 3.973 s |
|
|
62
|
+
| Desktop startup inside guest, median | 1.431 s | 0.537 s |
|
|
63
|
+
|
|
64
|
+
Warm Docker readiness remains about 4 seconds. These results do not show an overall warm boot speedup; SSH key generation and desktop startup improved.
|
|
65
|
+
|
|
66
|
+
Desktop benchmarks measure three internal stop/start cycles inside the guest from `systemctl start` to HTTP 200 and RFB greeting, showing about 62% less startup time in [local measurements](benchmarks/arm64-desktop-local.json). Local desktop timing excludes SSH delay; controller-visible timing includes network transport overhead and shows a smaller change.
|
|
67
|
+
|
|
68
|
+
The candidate passed lifecycle and headed-browser E2E suites on Ubuntu 22 ARM64, and extended suites with all 31 tool checks on Ubuntu 24 ARM64 and AMD64. Fresh-host testing resolved Chromium user-namespace policies and bridge peer filtering.
|
|
69
|
+
|
|
70
|
+
## Benchmark method
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
python3 deploy/benchmark-boot.py --location lab --samples 5 --desktop > boot.json
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The benchmark creates and destroys dedicated test boxes with 1 CPU and 2 GiB RAM. Each sample measures creation, then stops and resumes the same box. It records CLI return, command execution, and Docker API readiness from request initiation.
|
|
77
|
+
|
|
78
|
+
Desktop timing measures on-demand requests requiring both noVNC HTTP 200 and an RFB protocol greeting. Reports include median, minimum, maximum, nearest-rank p95, SSH round-trip time, image size, and systemd boot critical chain.
|
|
79
|
+
|
|
80
|
+
Measurements assume the image is pre-downloaded. A first use after import or cache eviction can still unpack Incus's optimized storage volume. Timings are software-dependent, varying with installed packages and services, image state, host hardware, load, and transport overhead.
|
|
81
|
+
|
|
82
|
+
Small sample sets are exploratory; with three or five samples, nearest-rank p95 is effectively their maximum. They do not measure image downloads, host reboots, process-memory restoration, or remote browser rendering.
|
|
83
|
+
|
|
84
|
+
## Build and verify
|
|
85
|
+
|
|
86
|
+
<details>
|
|
87
|
+
<summary>Image build, publication, and artifact details</summary>
|
|
88
|
+
|
|
89
|
+
Run the image builder on each native host. To preserve an existing image, specify a new alias such as `--alias raft-dev-next`. Building a candidate does not modify the pinned fingerprint in controller configuration.
|
|
90
|
+
|
|
91
|
+
After publication, the builder creates and deletes a cache-preparation instance to populate Incus's storage volume before reporting readiness. Imported archives or use after cache eviction can still require a slow first unpack.
|
|
92
|
+
|
|
93
|
+
The [native images workflow](https://github.com/Microck/raft/blob/main/.github/workflows/images.yml) runs on GitHub `ubuntu-24.04` and `ubuntu-24.04-arm` runners. It builds each image, runs extended lifecycle tests and all 31 developer tool checks, measures startup, and exports clean templates with SHA-256 checksums.
|
|
94
|
+
|
|
95
|
+
Successful CI runs attach seven-day artifacts (`raft-dev-amd64` and `raft-dev-arm64`), which require a GitHub login to download. Small `boot-results-amd64` and `boot-results-arm64` artifacts contain only reports.
|
|
96
|
+
|
|
97
|
+
Versioned public images are published under [releases](https://github.com/Microck/raft/releases) after recovery and fresh-host validation; public release assets do not expire on the seven-day CI schedule. Each archive must be imported into a host of the matching CPU architecture.
|
|
98
|
+
|
|
99
|
+
The CI runner uses a sparse Btrfs pool on an ephemeral VM and does not certify the 80 GiB production free-disk preflight. Pinned images retain their contents until you configure a new fingerprint.
|
|
100
|
+
|
|
101
|
+
</details>
|
|
102
|
+
|
package/docs/contract.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Runtime contract
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Raft contract
|
|
6
|
+
|
|
7
|
+
Raft manages persistent unprivileged native-architecture Incus system containers on configured ARM64 or AMD64 Linux hosts. Containers share the host kernel. Images must match host CPU architecture; foreign-architecture emulation and VM workflows are not implemented.
|
|
8
|
+
|
|
9
|
+
## Image releases
|
|
10
|
+
|
|
11
|
+
- Versioned releases provide clean ARM64 and AMD64 templates with SHA-256 checksums, package inventories, and build provenance.
|
|
12
|
+
- Publication requires native lifecycle checks, recovery verification, and fresh AMD64 Ubuntu VM provisioning and reboot checks.
|
|
13
|
+
- Disposable AMD64 KVM verification uses a sparse 100 GiB disk and does not certify physical storage capacity or ARM64 host reboot behavior.
|
|
14
|
+
|
|
15
|
+
## CLI distribution
|
|
16
|
+
|
|
17
|
+
- npm publishes `@microck/raft` with the same Python sources as the wheel and source archive. Its executable is `raft`; Linux and Python 3.11.8+ are required. Installation runs no scripts and downloads no workspace images.
|
|
18
|
+
- `raft --version` reports the release version without reading host configuration. The Python package, npm metadata and release tag must agree before publication.
|
|
19
|
+
- Releases include Python wheels, source archives, npm tarballs and both native images with checksums. The release workflow verifies installation outside the checkout and publishes npm with provenance before creating the GitHub release.
|
|
20
|
+
|
|
21
|
+
## Infrastructure and deployment
|
|
22
|
+
|
|
23
|
+
- **Strict infrastructure validation**: Deployment refuses conflicting storage or network names and incomplete infrastructure rather than overwriting or repairing state.
|
|
24
|
+
- **Storage pool sizing**: Existing owned Btrfs pools must retain the configured 60 GiB size; drift is rejected without automatic resizing.
|
|
25
|
+
- **Firewall precedence**: Redeployment reapplies firewall rules. Incus startup requires successful firewall application before the daemon starts, including socket activation.
|
|
26
|
+
- **CLI coverage**: The single-operator workflow spans lifecycle, execution, files, snapshots, jobs, tunnels, backup, and capacity inspection; see the [command reference](./reference/cli.md).
|
|
27
|
+
|
|
28
|
+
## Configuration and lifecycle defaults
|
|
29
|
+
|
|
30
|
+
| Setting | Behavior |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| Host identification | Configuration requires named SSH targets using letters, digits, underscores, or hyphens. |
|
|
33
|
+
| Qualified handles | Every handle includes location and instance name (`<location>:<name>`). |
|
|
34
|
+
| Image pinning | Workspace creation requires the immutable 64-hex image fingerprint, not an updating alias. |
|
|
35
|
+
| Lifecycle defaults | Defaults to first configured location, 1 CPU, 2 GiB RAM, and 600-second lifetime. Supported choices are 1 or 2 CPUs, 1/2/4 GiB RAM, and 60 to 2,592,000 seconds (30 days) TTL. |
|
|
36
|
+
| Admission limit | Hard ceiling of four saved boxes per host, including stopped boxes. |
|
|
37
|
+
| TTL calculation | Running TTL begins after launch completes, using the host clock. Cold image preparation does not consume TTL. |
|
|
38
|
+
| Host locking | Creation, lifecycle transitions, and expiry are serialized by host file lock (`/run/lock/raft-incus.lock`). Noninteractive transactions close controller stdin so commands cannot wait for piped input while holding this lock. Exec, SSH, and file-transfer paths remain unchanged. Piped input to `raft exec` is unsupported. Native Incus admin calls bypass CLI policy. |
|
|
39
|
+
|
|
40
|
+
## Capacity and admission inspection
|
|
41
|
+
|
|
42
|
+
`raft limits [--location NAME] [--json]` inspects hosts without modifying state. It reports effective CPUs, total and available RAM, saved and active box counts, active resource limits, and disk space.
|
|
43
|
+
|
|
44
|
+
- **Reserve calculations**: Recommends running boxes after reserving 1 CPU and the larger of 2 GiB or 10% host RAM.
|
|
45
|
+
- **Total capacity**: Assumes an empty dedicated host, capped at four boxes.
|
|
46
|
+
- **Additional running**: Subtracts active configured limits and evaluates available RAM minus host reserves. Every non-`Stopped` state (including `Frozen`) counts as active.
|
|
47
|
+
- **New box capacity**: Bounded by remaining saved-box slots: `min(more running, max(0, 4 - saved boxes))`.
|
|
48
|
+
- **Advisory estimates**: CPU and memory calculations are advisory planning estimates, not admission guarantees. Disk space below 5 GiB triggers diagnostic warnings. The four-saved-box admission cap is strictly enforced.
|
|
49
|
+
|
|
50
|
+
## Container lifecycle and execution
|
|
51
|
+
|
|
52
|
+
- **Stop and resume**: Stop terminates guest processes and preserves disk contents; stopped boxes retain disk but do not reserve guest RAM. Resume starts the container anew with an explicit TTL without restoring process memory.
|
|
53
|
+
- **Lifetime extension**: `raft extend --ttl` resets a running box's deadline from current host time without restarting; stopped boxes require `raft resume`.
|
|
54
|
+
- **Host worker contract**: `doctor`, disposable creation/fork, and retention cleanup require the current worker protocol. `doctor` checks it without running expiry or changing boxes, and reports the failing location. A mismatch fails before executing deletion policy or creating a disposable box; deploy the current checkout explicitly. Installing a new CLI does not update host workers.
|
|
55
|
+
- **Disposable lifecycle**: `new --disposable` opts a box into deletion on stop or TTL expiry, including its snapshots. Clones and recovered boxes default to persistent; fork accepts an explicit `--disposable`.
|
|
56
|
+
- **Retention cleanup**: `prune --older-than DAYS` previews persistent stopped boxes with a recorded stop timestamp. `--yes` deletes matching boxes under the host lifecycle lock. Running boxes and boxes without a recorded stop time are excluded. No automatic persistent-box deletion is enabled.
|
|
57
|
+
- **Resizing**: Resume and fork accept optional supported CPU/RAM overrides, preserving omitted values. Resume validates stopped state under the lock before changing sizing.
|
|
58
|
+
- **Expiration cleanup**: The host timer checks expirations every 15 seconds, stopping expired persistent containers and deleting expired disposable containers. Expiry may be delayed by graceful shutdown, long locked transactions, and host outage until startup.
|
|
59
|
+
- **Destroy**: Removes the container instance and its snapshots, releasing storage. Destroy confirms absence; SSH failure is not proof of deletion. Disposable boxes created with `new --disposable` are deleted on explicit stop or expiry. Persistent boxes still require explicit destroy for deletion.
|
|
60
|
+
- **Execution transport**: Incus exec and file APIs travel over authenticated host SSH without a public Incus listener or injected credentials. Interactive SSH runs a PTY over host SSH and Incus exec.
|
|
61
|
+
- **State inspection and tunnels**: `raft list` and `raft info` read native metadata without guest process counters during shutdown; tunnels use the managed NIC.
|
|
62
|
+
- **Resource reporting**: `raft usage` reports running cgroup memory, CPU microseconds, CPU affinity, and shared pool space. It does not provide per-box disk accounting or billing.
|
|
63
|
+
- **Recursive transfers**: `upload/download --recursive` require a running workspace and Python 3.11.8+ in the guest. They stream a tar archive and publish a new destination directory atomically without overwriting or merging. Relative links contained within the tree, regular files, directories and internal hardlinks are supported; escaping paths/links and special files are rejected. Source entries are pinned with no-follow descriptors before metadata inspection, file reads or directory descent, so pathname replacement cannot redirect access outside the selected tree. Failed transfers leave existing destinations unchanged.
|
|
64
|
+
- **Directory transfer completion**: The receiver verifies the sender's exact archive byte count before publication. A truncated stream, including one ending between complete entries, is rejected without publishing a partial tree.
|
|
65
|
+
- **File transfer failures**: Controller write errors return an error before replacing a download destination or publishing a backup archive.
|
|
66
|
+
|
|
67
|
+
## Security and network isolation
|
|
68
|
+
|
|
69
|
+
- **User namespaces**: Guest root maps to an unprivileged host UID. Container nesting and intercepted syscalls support Docker without mounting the host Docker socket.
|
|
70
|
+
- **Network filtering**: Dedicated bridge `rfbr0` allows DNS, DHCP, and outbound internet. Interface-scoped firewall rules block guest access to host services, cloud metadata, and private address ranges.
|
|
71
|
+
- **Bridge peer isolation**: A native nftables bridge rule blocks all inter-workspace frames on `rfbr0`. IPv6 and link-local peer traffic are blocked.
|
|
72
|
+
- **Private tunnels**: Forwarding listeners bind strictly to controller loopback (`127.0.0.1`). Guest services must listen on `0.0.0.0` or their managed IP, not guest loopback.
|
|
73
|
+
- **Image hygiene**: Development images omit coding agents, personal keys, and host credentials. SSH host keys are generated fresh on first boot. Release inspection rejects root and non-root SSH content, guest host keys, and credential-path links or nonregular entries.
|
|
74
|
+
|
|
75
|
+
## Storage, snapshots, and cloning
|
|
76
|
+
|
|
77
|
+
- **Storage pool**: A dedicated 60 GiB Btrfs pool per host supports snapshots and clones. Nested container subvolumes prevent strict per-box quota enforcement. The native compressed image cache resides outside the shared pool, so host disk space still matters.
|
|
78
|
+
- **Stopped-state requirement**: Snapshots, restores and forks require a stopped source for consistent disk contents. Operations run under the host lifecycle lock.
|
|
79
|
+
- **Snapshot restore**: `raft restore` rolls back disk blocks via `--diskonly`, retaining container configuration, limits, and MAC address.
|
|
80
|
+
- **Snapshot management**: `snapshot-delete <box> <name>` removes one named snapshot under the lock. `new --from LOCATION:BOX/SNAPSHOT` creates a same-host box from that snapshot, with normal creation defaults and a fresh network identity. The source may run; the named snapshot is immutable. Templates retain filesystem secrets.
|
|
81
|
+
- **Workspace forks**: `raft fork` copies filesystem data and secrets to a new container with an independent Incus name, fresh MAC address, and renewed TTL. Source snapshots are not inherited.
|
|
82
|
+
|
|
83
|
+
## Background jobs and desktop
|
|
84
|
+
|
|
85
|
+
- **Detached jobs**: Commands run as guest systemd units with journal logging and exit status. Job inspection and cancellation reject unknown IDs in the selected workspace. `raft cancel` stops the unit's control group. Stopping the container terminates jobs.
|
|
86
|
+
- **Desktop service**: On-demand Xvfb, Openbox, and noVNC stack on display `:99` without a login manager. Clipboard exchange is available without login-manager delay. Desktop provides full workspace access, not browser confinement. Closing the tunnel leaves the service running.
|
|
87
|
+
|
|
88
|
+
## Backup and recovery
|
|
89
|
+
|
|
90
|
+
- **Export safeguards**: `raft backup` exports stopped containers and snapshots to a local archive. Destination path must not exist, requires mode `0600`, and uses atomic hard-link publication.
|
|
91
|
+
- **Host locking**: Export holds the host lifecycle lock, temporarily delaying expiry checks on the source host.
|
|
92
|
+
- **Recovery admission**: `raft recover` imports archives into a stopped container with a fresh name, new MAC address, and cleared TTL, while enforcing the four-box admission limit. Recovery never overwrites existing boxes.
|
|
93
|
+
- **Archive security**: Archives contain full container filesystems, including credentials. Native archive configuration is not sanitized; backups do not imply scheduled off-host uploads. Store archives privately and import only trusted exports.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: First working box
|
|
3
|
+
description: Launch, execute commands, connect interactively, and stop your first workspace.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# First working box
|
|
7
|
+
|
|
8
|
+
Launch and manage a workspace after provisioning your host and pinning the image fingerprint (see [Installation and setup](./operations.mdx)).
|
|
9
|
+
|
|
10
|
+
## Step 1: Launch a workspace
|
|
11
|
+
|
|
12
|
+
Create a workspace with `raft new`:
|
|
13
|
+
|
|
14
|
+
```sh title="CONTROLLER: Create a new workspace"
|
|
15
|
+
box=$(raft new --location lab --ttl 600)
|
|
16
|
+
echo "Created workspace: $box"
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The command returns a location-qualified handle formatted as `<location>:<instance-name>` (for example `lab:rf-a1b2c3d4e5f60718`).
|
|
20
|
+
|
|
21
|
+
Omitting `--location` targets the first location in `~/.config/raft/incus.json`. The `--ttl` flag sets the lifetime in seconds (default: 600).
|
|
22
|
+
|
|
23
|
+
## Step 2: Verify command execution and Docker daemon
|
|
24
|
+
|
|
25
|
+
Execute commands inside the container with `raft exec`:
|
|
26
|
+
|
|
27
|
+
```sh title="CONTROLLER: Verify runtime tools and Docker daemon"
|
|
28
|
+
raft exec "$box" -- node --version
|
|
29
|
+
raft exec "$box" -- docker version
|
|
30
|
+
raft exec "$box" -- docker info
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
<Callout type="info" title="Daemon readiness check">
|
|
34
|
+
`docker version` and `docker info` verify guest `dockerd` readiness. Running `docker --version` inspects only the client binary without checking the daemon.
|
|
35
|
+
</Callout>
|
|
36
|
+
|
|
37
|
+
Arguments after `--` pass directly to `execve`. For pipes, redirects, or environment variables, wrap the command in `bash -lc '...'`.
|
|
38
|
+
|
|
39
|
+
## Step 3: Open an interactive terminal
|
|
40
|
+
|
|
41
|
+
Open a root terminal session inside the container:
|
|
42
|
+
|
|
43
|
+
```sh title="CONTROLLER: Open interactive root shell"
|
|
44
|
+
raft ssh "$box"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Type `exit` or press `Ctrl+D` to disconnect.
|
|
48
|
+
|
|
49
|
+
<Callout type="info" title="User namespace security">
|
|
50
|
+
Commands run as root in `/workspace` inside an unprivileged Linux user namespace. Container root maps to an unprivileged high UID on the host, sharing the host kernel.
|
|
51
|
+
</Callout>
|
|
52
|
+
|
|
53
|
+
## Step 4: Stop, resume, and destroy
|
|
54
|
+
|
|
55
|
+
Manage the workspace lifecycle:
|
|
56
|
+
|
|
57
|
+
```sh title="CONTROLLER: Stop, resume, and destroy workspace"
|
|
58
|
+
# Stop running processes while preserving files and snapshots
|
|
59
|
+
raft stop "$box"
|
|
60
|
+
|
|
61
|
+
# Resume the stopped workspace with a renewed lifetime
|
|
62
|
+
raft resume "$box" --ttl 600
|
|
63
|
+
|
|
64
|
+
# Permanently destroy the workspace and release host capacity
|
|
65
|
+
raft destroy "$box"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- **`raft stop`**: Stops running processes and frees active allocations while retaining files and snapshots.
|
|
69
|
+
- **`raft resume`**: Starts a stopped container with a new expiration deadline (`now + TTL`). Does not restore process memory.
|
|
70
|
+
- **`raft destroy`**: Permanently deletes the container, filesystem, and snapshots from the host storage pool.
|
|
71
|
+
|
|
72
|
+
## Next steps
|
|
73
|
+
|
|
74
|
+
- [Project setup and templates](./guides/project-setup.mdx): Prepare a project and reuse its dependencies.
|
|
75
|
+
- [Lifetime and persistent services](./guides/long-running-services.mdx): Manage deadlines and restart applications after resume.
|
|
76
|
+
- [Scripting and CI](./guides/scripting-ci.mdx): Parse CLI output and handle failures.
|
|
77
|
+
- [Docker in workspaces](./guides/docker.mdx): Run nested containers and build images.
|
|
78
|
+
- [File transfer](./guides/files.mdx): Upload and download files over SSH.
|
|
79
|
+
- [Background jobs](./guides/background-jobs.mdx): Run detached commands under systemd.
|
|
80
|
+
- [Desktop and private services](./guides/desktop-services.mdx): Forward guest ports and open noVNC desktop.
|
|
81
|
+
- [Snapshots and forks](./guides/snapshots-forks.mdx): Capture disk states and create same-host clones.
|
|
82
|
+
- [Backup and recovery](./guides/backup-recovery.mdx): Export portable archives and restore across hosts.
|
|
83
|
+
- [Troubleshooting](./guides/troubleshooting.mdx): Diagnose common errors and inspect host services.
|
|
84
|
+
- [Upgrades and image changes](./guides/upgrades.mdx): Update the controller, hosts, and image pins.
|
|
85
|
+
|
|
86
|
+
For temporary work, create with `--disposable`. Stopping or expiry then deletes the box and its snapshots. For persistent boxes, `raft prune --older-than 7` previews old stopped workspaces; add `--yes` only after reviewing the candidates.
|