rootfs apps: plan index and shared contract
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 file changed,
+180
−0
docs/superpowers/plans/2026-10-24-rootfs-apps-0-index.md+180 −0
CI passed — run details
CI log
=== gate ===
.amadan/test-gate.sh
=== launchctx (0 s)
ok amadan.net/carlos/platform.git/launchctx 0.013s
? amadan.net/carlos/platform.git/launchctx/controlname [no test files]
? amadan.net/carlos/platform.git/launchctx/wireid [no test files]
=== vet (1 s)
=== gofmt (0 s)
gofmt -l .: clean
=== rest (154 s)
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 4.047s
ok github.com/carlosframework/platform/cmd/moveprobe 11.968s
ok github.com/carlosframework/platform/cmd/zddprobe 0.716s
ok github.com/carlosframework/platform/internal/activator 62.037s
ok github.com/carlosframework/platform/internal/agentsock 0.036s
ok github.com/carlosframework/platform/internal/alerting 0.007s
ok github.com/carlosframework/platform/internal/analytics 0.033s
ok github.com/carlosframework/platform/internal/appconfig 3.193s
ok github.com/carlosframework/platform/internal/apperr 0.200s
ok github.com/carlosframework/platform/internal/applog 0.304s
ok github.com/carlosframework/platform/internal/awsinv 0.007s
ok github.com/carlosframework/platform/internal/boxchoice 0.009s
ok github.com/carlosframework/platform/internal/boxid 0.908s
? github.com/carlosframework/platform/internal/boxid/boxidtest [no test files]
? github.com/carlosframework/platform/internal/boxid/internal/signtest [no test files]
ok github.com/carlosframework/platform/internal/boxvolumes 0.015s
ok github.com/carlosframework/platform/internal/caps 0.036s
ok github.com/carlosframework/platform/internal/certwarm 0.005s
ok github.com/carlosframework/platform/internal/cgroup 0.822s
ok github.com/carlosframework/platform/internal/changelog 0.613s
ok github.com/carlosframework/platform/internal/claimactivation 1.385s
ok github.com/carlosframework/platform/internal/clamd 0.069s
? github.com/carlosframework/platform/internal/cliflag [no test files]
? github.com/carlosframework/platform/internal/controlname [no test files]
? github.com/carlosframework/platform/internal/controlsock [no test files]
ok github.com/carlosframework/platform/internal/dbdoor 7.592s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.014s
ok github.com/carlosframework/platform/internal/dbquery 0.009s
ok github.com/carlosframework/platform/internal/dbreads 2.185s
? github.com/carlosframework/platform/internal/dbreads/dbreadstest [no test files]
ok github.com/carlosframework/platform/internal/dbverify 0.005s
ok github.com/carlosframework/platform/internal/dnsrec 0.003s
ok github.com/carlosframework/platform/internal/docsite 0.019s
ok github.com/carlosframework/platform/internal/domains 2.964s
ok github.com/carlosframework/platform/internal/doorkey 0.013s
ok github.com/carlosframework/platform/internal/economics 0.011s
ok github.com/carlosframework/platform/internal/edge 46.011s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.014s
ok github.com/carlosframework/platform/internal/fleetapi 0.201s
ok github.com/carlosframework/platform/internal/gate 1.884s
ok github.com/carlosframework/platform/internal/genselect 0.022s
ok github.com/carlosframework/platform/internal/geo 0.016s
ok github.com/carlosframework/platform/internal/geoip 0.035s
ok github.com/carlosframework/platform/internal/historyreadback 0.271s
ok github.com/carlosframework/platform/internal/host 151.062s
ok github.com/carlosframework/platform/internal/hostactors 6.447s
ok github.com/carlosframework/platform/internal/hostactors/awsmembership 0.009s
ok github.com/carlosframework/platform/internal/hostbox 3.331s
ok github.com/carlosframework/platform/internal/hostclaim 0.857s
ok github.com/carlosframework/platform/internal/hostenv 0.006s
ok github.com/carlosframework/platform/internal/hostevidence 0.013s
ok github.com/carlosframework/platform/internal/hostexec 5.675s
ok github.com/carlosframework/platform/internal/hostfence 0.045s
ok github.com/carlosframework/platform/internal/hostfiles 10.294s
ok github.com/carlosframework/platform/internal/hostfloor 0.033s
ok github.com/carlosframework/platform/internal/hostguard 1.101s
ok github.com/carlosframework/platform/internal/hostops 2.348s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.007s
ok github.com/carlosframework/platform/internal/hostquarantine 0.006s
ok github.com/carlosframework/platform/internal/hostrelocate 0.650s
ok github.com/carlosframework/platform/internal/hostresolve 0.256s
ok github.com/carlosframework/platform/internal/hostwrite 0.003s
ok github.com/carlosframework/platform/internal/infraview 0.006s
ok github.com/carlosframework/platform/internal/instancectl 5.263s
ok github.com/carlosframework/platform/internal/instanceexec 2.821s
ok github.com/carlosframework/platform/internal/instancefs 0.008s
ok github.com/carlosframework/platform/internal/instancekey 0.006s
ok github.com/carlosframework/platform/internal/instanceupdates 0.012s
ok github.com/carlosframework/platform/internal/layout 1.927s
ok github.com/carlosframework/platform/internal/lbhealth 0.004s
ok github.com/carlosframework/platform/internal/lease 10.256s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.616s
ok github.com/carlosframework/platform/internal/ledger 0.021s
ok github.com/carlosframework/platform/internal/mail 0.242s
ok github.com/carlosframework/platform/internal/maintenance 0.097s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.625s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.068s
ok github.com/carlosframework/platform/internal/metrics 0.006s
ok github.com/carlosframework/platform/internal/moves 27.760s
ok github.com/carlosframework/platform/internal/moves/archive 1.154s
ok github.com/carlosframework/platform/internal/objstore 0.274s
ok github.com/carlosframework/platform/internal/ops 0.194s
ok github.com/carlosframework/platform/internal/park 0.295s
ok github.com/carlosframework/platform/internal/plans 0.006s
ok github.com/carlosframework/platform/internal/privatefile 0.007s
ok github.com/carlosframework/platform/internal/provision 47.547s
ok github.com/carlosframework/platform/internal/publicrelease 0.008s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 5.654s
ok github.com/carlosframework/platform/internal/relay 7.201s
ok github.com/carlosframework/platform/internal/release 30.339s
ok github.com/carlosframework/platform/internal/rollout 3.807s
ok github.com/carlosframework/platform/internal/rolloutwords 0.004s
ok github.com/carlosframework/platform/internal/rootaws 0.007s
ok github.com/carlosframework/platform/internal/rootparticipation 0.062s
ok github.com/carlosframework/platform/internal/rootstate 0.028s
ok github.com/carlosframework/platform/internal/runs 0.528s
ok github.com/carlosframework/platform/internal/s3compat 0.005s
ok github.com/carlosframework/platform/internal/safefile 0.004s
ok github.com/carlosframework/platform/internal/schedule 2.773s
ok github.com/carlosframework/platform/internal/seeds 0.013s
ok github.com/carlosframework/platform/internal/sim/engines 2.849s
ok github.com/carlosframework/platform/internal/sim/hostlife 14.112s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 4.481s
ok github.com/carlosframework/platform/internal/status 0.119s
ok github.com/carlosframework/platform/internal/steering 0.006s
ok github.com/carlosframework/platform/internal/stopevidence 0.246s
ok github.com/carlosframework/platform/internal/svcrole 0.004s
ok github.com/carlosframework/platform/internal/telemetry 0.005s
ok github.com/carlosframework/platform/internal/termfence 0.004s
ok github.com/carlosframework/platform/internal/tunnelclient 0.005s
ok github.com/carlosframework/platform/internal/unixdial 0.003s
ok github.com/carlosframework/platform/internal/updatewords 0.006s
ok github.com/carlosframework/platform/internal/usage 0.006s
ok github.com/carlosframework/platform/internal/version 0.005s
ok github.com/carlosframework/platform/internal/vet 1.653s
=== internal_console@0 (87 s)
ok github.com/carlosframework/platform/internal/console 84.524s
=== internal_console@1 (50 s)
ok github.com/carlosframework/platform/internal/console 47.644s
=== internal_console@2 (48 s)
ok github.com/carlosframework/platform/internal/console 45.374s
=== internal_console@3 (47 s)
ok github.com/carlosframework/platform/internal/console 44.178s
=== internal_console@4 (50 s)
ok github.com/carlosframework/platform/internal/console 47.178s
=== internal_console@5 (49 s)
ok github.com/carlosframework/platform/internal/console 46.450s
=== internal_console@6 (45 s)
ok github.com/carlosframework/platform/internal/console 42.223s
=== internal_console@7 (52 s)
ok github.com/carlosframework/platform/internal/console 49.374s
=== root@0 (117 s)
ok github.com/carlosframework/platform 115.343s
=== root@1 (89 s)
ok github.com/carlosframework/platform 87.659s
=== root@2 (100 s)
ok github.com/carlosframework/platform 98.292s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
linux/arm64 carlos binary: 50528418 bytes (ceiling 52000000)
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
minio: downloading https://github.com/minio/minio/releases/download/RELEASE.2025-09-07T16-13-09Z/minio.linux-arm64.RELEASE.2025-09-07T16-13-09Z
minio: downloaded, digest verified
mc: downloading https://github.com/minio/mc/releases/download/RELEASE.2025-08-13T08-35-41Z/mc.linux-arm64.RELEASE.2025-08-13T08-35-41Z
mc: downloaded, digest verified
minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
buckets and scoped user ready
=== RUN TestDeliveredContractMinio
=== RUN TestDeliveredContractMinio/put
=== RUN TestDeliveredContractMinio/get_round-trips_the_body
=== RUN TestDeliveredContractMinio/list_shows_the_key
=== RUN TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN TestDeliveredContractMinio/delete_removes_it
=== RUN TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
--- PASS: TestDeliveredContractMinio/put (0.00s)
--- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
--- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
--- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
--- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok github.com/carlosframework/platform/internal/objstore 0.026s
=== RUN TestT4StorageProbeMinio
2026/10/24 16:27:59 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18E18403ED8C86D4, HostID: 7c420053e0fbcd7f41653fdd92ffa2802c903d05303b1aa15adc2e8bb65d183f, api error SignatureDoesNotMatch: The request signature we calculated does not match the signature you provided. Check your key and signing method.
--- PASS: TestT4StorageProbeMinio (0.01s)
PASS
ok github.com/carlosframework/platform/internal/host 0.024s
=== RUN TestS3StoreMinioContract
=== RUN TestS3StoreMinioContract/conditional_get_and_etag_listing
=== RUN TestS3StoreMinioContract/bump_through_every_mutation_path
--- PASS: TestS3StoreMinioContract (0.03s)
--- PASS: TestS3StoreMinioContract/conditional_get_and_etag_listing (0.01s)
--- PASS: TestS3StoreMinioContract/bump_through_every_mutation_path (0.02s)
PASS
ok github.com/carlosframework/platform/internal/release 0.035s
Bucket created successfully `local/dbf-deploy`.
Bucket created successfully `local/dbf-data-ie`.
Bucket created successfully `local/dbf-data-uk`.
local/dbf-deploy versioning is enabled
Created policy `dbf-console` successfully.
Created policy `dbf-ukbox` successfully.
Added user `dbfconsole` successfully.
Added user `dbfukbox` successfully.
Attached Policies: [dbf-console]
To User: dbfconsole
Attached Policies: [dbf-ukbox]
To User: dbfukbox
=== RUN TestDBForwardMinio
2026/10/24 16:28:01 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/24 16:28:01 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=720
2026/10/24 16:28:01 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/24 16:28:01 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000002
2026/10/24 16:28:01 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000002 maxTXID=0000000000000002 size=370
2026/10/24 16:28:01 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000002 txid.db=0000000000000002
2026/10/24 16:28:01 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000002 txid.db=0000000000000003
2026/10/24 16:28:01 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000003 maxTXID=0000000000000003 size=454
2026/10/24 16:28:01 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000003 txid.db=0000000000000003
2026/10/24 16:28:01 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000000 txid.db=0000000000000001
2026/10/24 16:28:01 INFO ltx file uploaded system=store db=source.db replica=file level=0 minTXID=0000000000000001 maxTXID=0000000000000001 size=720
2026/10/24 16:28:01 INFO replica sync system=store db=source.db replica=file txid.replica=0000000000000001 txid.db=0000000000000001
2026/10/24 16:28:01 console: CARLOS_SECRETS_PUBKEY is unset, so no region outside the home can be chosen automatically
2026/10/24 16:28:01 console: could not notify member@example.com of a CLI token issue: keymail: lookup member@example.com: keymail: GET /api/federation/lookup?addr=member%40example.com&from=console%40127.0.0.1%3A40307: could not determine target mailbox
agent.go:135: dbreads: dbr_jdiej4fxvnjdlh24s2l2asfqae: planned 1 files (720 bytes) and restored to 1 in 9ms
agent.go:135: dbreads: dbr_jdiej4fxvnjdlh24s2l2asfqae: output 5 bytes in 1 confirmed chunks in 1ms
2026/10/24 16:28:02 console: db read dbr_jdiej4fxvnjdlh24s2l2asfqae: relayed 1 chunks (102 bytes) in 1.004s, ended done
agent.go:135: dbreads: dbr_gxpxlyfurma4vlw3qoslicu3ke: planned 3 files (1544 bytes) and restored to 3 in 10ms
agent.go:135: dbreads: dbr_gxpxlyfurma4vlw3qoslicu3ke: output 5 bytes in 1 confirmed chunks in 1ms
2026/10/24 16:28:03 console: db read dbr_gxpxlyfurma4vlw3qoslicu3ke: relayed 1 chunks (102 bytes) in 1.004s, ended done
agent.go:135: dbreads: dbr_tyegrnne3x32bw2zornwblyhuu: planned 3 files (1544 bytes) and held through 3 in 9ms
agent.go:135: dbreads: dbr_tyegrnne3x32bw2zornwblyhuu: output 1544 bytes in 1 confirmed chunks in 1ms
2026/10/24 16:28:04 console: db read dbr_tyegrnne3x32bw2zornwblyhuu: relayed 1 chunks (1641 bytes) in 1.003s, ended done
=== RUN TestDBForwardMinio/by_disconnect
=== NAME TestDBForwardMinio
agent.go:135: dbreads: dbr_golwee4m4diqmshk6viltvyriq: planned 3 files (1544 bytes) and restored to 3 in 9ms
2026/10/24 16:28:04 console: db read dbr_golwee4m4diqmshk6viltvyriq: relayed 0 chunks (0 bytes) in 64ms, ended client gone
agent.go:135: dbreads: dbr_golwee4m4diqmshk6viltvyriq: output 5 bytes in 1 confirmed chunks in 103ms
=== RUN TestDBForwardMinio/by_timeout
=== NAME TestDBForwardMinio
agent.go:135: dbreads: dbr_bp5ld6ge6vfla6qqjyiobjfthy: planned 3 files (1544 bytes) and restored to 3 in 10ms
2026/10/24 16:28:24 console: db read dbr_bp5ld6ge6vfla6qqjyiobjfthy: relayed 0 chunks (0 bytes) in 19.114s, ended no-result
agent.go:135: dbreads: dbr_bp5ld6ge6vfla6qqjyiobjfthy: output 5 bytes in 1 confirmed chunks in 19.088s
--- PASS: TestDBForwardMinio (22.55s)
--- PASS: TestDBForwardMinio/by_disconnect (0.22s)
--- PASS: TestDBForwardMinio/by_timeout (19.22s)
PASS
ok github.com/carlosframework/platform/internal/console 22.569s
minio contract: PASS
diff --git a/docs/superpowers/plans/2026-10-24-rootfs-apps-0-index.md b/docs/superpowers/plans/2026-10-24-rootfs-apps-0-index.md| new file mode 100644 |
| index 000000000..4db87412a |
| --- /dev/null |
| +++ b/docs/superpowers/plans/2026-10-24-rootfs-apps-0-index.md |
| @@ -0,0 +1,180 @@ |
| +# Rootfs apps Implementation Plans: Index |
| + |
| +> **For agentic workers:** this file is the map. Each plan it names is a |
| +> self-contained implementation plan with the superpowers header; execute |
| +> them in the order below with superpowers:subagent-driven-development or |
| +> superpowers:executing-plans. Do not start a plan whose dependencies are |
| +> not merged on `main`. |
| + |
| +**Goal:** Build rootfs apps (amadan carlos/platform #366): a release may |
| +carry a read-only root filesystem built locally from an OCI image or a |
| +directory, run as a nested instance under systemd on the customer's own |
| +boxes, always-on by default, with declared `web`, worker, `release` and |
| +`console` processes, an operator-granted egress allow, and interactive |
| +sessions. |
| + |
| +**Spec:** `docs/superpowers/specs/2026-10-24-rootfs-apps-design.md` rev 4 |
| +(bc45f56b), approved by Paul 2026-10-24. Rulings R1 to R6 (§2.1) and the |
| +calls in §11 are requirements; plans cite the section beside the task that |
| +answers it. |
| + |
| +**Built against:** `main` at 64a002c8. |
| + |
| +**Copy:** every user-facing string of the series goes in one deck, |
| +`2026-10-24-rootfs-apps-copy.md`, assembled from the string tables each plan |
| +carries (IDs `RFS-<plan>-C<nn>`). Plan 1 Task 1 puts the gate in place and |
| +gets Paul's review before any task writes a string. Copy voice: no em |
| +dashes; neutral names only (`shop`, `example.com`), never a tenant's. |
| + |
| +## The series |
| + |
| +| # | plan | produces | depends on | |
| +|---|---|---|---| |
| +| 1 | `2026-10-24-rootfs-apps-1-ship.md` | the copy gate; manifest `kind: "rootfs"`, `processes`, `hibernate`; validation; `carlos ship --rootfs` (OCI or directory to squashfs, locally); the ship door's kind-fixed refusal and rootfs upload cap; `carlos ship --check`; the framework `console` fill-in | `main` | |
| +| 2 | `2026-10-24-rootfs-apps-2-box-isolation.md` | staging the image; sweep coverage; the rootfs unit templates and the rootfs probe; the trampoline's rootfs launch shape; the control service's rootfs start path; the box's `rootfs_ready` report; the box-side placement refusal | Plan 1 merged | |
| +| 3 | `2026-10-24-rootfs-apps-3-lifecycle.md` | `Record.Runtime` and the route column; hibernation fixed at mint; always-on nested instances; workers; the `release` process at swap; `carlos run` for rootfs and `--version` | Plans 1 and 2 merged | |
| +| 4 | `2026-10-24-rootfs-apps-4-console-and-docs.md` | the console's rootfs placement checks and instance creation; the single-tenant deployment setting; the guide and contract docs; the sample Rails app; the dev-fleet end-to-end checklist (spec §6.1) | Plans 1 to 3 merged | |
| +| 5 | `2026-10-24-rootfs-apps-5-egress-allow.md` | the egress grant (console, signed, delivered), its rendering as `IPAddressAllow=` on the instance slice, hostname re-resolution | Plan 2 merged | |
| +| 6 | `2026-10-24-rootfs-apps-6-interactive.md` | `carlos run -it` and `carlos console`; the per-app allow; the CLI-console WebSocket, the tunnel leg, the box's pty cmd unit; audit and limits | Plans 2 and 3 merged | |
| + |
| +``` |
| +main ─► Plan 1 ─► Plan 2 ─► Plan 3 ─► Plan 4 (dev-fleet e2e = spec §6 step 1) |
| + ├─────► Plan 5 |
| + └─────────────► Plan 6 |
| +``` |
| + |
| +Plans 5 and 6 are needed for spec §6 step 4, not step 1. They can be built |
| +beside Plan 4. |
| + |
| +## The shared contract |
| + |
| +Every plan uses these names. A plan that finds one cannot work against the |
| +code stops and records the problem at the top of its own file under |
| +"Contract deviations" instead of choosing a different name silently. |
| + |
| +### Manifest (`internal/release/release.go`, `Manifest`) |
| + |
| +- `Kind` gains the value `"rootfs"`. `validateManifest` (release.go:332) |
| + accepts it. An old binary's `validateManifest` rejects it, which is what |
| + keeps an old box from staging a rootfs release (spec C2); Plan 1 pins |
| + that with a test against the old rule. |
| +- Artifact key `"rootfs-linux-arm64"`, file `"rootfs.squashfs"`. Never |
| + `"linux-arm64"` on a rootfs manifest. |
| +- New fields, additive to schema 1, `omitempty`, valid only when |
| + `Kind == "rootfs"`: |
| + ```go |
| + Processes map[string]Process `json:"processes,omitempty"` |
| + Hibernate bool `json:"hibernate,omitempty"` // opt-in; default always-on (R5) |
| + |
| + type Process struct { |
| + Command []string `json:"command"` |
| + Env map[string]string `json:"env,omitempty"` |
| + WorkingDir string `json:"working_dir,omitempty"` |
| + TimeoutSec int `json:"timeout_sec,omitempty"` // release only |
| + } |
| + ``` |
| +- Process names: `web` (exactly one), `release` (at most one), `console` |
| + (at most one), workers matching `^[a-z][a-z0-9-]{0,15}$` (at most 4). |
| + `release.ProcessWeb`, `ProcessRelease`, `ProcessConsole` are the |
| + constants; `release.ValidWorkerName(string) bool` the predicate. |
| +- `release` timeout: default 600 s, maximum the `carlos run` maximum. |
| + Health bound for rootfs `web`: 120 s. |
| +- Rootfs upload cap: 2 GiB for the rootfs part (`apiShipMaxBytesRootfs`). |
| + |
| +### Image |
| + |
| +- Format: squashfs, written by `carlos ship` in pure Go (no mksquashfs, no |
| + Docker needed to ship). Plan 1 picks the writer and proves a round trip: |
| + ownership, modes, symlinks, hardlinks, device-free, readable by the |
| + kernel's squashfs on the box. |
| +- Input: an OCI layout directory, a `docker save`/`podman save` tarball, |
| + or a plain directory. OCI is flattened locally (whiteouts honoured). |
| +- Mount points `carlos ship` adds if absent: `/data`, `/run/carlos`, |
| + `/opt/carlos`, `/tmp`, `/dev`, `/proc`, `/sys`. |
| +- Architecture: arm64 only; anything else refuses at ship. |
| + |
| +### Instance record and route |
| + |
| +- `provision.Record` gains `Runtime string \`json:"runtime,omitempty"\``. |
| + Values: `""` (binary, today) and `"rootfs-v1"`. Any other value refuses |
| + before materialization or start. |
| +- A rootfs record always carries an explicit `Hibernate` (`*bool`), set at |
| + mint from the creating release's manifest and never changed by ordinary |
| + writers (spec §5.0). |
| +- `registry.Route` gains `Runtime string`, column `runtime TEXT NOT NULL |
| + DEFAULT ''`, materialized from the record in the same transaction as |
| + `hibernate`. |
| + |
| +### Environment the processes get |
| + |
| +- `CARLOS_SOCKET`: the socket `web` binds (the path today's binary gets as |
| + `--socket`). |
| +- `CARLOS_DB`: the database path (today's `--db`). |
| +- Every rootfs process (web, workers, release, console, `carlos run`) gets |
| + the same environment as `web`; workers no longer read |
| + `<host>-sidecar.env`. |
| + |
| +### Units and roles (`internal/host/units/`, `internal/instancectl/names.go`) |
| + |
| +- Templates: `carlos-instance-rootfs@.service`, |
| + `carlos-instance-rootfs-gen@.service`, |
| + `carlos-instance-rootfs-worker@.service`, |
| + `carlos-instance-rootfs-cmd@.service`, |
| + `carlos-instance-rootfs-probe.service`. |
| +- They share one restated namespace block, byte for byte, the way the |
| + binary templates do today (`TestInstanceTemplatesShareOneNamespace`). |
| +- New role: `instancectl.RoleWorker` (`"worker"`). The worker unit's |
| + instance string encodes (instance ID, worker name); Plan 2 fixes the |
| + encoding and its parser, `instancectl.WorkerInstance(iid, name string) |
| + string` and `ParseWorkerInstance(s string) (iid, name string, ok bool)`. |
| +- The rootfs templates are chosen by `Route.Runtime`, never by what is |
| + staged. |
| + |
| +### Box facts |
| + |
| +- `rootfs_ready` (bool) joins the box status snapshot beside the nested |
| + layout readiness; true only after the rootfs probe passed on this boot. |
| +- Placement predicate, box side: |
| + `host.RootfsPlacementAllowed(f RootfsBoxFacts, rt registry.Route) error` |
| + (Plan 2). Allowed only when: the box is nested-ready and `rootfs_ready`; |
| + and either `CARLOS_DEPLOYMENT_SINGLE_TENANT=1` or the box is a fleet box |
| + (`CARLOS_REGION=fleet/<name>`) whose fleet's owning account is the row's |
| + account. Pools never qualify in v1. |
| +- `CARLOS_DEPLOYMENT_SINGLE_TENANT` joins the host.env allowlist |
| + (`internal/hostenv`) and the console's env; operator-only. |
| + |
| +### Egress (Plan 5) |
| + |
| +- Capability `egress`, a list of destinations (`10.0.0.0/16`, |
| + `db.example.com`). The build declares it; the operator grants it per app; |
| + the box renders the intersection. Valid only where |
| + `RootfsPlacementAllowed`'s ownership half holds (the customer's own |
| + boxes), for binary and rootfs nested rows alike (spec §5.9). |
| + |
| +## Repo rules every plan carries |
| + |
| +- Read AGENTS.md. Delegation tiers, the census gates and the claim |
| + machinery are in force: new console tables go in `claimSQLTables`; new |
| + workers and routes need `claim-routes.txt`, a claim fixture and a probe; |
| + new bucket keys need a hostops classifier family; operator-only doors |
| + join the svcroutes empty-role list; changes under console/plans/root |
| + re-pin the plan14 overlay; never name a function `RecordKey`. |
| +- CI builds darwin and windows: linux-only syscalls need `//go:build linux` |
| + and a `!linux` stub. |
| +- Tests run locally only for touched packages, with `-run`; CI runs the |
| + full suite. New `internal/host` tests call `t.Parallel()` unless they set |
| + env, cwd, umask, log output or package vars. |
| +- Production-wired witnesses: tests for refusals and launch go through |
| + real wiring, not fixtures. |
| +- A box that has armed host lifecycle claim control refuses legacy launches |
| + (`internal/instancectl/server.go:840`); each launch path states what it |
| + does on such a box, as `carlos run` did. |
| + |
| +## Where the code differs from the spec |
| + |
| +Collected from the plans as they are written. Each is decided once, here. |
| + |
| +1. **#122 is fixed.** The spec's rev 1 to 3 said old versions are never |
| + reaped; `sweepStagedVersions` (`internal/host/versionsweep.go:52`) runs |
| + as step 0 of the root adopt pass. Plan 2 extends its keep set to rootfs |
| + images. |