Plan 14 integrated: whole-branch review round 2 fixes P3-R10 (not for merge)
No-census adopt/update/recovery take no new lock or file; retired installations enumerate from retained root evidence; pending-acquisition recovery through instance-control and system recovery; preflight inventories legacy backing and named units; census corrections.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
17 files changed,
+731
−69
internal/host/claimactivation_preflight.go+65 −0internal/host/claimactivation_preflight_test.go+41 −0internal/host/claimconstruction_rootrunner_linux_test.go+55 −2internal/host/plan14_r10_root_linux_test.go+288 −0internal/host/plan14_r9_root_linux_test.go+86 −16internal/host/rootmigration_bootstrap_root_linux_test.go+58 −2internal/host/rootmigration_production_linux.go+17 −1internal/host/rootmigration_startup.go+1 −1internal/host/rootmovesfloor_source.go+1 −1internal/host/rootreleaselock.go+1 −19internal/host/systemapply.go+1 −1internal/host/systemparticipation.go+8 −1internal/hostguard/generationinventory.go+1 −1internal/hostguard/retired_inventory_test.go+60 −0internal/hostguard/root.go+21 −2internal/hostguard/rootmigration_inventory.go+1 −1internal/provision/testdata/plan14-root-reads-p3.tsv+26 −21
CI passed — run details
CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok amadan.net/carlos/platform.git/launchctx 0.011s
? amadan.net/carlos/platform.git/launchctx/controlname [no test files]
? amadan.net/carlos/platform.git/launchctx/wireid [no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok github.com/carlosframework/platform 293.205s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 2.667s
ok github.com/carlosframework/platform/cmd/moveprobe 92.151s
ok github.com/carlosframework/platform/cmd/zddprobe 0.706s
ok github.com/carlosframework/platform/internal/activator 251.445s
ok github.com/carlosframework/platform/internal/agentsock 0.015s
ok github.com/carlosframework/platform/internal/alerting 0.003s
ok github.com/carlosframework/platform/internal/analytics 0.018s
ok github.com/carlosframework/platform/internal/appconfig 2.441s
ok github.com/carlosframework/platform/internal/apperr 0.069s
ok github.com/carlosframework/platform/internal/applog 0.123s
ok github.com/carlosframework/platform/internal/awsinv 0.003s
ok github.com/carlosframework/platform/internal/boxchoice 0.004s
ok github.com/carlosframework/platform/internal/boxid 0.750s
? 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.008s
ok github.com/carlosframework/platform/internal/caps 0.012s
ok github.com/carlosframework/platform/internal/certwarm 0.002s
ok github.com/carlosframework/platform/internal/cgroup 0.695s
ok github.com/carlosframework/platform/internal/changelog 0.305s
ok github.com/carlosframework/platform/internal/clamd 0.055s
? github.com/carlosframework/platform/internal/cliflag [no test files]
ok github.com/carlosframework/platform/internal/console 389.115s
? 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 5.620s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.009s
ok github.com/carlosframework/platform/internal/dbverify 0.004s
ok github.com/carlosframework/platform/internal/dnsrec 0.002s
ok github.com/carlosframework/platform/internal/docsite 0.005s
ok github.com/carlosframework/platform/internal/domains 2.335s
ok github.com/carlosframework/platform/internal/doorkey 0.006s
ok github.com/carlosframework/platform/internal/economics 0.004s
ok github.com/carlosframework/platform/internal/edge 26.614s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.008s
ok github.com/carlosframework/platform/internal/fleetapi 0.130s
ok github.com/carlosframework/platform/internal/gate 1.654s
ok github.com/carlosframework/platform/internal/genselect 0.014s
ok github.com/carlosframework/platform/internal/geo 0.012s
ok github.com/carlosframework/platform/internal/geoip 0.029s
ok github.com/carlosframework/platform/internal/historyreadback 0.151s
ok github.com/carlosframework/platform/internal/host 174.342s
ok github.com/carlosframework/platform/internal/hostactors 0.482s
ok github.com/carlosframework/platform/internal/hostbox 2.546s
ok github.com/carlosframework/platform/internal/hostclaim 0.491s
ok github.com/carlosframework/platform/internal/hostevidence 0.012s
ok github.com/carlosframework/platform/internal/hostexec 4.816s
ok github.com/carlosframework/platform/internal/hostfence 0.032s
ok github.com/carlosframework/platform/internal/hostfiles 10.475s
ok github.com/carlosframework/platform/internal/hostfloor 0.013s
ok github.com/carlosframework/platform/internal/hostguard 1.026s
ok github.com/carlosframework/platform/internal/hostops 1.519s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.012s
ok github.com/carlosframework/platform/internal/hostquarantine 0.013s
ok github.com/carlosframework/platform/internal/hostrelocate 0.450s
ok github.com/carlosframework/platform/internal/hostresolve 0.140s
ok github.com/carlosframework/platform/internal/hostwrite 0.012s
ok github.com/carlosframework/platform/internal/infraview 0.012s
ok github.com/carlosframework/platform/internal/instancectl 5.230s
ok github.com/carlosframework/platform/internal/instanceexec 2.723s
ok github.com/carlosframework/platform/internal/instancefs 0.018s
ok github.com/carlosframework/platform/internal/instancekey 0.013s
ok github.com/carlosframework/platform/internal/instanceupdates 0.019s
ok github.com/carlosframework/platform/internal/layout 1.493s
ok github.com/carlosframework/platform/internal/lease 9.801s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.021s
ok github.com/carlosframework/platform/internal/ledger 0.010s
ok github.com/carlosframework/platform/internal/mail 0.153s
ok github.com/carlosframework/platform/internal/maintenance 0.082s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.522s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.066s
ok github.com/carlosframework/platform/internal/metrics 0.004s
ok github.com/carlosframework/platform/internal/moves 20.984s
ok github.com/carlosframework/platform/internal/moves/archive 0.756s
ok github.com/carlosframework/platform/internal/objstore 0.163s
ok github.com/carlosframework/platform/internal/ops 0.169s
ok github.com/carlosframework/platform/internal/park 0.190s
ok github.com/carlosframework/platform/internal/privatefile 0.003s
ok github.com/carlosframework/platform/internal/provision 34.868s
ok github.com/carlosframework/platform/internal/publicrelease 0.007s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 5.462s
ok github.com/carlosframework/platform/internal/relay 7.257s
ok github.com/carlosframework/platform/internal/release 21.324s
ok github.com/carlosframework/platform/internal/rollout 3.734s
ok github.com/carlosframework/platform/internal/rolloutwords 0.003s
ok github.com/carlosframework/platform/internal/rootaws 0.007s
ok github.com/carlosframework/platform/internal/rootparticipation 0.057s
ok github.com/carlosframework/platform/internal/rootstate 0.026s
ok github.com/carlosframework/platform/internal/runs 0.574s
ok github.com/carlosframework/platform/internal/s3compat 0.008s
ok github.com/carlosframework/platform/internal/safefile 0.004s
ok github.com/carlosframework/platform/internal/schedule 2.471s
ok github.com/carlosframework/platform/internal/seeds 0.010s
ok github.com/carlosframework/platform/internal/sim/hostlife 9.385s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.927s
ok github.com/carlosframework/platform/internal/status 0.037s
ok github.com/carlosframework/platform/internal/steering 0.005s
ok github.com/carlosframework/platform/internal/stopevidence 0.211s
ok github.com/carlosframework/platform/internal/svcrole 0.002s
ok github.com/carlosframework/platform/internal/telemetry 0.004s
ok github.com/carlosframework/platform/internal/termfence 0.004s
ok github.com/carlosframework/platform/internal/tunnelclient 0.004s
ok github.com/carlosframework/platform/internal/unixdial 0.002s
ok github.com/carlosframework/platform/internal/updatewords 0.004s
ok github.com/carlosframework/platform/internal/usage 0.004s
ok github.com/carlosframework/platform/internal/version 0.001s
ok github.com/carlosframework/platform/internal/vet 1.532s
=== 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: 48496802 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: cached, digest verified
mc: cached, 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.022s
=== RUN TestT4StorageProbeMinio
2026/10/16 06:31:18 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DEEECFD34D6B26, 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.020s
minio contract: PASS
diff --git a/internal/host/claimactivation_preflight.go b/internal/host/claimactivation_preflight.go| index 57e677860..7e35f8bdd 100644 |
| --- a/internal/host/claimactivation_preflight.go |
| +++ b/internal/host/claimactivation_preflight.go |
| @@ -10,7 +10,9 @@ import ( |
| "github.com/carlosframework/platform/internal/registry" |
| "github.com/carlosframework/platform/internal/release" |
| "github.com/carlosframework/platform/internal/rootstate" |
| + "io/fs" |
| "os" |
| + "path/filepath" |
| "slices" |
| "strings" |
| ) |
| @@ -68,6 +70,9 @@ func claimActivationVerified(ctx context.Context, store release.Store, r ClaimRu |
| } |
| } |
| } |
| + if err := claimActivationLocalUnits(r); err != nil { |
| + return err |
| + } |
| if r.Mode == "agent" { |
| return nil |
| } |
| @@ -99,3 +104,63 @@ func claimActivationVerified(ctx context.Context, store release.Store, r ClaimRu |
| } |
| return nil |
| } |
| + |
| +func claimActivationLocalUnits(r ClaimRuntimeResources) error { |
| + if r.Mode != "agent" && os.Geteuid() == 0 { |
| + for _, dir := range []string{"/var/lib/carlos-app", "/var/lib/private/carlos-app"} { |
| + entries, err := os.ReadDir(dir) |
| + if errors.Is(err, os.ErrNotExist) { |
| + continue |
| + } |
| + if err != nil { |
| + return err |
| + } |
| + if len(entries) != 0 { |
| + return hostwrite.ErrUnavailable |
| + } |
| + } |
| + } |
| + for _, dir := range []string{r.UnitsDir, "/etc/systemd/system", "/run/systemd/system", "/usr/lib/systemd/system", "/lib/systemd/system", "/etc/carlos/apps"} { |
| + if dir == "" { |
| + continue |
| + } |
| + err := filepath.WalkDir(dir, func(path string, entry fs.DirEntry, err error) error { |
| + if errors.Is(err, os.ErrNotExist) && path == dir { |
| + return nil |
| + } |
| + if err != nil { |
| + return err |
| + } |
| + name := entry.Name() |
| + if strings.HasPrefix(name, "carlos-app@") && name != "carlos-app@.service" && name != "carlos-app@.socket" { |
| + return hostwrite.ErrUnavailable |
| + } |
| + if entry.IsDir() { |
| + return nil |
| + } |
| + if dir == "/etc/carlos/apps" { |
| + return hostwrite.ErrUnavailable |
| + } |
| + if !strings.HasSuffix(name, ".service") && !strings.HasSuffix(name, ".conf") { |
| + return nil |
| + } |
| + if _, err := unitsFS.ReadFile("units/" + name); err == nil { |
| + return nil |
| + } |
| + raw, err := os.ReadFile(path) |
| + if err != nil { |
| + return err |
| + } |
| + for _, marker := range []string{filepath.Join(r.OptDir, "live") + "/", "/opt/carlos/live/", "/etc/carlos/apps/", "User=carlos", "User = carlos"} { |
| + if strings.Contains(string(raw), marker) { |
| + return hostwrite.ErrUnavailable |
| + } |
| + } |
| + return nil |
| + }) |
| + if err != nil { |
| + return err |
| + } |
| + } |
| + return nil |
| +} |
diff --git a/internal/host/claimactivation_preflight_test.go b/internal/host/claimactivation_preflight_test.go| index 4feb386ac..0350888df 100644 |
| --- a/internal/host/claimactivation_preflight_test.go |
| +++ b/internal/host/claimactivation_preflight_test.go |
| @@ -107,3 +107,44 @@ func TestPlan14ActivationRejectsHiddenNamedRecords(t *testing.T) { |
| }) |
| } |
| } |
| + |
| +func TestPlan14ActivationRejectsHiddenLocalUnits(t *testing.T) { |
| + for _, unit := range []string{"carlos-app@shop.example.service", "console.service", "woodstar.service.d/override.conf"} { |
| + for _, attack := range []string{"omitted", "cleared"} { |
| + t.Run(unit+"/"+attack, func(t *testing.T) { |
| + reg := restartTestRegistry(t) |
| + if attack == "cleared" { |
| + if err := reg.Upsert(registry.Route{Host: "shop.example", Account: "bab", App: "shop", Socket: "/run/shop.sock"}); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + dir := t.TempDir() |
| + units := filepath.Join(dir, "units") |
| + path := filepath.Join(units, unit) |
| + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := os.WriteFile(path, []byte("[Service]\nExecStart=/opt/carlos/live/shop.example serve\n"), 0644); err != nil { |
| + t.Fatal(err) |
| + } |
| + store := release.NewDirStore(filepath.Join(dir, "bucket")) |
| + for _, mode := range []string{"agent", "instance-control"} { |
| + p := &activationPreflightOwner{} |
| + r := ClaimRuntimeResources{Mode: mode, Registry: reg, DataDir: filepath.Join(dir, "data"), OptDir: filepath.Join(dir, "opt"), UnitsDir: units} |
| + if _, err := OpenProductionClaimAssembly(t.Context(), store, r, p); !errors.Is(err, hostwrite.ErrUnavailable) || p.calls != 0 { |
| + t.Fatal(err, p.calls) |
| + } |
| + r.Mode = "instance-control" |
| + if err := migrateClaimJournalsWith(t.Context(), store, r, p, true); !errors.Is(err, hostwrite.ErrUnavailable) || p.calls != 0 { |
| + t.Fatal(err, p.calls) |
| + } |
| + for _, path := range []string{r.DataDir, r.OptDir} { |
| + if _, err := os.Stat(path); !os.IsNotExist(err) { |
| + t.Fatal("created state", path, err) |
| + } |
| + } |
| + } |
| + }) |
| + } |
| + } |
| +} |
diff --git a/internal/host/claimconstruction_rootrunner_linux_test.go b/internal/host/claimconstruction_rootrunner_linux_test.go| index 745079928..806967730 100644 |
| --- a/internal/host/claimconstruction_rootrunner_linux_test.go |
| +++ b/internal/host/claimconstruction_rootrunner_linux_test.go |
| @@ -20,7 +20,9 @@ import ( |
| "time" |
| "github.com/carlosframework/platform/internal/hostactors" |
| + "github.com/carlosframework/platform/internal/hostbox" |
| execlease "github.com/carlosframework/platform/internal/hostexec" |
| + "github.com/carlosframework/platform/internal/hostguard" |
| "github.com/carlosframework/platform/internal/hostops" |
| "github.com/carlosframework/platform/internal/release" |
| "github.com/carlosframework/platform/internal/rootparticipation" |
| @@ -73,7 +75,7 @@ func filesSeedInstanceControlChild(t *testing.T, binary, root string, listener * |
| } |
| cmd := exec.Command(binary, "-test.run=^TestRootFilesSeedInstanceControlPayload$", "-test.v", "-test.timeout=90s") |
| cmd.Env = append(os.Environ(), "FILES_SEED_CONTROL=1", "TMPDIR="+root) |
| - if os.Getenv("PLAN14_R9_PENDING") != "" { |
| + if os.Getenv("PLAN14_R9_PENDING") != "" || os.Getenv("PLAN14_R10_ACQUISITION") != "" || os.Getenv("PLAN14_R10_RETIRED") != "" { |
| cmd.SysProcAttr = &syscall.SysProcAttr{Cloneflags: syscall.CLONE_NEWNS} |
| for _, path := range []string{"/run/carlos", "/run/carlos/hostguard"} { |
| if err := os.Mkdir(path, 0755); err == nil { |
| @@ -112,6 +114,15 @@ func TestRootFilesSeedInstanceControlPayload(t *testing.T) { |
| t.Setenv("LISTEN_FDS", "1") |
| t.Setenv("LISTEN_PID", strconv.Itoa(os.Getpid())) |
| store, resources, owners, _ := fix4JournalFixture(t) |
| + if os.Getenv("PLAN14_R10_ACQUISITION") != "" || os.Getenv("PLAN14_R10_RETIRED") != "" { |
| + openClaimRootAuthority = func() (rootstate.Store, error) { |
| + return rootstate.Open("/var/lib/carlos-claims/authority/fences", true, false) |
| + } |
| + openClaimRootJournal = func() (*hostbox.LaunchJournal, error) { |
| + return hostbox.OpenLaunchJournal("/var/lib/carlos-claims/journals.db") |
| + } |
| + } |
| + |
| resources.OptDir = generationRootRunnerDir(t) |
| dir := t.TempDir() |
| t.Setenv("CARLOS_DEPLOYMENT_DIR", dir) |
| @@ -144,7 +155,40 @@ func TestRootFilesSeedInstanceControlPayload(t *testing.T) { |
| return rootstate.Open(filepath.Join(dir, "release-exclusion"), true, false) |
| }} |
| rootLaunchStartup.Do(func() { |
| - rootLaunchStartup.err = rootparticipation.RegisterLaunchVerifier(func(ctx context.Context, _ uint64) error { return exclusion.Check(ctx) }, exclusion.With) |
| + verify := func(ctx context.Context, _ uint64) error { return exclusion.Check(ctx) } |
| + if os.Getenv("PLAN14_R10_ACQUISITION") != "" || os.Getenv("PLAN14_R10_RETIRED") != "" { |
| + generation, err := rootstate.Open(release.RootGenerationsDir, true, false) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + generation.Close() |
| + read := func(ctx context.Context) (generationInventory, error) { |
| + snapshot, err := productionLocalGenerationInventory(ctx) |
| + if err != nil { |
| + t.Logf("production generation read: %v", err) |
| + return snapshot, err |
| + } |
| + if os.Getenv("PLAN14_R10_RETIRED") != "" { |
| + if len(snapshot.Installations) != 2 || len(snapshot.Payloads) != 2 { |
| + t.Fatalf("lost retained installation or payload: %d %d", len(snapshot.Installations), len(snapshot.Payloads)) |
| + } |
| + installs := map[string]hostguard.FenceView{} |
| + for _, v := range snapshot.Installations { |
| + installs[v.Host] = v |
| + } |
| + if err := generationPayloadBindings(snapshot.Payloads[1:], installs); err == nil { |
| + t.Fatal("retirement dropped payload obligation") |
| + } |
| + } |
| + return snapshot, nil |
| + } |
| + v := rootLaunchVerifier{exclude: exclusion, open: func() (rootstate.Store, error) { return rootstate.Open(release.RootGenerationsDir, false, false) }, reconcile: generationReconciler{read: read, kernel: func(context.Context) (string, string, error) { |
| + boot, err := execlease.BootID() |
| + return boot, floorTestDigest, err |
| + }}} |
| + verify = v.verify |
| + } |
| + rootLaunchStartup.err = rootparticipation.RegisterLaunchVerifier(verify, exclusion.With) |
| }) |
| if rootLaunchStartup.err != nil { |
| t.Fatal(rootLaunchStartup.err) |
| @@ -157,6 +201,15 @@ func TestRootFilesSeedInstanceControlPayload(t *testing.T) { |
| } |
| } |
| } |
| + if os.Getenv("PLAN14_R10_RETIRED") != "" { |
| + for _, host := range []string{"sibling.example", "retired.example"} { |
| + t.Setenv("PLAN14_R10_INSTALL_HOST", host) |
| + filesSeedPendingConstruction(t, release.NewDirStore(dir), resources, owners.ClaimOwnerBindings, actor) |
| + } |
| + } |
| + if os.Getenv("PLAN14_R10_ACQUISITION") != "" { |
| + plan14PendingAcquisition(t, resources.OptDir, actor.ControlBucket) |
| + } |
| RunInstanceControl(nil) |
| t.Fatal("claims-on instance-control returned before serving") |
| } |
diff --git a/internal/host/plan14_r10_root_linux_test.go b/internal/host/plan14_r10_root_linux_test.go| new file mode 100644 |
| index 000000000..02cf3ef6f |
| --- /dev/null |
| +++ b/internal/host/plan14_r10_root_linux_test.go |
| @@ -0,0 +1,288 @@ |
| +//go:build linux && rootrunner |
| + |
| +package host |
| + |
| +import ( |
| + "bytes" |
| + "context" |
| + "errors" |
| + "os" |
| + "os/exec" |
| + "path/filepath" |
| + "testing" |
| + "time" |
| + |
| + "github.com/carlosframework/platform/internal/hostwrite" |
| + "github.com/carlosframework/platform/internal/registry" |
| + "github.com/carlosframework/platform/internal/release" |
| + "github.com/carlosframework/platform/internal/rootparticipation" |
| + "github.com/carlosframework/platform/internal/rootstate" |
| +) |
| + |
| +func TestPlan14NoCensusReleaseRootRunner(t *testing.T) { |
| + if _, err := os.Lstat("/var/lib/carlos-release"); !os.IsNotExist(err) { |
| + t.Fatal("existing release state", err) |
| + } |
| + t.Cleanup(func() { os.RemoveAll("/var/lib/carlos-release") }) |
| + for _, scenario := range []string{"absent", "empty", "held", "aborted"} { |
| + t.Run(scenario, func(t *testing.T) { |
| + var s rootstate.Store |
| + if scenario != "absent" { |
| + var err error |
| + s, err = rootstate.Open(rootReleasePrivate, true, false) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer s.Close() |
| + } |
| + if scenario == "held" || scenario == "aborted" { |
| + unlock, err := s.Lock(t.Context(), "release.lock") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if scenario == "held" { |
| + defer unlock() |
| + } else { |
| + unlock() |
| + } |
| + } |
| + before, _ := os.ReadDir(rootReleasePrivate) |
| + for _, flag := range []string{"", "1"} { |
| + t.Setenv("CARLOS_HOST_CLAIM", flag) |
| + plan14NoCensusCommands(t) |
| + ctx, cancel := context.WithTimeout(t.Context(), time.Second) |
| + called := false |
| + err := withClaimRootRelease(ctx, false, func() (rootstate.Store, error) { t.Fatal("opened root release"); return nil, os.ErrPermission }, func() error { called = true; return nil }, func() (rootparticipation.Ledger, bool, error) { return rootparticipation.Ledger{}, false, nil }) |
| + if err != nil || !called { |
| + t.Fatal("adopt", err, called) |
| + } |
| + for _, recover := range []bool{false, true} { |
| + f := newApplyFixture(t, "canary", "v1.0.0") |
| + f.a.participation = nil |
| + f.a.rootMoves = rootMovesFloorIdentity |
| + if err := f.a.runWithRelease(ctx, recover); err != nil { |
| + t.Fatal("system", recover, err) |
| + } |
| + } |
| + cancel() |
| + } |
| + after, _ := os.ReadDir(rootReleasePrivate) |
| + if len(after) != len(before) { |
| + t.Fatal("new release file", before, after) |
| + } |
| + if scenario == "absent" { |
| + if _, err := os.Lstat(rootReleasePrivate); !os.IsNotExist(err) { |
| + t.Fatal("created release directory", err) |
| + } |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func plan14NoCensusCommands(t *testing.T) { |
| + t.Helper() |
| + for _, mode := range []string{"adopt", "apply", "recover"} { |
| + dir := t.TempDir() |
| + ctx, cancel := context.WithTimeout(t.Context(), 5*time.Second) |
| + cmd := exec.CommandContext(ctx, os.Args[0], "-test.run=^TestPlan14NoCensusCommandPayload$") |
| + cmd.Env = isolatedAdoptOrderEnv(t, "PLAN14_R10_COMMAND="+mode, |
| + "CARLOS_HOST_CLAIM="+os.Getenv("CARLOS_HOST_CLAIM"), "CARLOS_DATA_DIR="+filepath.Join(dir, "data"), |
| + "CARLOS_OPT_DIR="+filepath.Join(dir, "opt"), "CARLOS_DEPLOYMENT_BUCKET=", "CARLOS_DEPLOYMENT_DIR="+filepath.Join(dir, "bucket"), "CARLOS_REGION_HA=", "CARLOS_MOVES_PHASE1=") |
| + out, err := cmd.CombinedOutput() |
| + cancel() |
| + if mode == "adopt" { |
| + var status *exec.ExitError |
| + if !errors.As(err, &status) || status.ExitCode() != 2 || !bytes.Contains(out, []byte("flag provided but not defined: -plan14-no-such-option")) { |
| + t.Fatal("adopt did not reach ordinary argument handling", err, string(out)) |
| + } |
| + } else if err != nil { |
| + t.Fatal(mode, err, string(out)) |
| + } |
| + } |
| +} |
| + |
| +func TestPlan14NoCensusCommandPayload(t *testing.T) { |
| + switch os.Getenv("PLAN14_R10_COMMAND") { |
| + case "adopt": |
| + RunAdopt([]string{"-plan14-no-such-option"}) |
| + case "apply": |
| + RunSystemApply(nil) |
| + case "recover": |
| + RunSystemApply([]string{"-recover"}) |
| + default: |
| + t.Fatal("fixture only") |
| + } |
| +} |
| + |
| +func TestPlan14PendingAcquisitionStartupRootRunner(t *testing.T) { |
| + if err := os.Mkdir("/var/lib/carlos-release", 0700); err != nil { |
| + t.Fatal(err) |
| + } |
| + t.Cleanup(func() { os.RemoveAll("/var/lib/carlos-release") }) |
| + for _, mode := range []string{"instance-control", "system"} { |
| + t.Run(mode, func(t *testing.T) { |
| + t.Setenv("PLAN14_R10_ACQUISITION", mode) |
| + runServingParkPeerNamespace(t, false, false, "seed-construction") |
| + }) |
| + } |
| +} |
| + |
| +func TestPlan14AcquisitionCrashPayload(t *testing.T) { |
| + if os.Getenv("PLAN14_R10_CRASH") != "1" { |
| + t.Fatal("fixture only") |
| + } |
| + s, err := rootstate.Open(rootparticipation.Namespace, false, false) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + l := rootparticipation.Ledger{State: s} |
| + if err := l.Acquire(t.Context(), func() error { os.Exit(0); return nil }); err != nil { |
| + t.Fatal(err) |
| + } |
| + t.Fatal("did not die at durable acquisition") |
| +} |
| + |
| +func plan14PendingAcquisition(t *testing.T, opt, deployment string) { |
| + cmd := exec.Command(os.Args[0], "-test.run=^TestPlan14AcquisitionCrashPayload$") |
| + cmd.Env = append(os.Environ(), "PLAN14_R10_CRASH=1") |
| + if out, err := cmd.CombinedOutput(); err != nil { |
| + t.Fatal(string(out), err) |
| + } |
| + s, err := rootstate.Open(rootparticipation.Namespace, false, false) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer s.Close() |
| + l := rootparticipation.Ledger{State: s} |
| + if _, err := l.Census(); !errors.Is(err, rootparticipation.ErrUnavailable) { |
| + t.Fatal("pending acquisition admitted", err) |
| + } |
| + c, err := l.RecoveryCensus() |
| + if err != nil || c.Pending != "acquisition" { |
| + t.Fatal(c, err) |
| + } |
| + t.Setenv("CARLOS_DEPLOYMENT_BUCKET", deployment) |
| + id, required, err := rootMovesFloorIdentity() |
| + if err != nil || !required { |
| + t.Fatal("floor identity dead end", required, err) |
| + } |
| + t.Setenv("CARLOS_DEPLOYMENT_BUCKET", "") |
| + if os.Getenv("PLAN14_R10_ACQUISITION") == "system" { |
| + f := newApplyFixture(t, "canary", "v1.0.0") |
| + f.a.optDir = opt |
| + f.a.dataDir = "/data" |
| + f.a.rootMoves = func() (rootFloorIdentity, bool, error) { return id, true, nil } |
| + f.a.participation = nil |
| + if err := f.a.runWithRelease(t.Context(), true); err != nil { |
| + t.Fatal("system recovery entrypoint", err) |
| + } |
| + if _, err := l.Census(); err != nil { |
| + t.Fatal("acquisition not settled", err) |
| + } |
| + } |
| +} |
| + |
| +func TestPlan14RetiredInstallationStartupRootRunner(t *testing.T) { |
| + if err := os.Mkdir("/var/lib/carlos-release", 0700); err != nil { |
| + t.Fatal(err) |
| + } |
| + t.Cleanup(func() { os.RemoveAll("/var/lib/carlos-release") }) |
| + for _, mode := range []string{"complete", "interrupted"} { |
| + t.Run(mode, func(t *testing.T) { |
| + t.Setenv("PLAN14_R10_RETIRED", mode) |
| + runServingParkPeerNamespace(t, false, false, "seed-construction") |
| + }) |
| + } |
| +} |
| + |
| +func TestPlan14RetiredMigrationPayloadRootRunner(t *testing.T) { |
| + testRootMigrationBootstrap(t, true, false) |
| +} |
| + |
| +func TestRootRebootPlan14ReviewBatchPhase1(t *testing.T) { plan14ReviewRebootBatch(t, "1") } |
| +func TestRootRebootPlan14ReviewBatchPhase2(t *testing.T) { plan14ReviewRebootBatch(t, "2") } |
| +func plan14ReviewRebootBatch(t *testing.T, phase string) { |
| + if phase == "1" { |
| + cmd := exec.CommandContext(t.Context(), os.Args[0], "-test.run=^(TestPlan14LocalBackingStateRootRunner|TestPlan14NoCensusReleaseRootRunner|TestPlan14InitialMigrationExclusionRootRunner|TestPlan14RetiredInstallationStartupRootRunner|TestPlan14RetiredMigrationPayloadRootRunner|TestPlan14PendingInventoryStartupRootRunner|TestPlan14RootMigrationBootstrapRootRunner|TestPlan14RootMigrationHistoricRootRunner|TestPlan14RootMigrationFreshStopRootRunner|TestPlan14RootRetryHardenedController|TestPlan14RootFailureProductionOpener)$", "-test.v", "-test.timeout=5m") |
| + out, err := cmd.CombinedOutput() |
| + t.Log(string(out)) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + plan14RebootGroups(t, phase, []string{"(InventoryIdentity|Plan14Migration|PersistedFamilies|ClaimUnitMigration|GenerationPinIdentity|GenerationDevices|ClaimUnitDevice|FloorPrevAuthorization|ServingParkChangedMount)"}) |
| +} |
| + |
| +func TestRootRebootPlan14AcceptanceSeedBatchPhase1(t *testing.T) { |
| + cmd := exec.CommandContext(t.Context(), os.Args[0], "-test.run=^(TestPlan14NoCensusReleaseRootRunner|TestPlan14InitialMigrationExclusionRootRunner)$", "-test.v", "-test.timeout=2m") |
| + out, err := cmd.CombinedOutput() |
| + t.Log(string(out)) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + plan14RebootGroups(t, "1", []string{"ServingParkAcceptance", "FilesSeedTwoHost"}) |
| +} |
| +func TestRootRebootPlan14AcceptanceSeedBatchPhase2(t *testing.T) { |
| + plan14RebootGroups(t, "2", []string{"ServingParkAcceptance", "FilesSeedTwoHost"}) |
| +} |
| +func plan14RebootGroups(t *testing.T, phase string, groups []string) { |
| + for _, group := range groups { |
| + cmd := exec.CommandContext(t.Context(), os.Args[0], "-test.run=^TestRootReboot"+group+"Phase"+phase+"$", "-test.v", "-test.timeout=8m") |
| + out, err := cmd.CombinedOutput() |
| + t.Log(string(out)) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| +} |
| + |
| +func TestPlan14LocalBackingStateRootRunner(t *testing.T) { |
| + if err := os.Mkdir("/var/lib/private", 0700); err == nil { |
| + t.Cleanup(func() { os.Remove("/var/lib/private") }) |
| + } else if !os.IsExist(err) { |
| + t.Fatal(err) |
| + } |
| + for _, dir := range []string{"/var/lib/carlos-app", "/var/lib/private/carlos-app"} { |
| + t.Run(dir, func(t *testing.T) { |
| + if err := os.Mkdir(dir, 0755); err != nil { |
| + t.Fatal("existing backing state", err) |
| + } |
| + t.Cleanup(func() { os.RemoveAll(dir) }) |
| + if err := os.Mkdir(filepath.Join(dir, "hidden.example"), 0700); err != nil { |
| + t.Fatal(err) |
| + } |
| + reg := restartTestRegistry(t) |
| + for _, attack := range []string{"omitted", "cleared"} { |
| + if attack == "cleared" { |
| + if err := reg.Upsert(registry.Route{Host: "hidden.example", Account: "bab", App: "app", Socket: "/run/hidden.sock"}); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + p := &activationPreflightOwner{} |
| + scratch := t.TempDir() |
| + r := ClaimRuntimeResources{Mode: "instance-control", Registry: reg, DataDir: scratch, OptDir: scratch, UnitsDir: scratch} |
| + store := release.NewDirStore(t.TempDir()) |
| + if _, err := OpenProductionClaimAssembly(t.Context(), store, r, p); !errors.Is(err, hostwrite.ErrUnavailable) || p.calls != 0 { |
| + t.Fatal("local backing omitted", attack, err, p.calls) |
| + } |
| + if err := migrateClaimJournalsWith(t.Context(), store, r, p, true); !errors.Is(err, hostwrite.ErrUnavailable) || p.calls != 0 { |
| + t.Fatal("migration backing omitted", attack, err, p.calls) |
| + } |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestRootRebootPlan14FinalSuitePhase1(t *testing.T) { |
| + cmd := exec.CommandContext(t.Context(), os.Args[0], "-test.run="+"^(TestClaimRestoreOwnWriteMaterializes|TestClaimRestoreWriteOnlyRetagsPair|TestFilesSeedCLIStopFixture|TestFilesSeedExecutorLifetime|TestFilesSeedLiveFixtureShape|TestPlan14ActivationNamedUnitPreflight|TestPlan14BootLocalRecoveryRootRunner|TestPlan14BoundNetworkEdgeRootRunner|TestPlan14BoundRouteProgramRootRunner|TestPlan14BoundStartupRetirementRootRunner|TestPlan14CombinedBoundRetirementRootRunner|TestPlan14ConsoleLibraryRootRunner|TestPlan14ExecutionHostileCensusRootRunner|TestPlan14FloorRiseWithoutPlatformUpdateRootRunner|TestPlan14GenerationBackendInstalledRootRunner|TestPlan14GenerationContainedRunningRootRunner|TestPlan14GenerationDescriptorTransferRootRunner|TestPlan14GenerationExactRetirementRootRunner|TestPlan14GenerationInstalledUnitRootRunner|TestPlan14GenerationInventoryCheckedRootReader|TestPlan14GenerationKernelCensusRootRunner|TestPlan14GenerationKernelNamespaceMountRootRunner|TestPlan14GenerationKernelRetirementRootRunner|TestPlan14GenerationParkedRootRunner|TestPlan14GenerationPayloadPhysicalRootRunner|TestPlan14GenerationPrivateJournalRootRunner|TestPlan14GenerationRelocationUIDRootRunner|TestPlan14GenerationRequiredArtifactsRootRunner|TestPlan14GenerationRunningTenantPinRootRunner|TestPlan14GuardExecEdgeRootRunner|TestPlan14GuardRecoveryLegacyPolicyRootRunner|TestPlan14InitialMigrationExclusionRootRunner|TestPlan14InstalledClaimLaunchRootRunner|TestPlan14LaunchVerifierRootRunner|TestPlan14LocalBackingStateRootRunner|TestPlan14MaterializedBoundRouteRootRunner|TestPlan14MigratedBoundRouteRootRunner|TestPlan14NamedClaimLaunchRootRunner|TestPlan14NamedStartupDefersReplacementRootRunner|TestPlan14NoCensusReleaseRootRunner|TestPlan14OrdinaryUnitAuthorizationRootRunner|TestPlan14PendingAcquisitionStartupRootRunner|TestPlan14PendingInventoryStartupRootRunner|TestPlan14RealConsoleRootRunner|TestPlan14RealWoodstarRootRunner|TestPlan14RebootStopDescriptorIdentity|TestPlan14RepairExecutorEndToEndRootRunner|TestPlan14RetiredInstallationStartupRootRunner|TestPlan14RetiredMigrationPayloadRootRunner|TestPlan14RootCapabilitiesBothPaths|TestPlan14RootFailureProductionOpener|TestPlan14RootFloorAdoptRefreshRootRunner|TestPlan14RootFloorBootSelectors|TestPlan14RootFloorCrashOrderingRootRunner|TestPlan14RootFloorHealthyAdoptRootRunner|TestPlan14RootFloorRecoveryRollbackRootRunner|TestPlan14RootFloorSignedTargetRootRunner|TestPlan14RootFloorSyscallCrashOrdering|TestPlan14RootFloorUnknownCrashRecoveryRootRunner|TestPlan14RootInventoryPersistentProducer|TestPlan14RootInventoryRejectsOldBootMaterialization|TestPlan14RootJournalNamespaceRootRunner|TestPlan14RootMigrationBootstrapRootRunner|TestPlan14RootMigrationFreshStopRootRunner|TestPlan14RootMigrationHistoricRootRunner|TestPlan14RootMigrationLedgerExclusionRootRunner|TestPlan14RootMigrationMaskRootRunner|TestPlan14RootMigrationRootRunner|TestPlan14RootMigrationTimerAdoptionRootRunner|TestPlan14RootMigrationUnarmedRootRunner|TestPlan14RootRetryHardenedController|TestPlan14RootRetryProjectionDelivery|TestPlan14RootRuntimeInstalledCompletion|TestPlan14RootRuntimeInstalledFailure|TestPlan14RootSeedsFloorRootRunner|TestPlan14RootSeedsFloorSyscallCrashOrdering|TestPlan14SwapFailureHostileRootRunner|TestPlan14SwapFailureNoCensusRootRunner|TestPlan14TimerIntentionRootRunner|TestPlan14TimerRetirementRootRunner|TestPlan14UnitBindingRootRunner|TestRootFilesSeedCombinedRecoveryAllowsUpdate|TestRootFilesSeedDelegatedController|TestRootFilesSeedDelegatedOneShot|TestRootFilesSeedInstanceControlConstruction|TestRootFilesSeedIsolatedTenant|TestRootFilesSeedIsolationNegativeControl|TestRootFilesSeedKilledControllerRerun|TestRootFilesSeedLiveTransport|TestRootFilesSeedRealOriginPreparation|TestRootFilesSeedRealOriginTenant|TestRootFilesSeedSocketCompleteExecutor|TestRootFilesSeedSocketDisconnectExecutor|TestRootFilesSeedSocketShutdownExecutor|TestRootFilesSeedTwoHostIsolation|TestRootFilesSeedWholeExecutorKilledRerun|TestRootServingHandoverDirectoryProof|TestRootServingNamespaceStartup|TestRootServingNamespaceUnrecordedStartup|TestRootServingParkAuthenticatedHandover|TestRootServingParkAuthenticatedPeer|TestRootServingParkColdHandoverBoundary|TestRootServingParkCollection|TestRootServingParkDisplacedRoot|TestRootServingParkInstalledUnit|TestRootServingParkNestedBindRestore|TestRootServingParkQuarantine|TestRootServingParkRetainedDoesNotBlockRetirement|TestRootServingParkSnapshotClone|TestRootServingParkStoppedWriterHandover|TestRootServingParkUnrecordedReplacement)$", "-test.v", "-test.timeout=25m") |
| + out, err := cmd.CombinedOutput() |
| + t.Log(string(out)) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + plan14RebootGroups(t, "1", []string{"FilesSeedTwoHostRenumber"}) |
| +} |
| + |
| +func TestRootRebootPlan14FinalSuitePhase2(t *testing.T) { |
| + plan14RebootGroups(t, "2", []string{"FilesSeedTwoHostRenumber"}) |
| +} |
diff --git a/internal/host/plan14_r9_root_linux_test.go b/internal/host/plan14_r9_root_linux_test.go| index 33fa26bc3..ab152f31e 100644 |
| --- a/internal/host/plan14_r9_root_linux_test.go |
| +++ b/internal/host/plan14_r9_root_linux_test.go |
| @@ -75,25 +75,40 @@ func plan14InitialMigrationExclusion(t *testing.T, store release.Store, r ClaimR |
| old := rootMigrationQuiesce |
| defer func() { rootMigrationQuiesce = old }() |
| rootMigrationQuiesce = func(context.Context) (func(), error) { |
| - for _, path := range []string{"adopt", "update"} { |
| - ctx, cancel := context.WithTimeout(t.Context(), 40*time.Millisecond) |
| - ran := false |
| - var err error |
| - if path == "adopt" { |
| - err = withClaimRootRelease(ctx, false, func() (rootstate.Store, error) { return rootstate.Open(rootReleasePrivate, true, false) }, func() error { ran = true; return nil }, func() (rootparticipation.Ledger, bool, error) { return rootparticipation.Ledger{}, false, nil }) |
| - } else { |
| - err = withExistingRootRelease(ctx, func() error { ran = true; return nil }) |
| - } |
| - cancel() |
| - if err == nil || ran { |
| - t.Fatal("initial migration did not exclude", path, err) |
| - } |
| + if err := verifyOfflineRootMigration(t.Context()); err != nil { |
| + t.Fatal(err) |
| } |
| return nil, os.ErrPermission |
| } |
| if err := migrateRootClaimAuthority(t.Context(), store, r, owners, actor, journals, launches); !errors.Is(err, os.ErrPermission) { |
| t.Fatal("exclusion witness", err) |
| } |
| + before, err := os.ReadDir(rootReleasePrivate) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + for _, flag := range []string{"", "1"} { |
| + t.Setenv("CARLOS_HOST_CLAIM", flag) |
| + plan14NoCensusCommands(t) |
| + if err := withClaimRootRelease(t.Context(), false, func() (rootstate.Store, error) { |
| + t.Fatal("release opened after aborted migration") |
| + return nil, os.ErrPermission |
| + }, func() error { return nil }); err != nil { |
| + t.Fatal(err) |
| + } |
| + for _, recover := range []bool{false, true} { |
| + f := newApplyFixture(t, "canary", "v1.0.0") |
| + f.a.participation = nil |
| + f.a.rootMoves = rootMovesFloorIdentity |
| + if err := f.a.runWithRelease(t.Context(), recover); err != nil { |
| + t.Fatal("legacy apply after aborted migration", recover, err) |
| + } |
| + } |
| + } |
| + after, err := os.ReadDir(rootReleasePrivate) |
| + if err != nil || len(after) != len(before) { |
| + t.Fatal("new release state after abort", err, before, after) |
| + } |
| } |
| func TestPlan14RepairExecutorEndToEndRootRunner(t *testing.T) { |
| @@ -202,6 +217,10 @@ func TestPlan14RepairExecutorEndToEndRootRunner(t *testing.T) { |
| } |
| func TestPlan14PendingInventoryStartupRootRunner(t *testing.T) { |
| + if err := os.Mkdir("/var/lib/carlos-release", 0700); err != nil { |
| + t.Fatal(err) |
| + } |
| + t.Cleanup(func() { os.RemoveAll("/var/lib/carlos-release") }) |
| for _, mode := range []string{"instance-control", "system"} { |
| t.Run(mode, func(t *testing.T) { |
| t.Setenv("PLAN14_R9_PENDING", mode) |
| @@ -242,7 +261,11 @@ func filesSeedPendingConstruction(t *testing.T, store release.Store, resources C |
| t.Fatal(err) |
| } |
| t.Cleanup(func() { unix.Unmount("/etc/systemd/system", 0) }) |
| - f := newRecoveryFixture(t) |
| + host := os.Getenv("PLAN14_R10_INSTALL_HOST") |
| + if host == "" { |
| + host = recoveryHost |
| + } |
| + f := newRecoveryFixture(t, host) |
| obs := f.read() |
| obs.Claim.Op.Exec = actor.Box |
| obs.Claim.Op.Participants = []string{actor.Box} |
| @@ -297,23 +320,70 @@ func filesSeedPendingConstruction(t *testing.T, store release.Store, resources C |
| if err != nil { |
| t.Fatal(err) |
| } |
| + if os.Getenv("PLAN14_R10_RETIRED") != "" { |
| + if err := checkClaimJournalMigration(t.Context(), journals, filepath.Join("/data", ".hostclaims")); err != nil { |
| + t.Fatal("journal startup before installation", err) |
| + } |
| + } |
| state, err := openClaimRootAuthority() |
| if err != nil { |
| t.Fatal(err) |
| } |
| defer state.Close() |
| - boundary := &r9PendingBoundary{Store: state} |
| + boundary := &r9PendingBoundary{Store: state, failed: os.Getenv("PLAN14_R10_RETIRED") != ""} |
| authority := hostguard.NewRootAuthority(boundary) |
| roots := map[string]string{"data": "/data", "opt": resources.OptDir, "units": "/etc/systemd/system"} |
| guard := hostguard.Guard{Root: authority, DataDir: "/data", Claims: hostguard.StoreReader{Store: store}} |
| census := &hostbox.EmptyInstallationCensus{Inventory: journals.Inventory, Registry: reg, Launches: journal, Box: actor.Box, Enrollment: actor.Enrollment, DirectoryRevision: "1", Roots: roots, SharedPaths: claimGlobalSharedPaths(resources)} |
| - root := ClaimRootHTTP{Journals: journals, Launches: journal, Roots: roots, InventoryActor: func(context.Context) (hostbox.Actor, error) { return actor, nil }, Reconcile: hostbox.ReconcileAdapter{Registry: reg, Inventory: journals.Inventory, Census: census, Box: actor.Box, Enrollment: actor.Enrollment}, Stop: ClaimStopHTTP{Coordinator: ClaimStopCoordinator{Guard: guard, Evidence: store}}} |
| + root := ClaimRootHTTP{history: productionCreationHistory, Journals: journals, Launches: journal, Roots: roots, InventoryActor: func(context.Context) (hostbox.Actor, error) { return actor, nil }, Reconcile: hostbox.ReconcileAdapter{Registry: reg, Inventory: journals.Inventory, Census: census, Box: actor.Box, Enrollment: actor.Enrollment}, Stop: ClaimStopHTTP{Coordinator: ClaimStopCoordinator{Guard: guard, Evidence: store}}} |
| _, client := hostbox.RemoteJournals(func(ctx context.Context, q hostbox.JournalRequest) (hostbox.JournalResponse, error) { |
| out, err := root.commitRootInventory(ctx, q) |
| t.Logf("pending commit: %v", err) |
| return out, err |
| }) |
| _, err = client.WriteExpected(t.Context(), hostbox.StopInventory{Complete: true, Host: obs.Host, Incarnation: h.Incarnation, InstallationStamp: h.Stamp, Box: actor.Box, Enrollment: actor.Enrollment, DirectoryRevision: "1"}, hostbox.InventoryExpectation{RecordKey: h.Key, RecordETag: etag, Target: hostguard.FenceTarget{Host: obs.Host, Incarnation: h.Incarnation, Stamp: h.Stamp, Session: h.Session, Box: actor.Box, Enrollment: actor.Enrollment, ClaimETag: obs.ETag, Op: obs.Claim.Op.ID, Phase: obs.Claim.Op.Phase}}) |
| + if os.Getenv("PLAN14_R10_RETIRED") != "" { |
| + if err != nil { |
| + t.Fatal("complete installation", err) |
| + } |
| + if obs.Host == "sibling.example" { |
| + if err := guard.With(t.Context(), obs.Host, hostguard.Captured{Incarnation: h.Incarnation, Stamp: h.Stamp}, func(*hostclaim.Claim) error { return nil }, func() error { return nil }); err != nil { |
| + t.Fatal("sibling admission", err) |
| + } |
| + return |
| + } |
| + want := hostguard.Captured{Incarnation: h.Incarnation, Stamp: h.Stamp} |
| + in, err := authority.Installation(t.Context(), obs.Host, want) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + interrupted := os.Getenv("PLAN14_R10_RETIRED") == "interrupted" |
| + err = guard.RetireInstallationWith(t.Context(), obs.Host, want, func(*hostclaim.Claim) error { return nil }, func() error { |
| + if interrupted { |
| + return os.ErrPermission |
| + } |
| + return nil |
| + }) |
| + if interrupted != errors.Is(err, os.ErrPermission) { |
| + t.Fatal("retirement boundary", err) |
| + } |
| + state.Close() |
| + reopened, err := openClaimRootAuthority() |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer reopened.Close() |
| + authority = hostguard.NewRootAuthority(reopened) |
| + retained, err := authority.Installation(t.Context(), obs.Host, want) |
| + if err != nil || retained != in { |
| + t.Fatal("retired identity lost", retained, in, err) |
| + } |
| + guard.Root = authority |
| + if err := guard.With(t.Context(), obs.Host, want, func(*hostclaim.Claim) error { return nil }, func() error { t.Fatal("retired admitted"); return nil }); err == nil { |
| + t.Fatal("retired admitted") |
| + } |
| + return |
| + } |
| if err == nil || !boundary.failed { |
| t.Fatal("pending boundary not reached", err) |
| } |
diff --git a/internal/host/rootmigration_bootstrap_root_linux_test.go b/internal/host/rootmigration_bootstrap_root_linux_test.go| index 733d5d84d..a13c292a0 100644 |
| --- a/internal/host/rootmigration_bootstrap_root_linux_test.go |
| +++ b/internal/host/rootmigration_bootstrap_root_linux_test.go |
| @@ -8,6 +8,7 @@ import ( |
| "crypto/ed25519" |
| "encoding/base64" |
| "encoding/json" |
| + "errors" |
| "os" |
| "os/exec" |
| "path/filepath" |
| @@ -39,6 +40,13 @@ func testRootMigrationBootstrap(t *testing.T, historic, fresh bool) { |
| if !historic { |
| generationRunnerRegistryUser(t) |
| } |
| + for _, name := range rootMigrationOfflineUnits { |
| + path := filepath.Join("/run/systemd/system", name) |
| + if err := os.Symlink("/dev/null", path); err != nil { |
| + t.Fatal(err) |
| + } |
| + t.Cleanup(func() { os.Remove(path); exec.Command("systemctl", "daemon-reload").Run() }) |
| + } |
| unit := "/usr/lib/systemd/system/carlos-edge.service" |
| for _, p := range []string{unit, "/etc/systemd/system/carlos-edge.service", "/run/systemd/system/carlos-edge.service"} { |
| if _, err := os.Lstat(p); !os.IsNotExist(err) { |
| @@ -60,6 +68,21 @@ func testRootMigrationBootstrap(t *testing.T, historic, fresh bool) { |
| t.Fatal(err, string(out)) |
| } |
| + if err := verifyOfflineRootMigration(t.Context()); err != nil { |
| + t.Fatal("offline root units", err) |
| + } |
| + properties := map[string]string{} |
| + for _, name := range rootMigrationOfflineUnits { |
| + out, err := claimSystemctl(t.Context(), "show", name, "--property=LoadState,ActiveState", "--no-pager") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + properties[name] = string(out) |
| + if out, err := exec.Command("systemctl", "start", name).CombinedOutput(); err == nil || !bytes.Contains(out, []byte("masked")) { |
| + t.Fatal(name, string(out), err) |
| + } |
| + } |
| + savedProperties, _ := json.Marshal(properties) |
| oldOwner := claimQuiesceOwner |
| claimQuiesceOwner = func() (int, int, error) { return 65534, 65534, nil } |
| defer func() { claimQuiesceOwner = oldOwner }() |
| @@ -77,11 +100,11 @@ func testRootMigrationBootstrap(t *testing.T, historic, fresh bool) { |
| if fresh { |
| name = "TestPlan14RootMigrationFreshStopRootRunner" |
| } |
| - if t.Name() == "TestRootRebootPlan14MigrationInstalledPhase1" || t.Name() == "TestPlan14InitialMigrationExclusionRootRunner" { |
| + if t.Name() == "TestRootRebootPlan14MigrationInstalledPhase1" || t.Name() == "TestPlan14InitialMigrationExclusionRootRunner" || t.Name() == "TestPlan14RetiredMigrationPayloadRootRunner" { |
| name = t.Name() |
| } |
| cmd := exec.CommandContext(t.Context(), "unshare", "--mount", "--pid", "--fork", "--mount-proc", os.Args[0], "-test.run=^"+name+"$", "-test.v") |
| - cmd.Env = append(os.Environ(), "PLAN14_P3_BOOTSTRAP_CHILD=1") |
| + cmd.Env = append(os.Environ(), "PLAN14_P3_BOOTSTRAP_CHILD=1", "PLAN14_OFFLINE_UNITS="+string(savedProperties)) |
| if out, err := cmd.CombinedOutput(); err != nil { |
| t.Fatal(err, string(out)) |
| } else { |
| @@ -97,6 +120,13 @@ func testRootMigrationBootstrap(t *testing.T, historic, fresh bool) { |
| t.Fatal(err) |
| } |
| } |
| + claimSystemctl = func(ctx context.Context, args ...string) ([]byte, error) { |
| + var properties map[string]string |
| + if json.Unmarshal([]byte(os.Getenv("PLAN14_OFFLINE_UNITS")), &properties) != nil || len(args) < 2 || args[0] != "show" || properties[args[1]] == "" { |
| + return nil, os.ErrPermission |
| + } |
| + return []byte(properties[args[1]]), nil |
| + } |
| rootMigrationQuiesce = func(context.Context) (func(), error) { return func() {}, nil } |
| for _, path := range []string{"/var/lib/carlos-claims", "/var/lib/carlos-release"} { |
| if err := os.Mkdir(path, 0700); err != nil { |
| @@ -341,6 +371,32 @@ func testRootMigrationBootstrap(t *testing.T, historic, fresh bool) { |
| if err := g.With(t.Context(), target.Host, captured, allow, launch); err != nil || effects != 1 { |
| t.Fatal("successor writer", err, effects) |
| } |
| + if t.Name() == "TestPlan14RetiredMigrationPayloadRootRunner" { |
| + if err := g.RetireInstallationWith(t.Context(), target.Host, captured, allow, func() error { return os.ErrPermission }); !errors.Is(err, os.ErrPermission) { |
| + t.Fatal("retirement interrupt", err) |
| + } |
| + fences.Close() |
| + if err := verifyRootMigrationStartup(t.Context(), id, service.Inventory); err != nil { |
| + t.Fatal("retired migration startup", err) |
| + } |
| + read := func(ctx context.Context) (generationInventory, error) { |
| + return generationInventoryAt(ctx, rootparticipation.Namespace, "/var/lib/carlos-claims/authority/fences", "/var/lib/carlos-claims/journals.db") |
| + } |
| + snapshot, err := read(t.Context()) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if len(snapshot.Installations) != 1 || snapshot.Installations[0].Installation.Stamp != captured.Stamp || len(snapshot.Payloads) == 0 || len(snapshot.Payloads[0].Inventory.Entries) == 0 { |
| + t.Fatal("retired payload lost", snapshot) |
| + } |
| + verifier := generationReconciler{read: read, kernel: productionGenerationKernelCensus} |
| + if _, err := verifier.reconcile(t.Context(), nil); err != nil { |
| + t.Fatal("retired generation verification", err) |
| + } |
| + if _, err := os.Stat(filepath.Join(r.DataDir, "historic-data")); err != nil { |
| + t.Fatal("payload removed", err) |
| + } |
| + } |
| if t.Name() == "TestRootRebootPlan14MigrationInstalledPhase1" || t.Name() == "TestPlan14InitialMigrationExclusionRootRunner" { |
| if err := journal.Close(); err != nil { |
| t.Fatal(err) |
diff --git a/internal/host/rootmigration_production_linux.go b/internal/host/rootmigration_production_linux.go| index 39152adcb..3dc7e87d4 100644 |
| --- a/internal/host/rootmigration_production_linux.go |
| +++ b/internal/host/rootmigration_production_linux.go |
| @@ -32,7 +32,7 @@ func migrateRootClaimAuthority(ctx context.Context, store release.Store, r Claim |
| if err := claimActivationVerified(ctx, store, r); err != nil { |
| return err |
| } |
| - if err := quiesceLegacyRootMutators(ctx); err != nil { |
| + if err := verifyOfflineRootMigration(ctx); err != nil { |
| return err |
| } |
| state, err := rootstate.Open(rootMigrationDir, true, false) |
| @@ -279,3 +279,19 @@ func quiesceLegacyRootMutators(ctx context.Context) error { |
| } |
| return nil |
| } |
| + |
| +var rootMigrationOfflineUnits = []string{"carlos-adopt.service", "carlos-stage.service", "carlos-config-sync.service", "carlos-restart.service", "carlos-system-update.service", "carlos-system-recover.service"} |
| + |
| +func verifyOfflineRootMigration(ctx context.Context) error { |
| + for _, unit := range rootMigrationOfflineUnits { |
| + raw, err := claimSystemctl(ctx, "show", unit, "--property=LoadState,ActiveState", "--no-pager") |
| + if err != nil { |
| + return err |
| + } |
| + props := string(raw) |
| + if !strings.Contains(props, "LoadState=masked\n") || !(strings.Contains(props, "ActiveState=inactive\n") || strings.Contains(props, "ActiveState=failed\n")) { |
| + return errRootMigrationBusy |
| + } |
| + } |
| + return quiesceLegacyRootMutators(ctx) |
| +} |
diff --git a/internal/host/rootmigration_startup.go b/internal/host/rootmigration_startup.go| index 63f4514be..4f5413261 100644 |
| --- a/internal/host/rootmigration_startup.go |
| +++ b/internal/host/rootmigration_startup.go |
| @@ -79,7 +79,7 @@ func verifyRootMigrationStartup(ctx context.Context, id rootFloorIdentity, inven |
| if err != nil || !required { |
| return err |
| } |
| - c, err := l.Census() |
| + c, err := l.RecoveryCensus() |
| if err != nil { |
| return err |
| } |
diff --git a/internal/host/rootmovesfloor_source.go b/internal/host/rootmovesfloor_source.go| index 2fcff9fd1..2f12845f0 100644 |
| --- a/internal/host/rootmovesfloor_source.go |
| +++ b/internal/host/rootmovesfloor_source.go |
| @@ -22,7 +22,7 @@ func rootMovesFloorIdentity() (rootFloorIdentity, bool, error) { |
| if err != nil || !required { |
| return rootFloorIdentity{}, required, err |
| } |
| - c, err := l.Census() |
| + c, err := l.RecoveryCensus() |
| id := rootFloorIdentity{Deployment: os.Getenv("CARLOS_DEPLOYMENT_BUCKET"), Box: c.Box, Enrollment: c.Enrollment} |
| if err == nil && id.Deployment == "" { |
| err = errRootFloorHistory |
diff --git a/internal/host/rootreleaselock.go b/internal/host/rootreleaselock.go| index 39ff11cc3..32a072037 100644 |
| --- a/internal/host/rootreleaselock.go |
| +++ b/internal/host/rootreleaselock.go |
| @@ -2,7 +2,6 @@ package host |
| import ( |
| "context" |
| - "errors" |
| "github.com/carlosframework/platform/internal/rootparticipation" |
| "os" |
| @@ -37,7 +36,7 @@ func withClaimReleaseCensus(ctx context.Context, census func() (rootparticipatio |
| return err |
| } |
| if !required { |
| - return withExistingRootRelease(ctx, effect) |
| + return effect() |
| } |
| return withRootReleaseStore(ctx, open, effect) |
| } |
| @@ -55,20 +54,3 @@ type adoptRootSource struct { |
| OpenRelease func() (rootstate.Store, error) |
| Census func() (rootparticipation.Ledger, bool, error) |
| } |
| - |
| -func withExistingRootRelease(ctx context.Context, effect func() error) error { |
| - if _, err := os.Lstat(rootReleasePrivate); errors.Is(err, os.ErrNotExist) { |
| - return effect() |
| - } else if err != nil { |
| - return err |
| - } |
| - s, err := rootstate.Open(rootReleasePrivate, false, false) |
| - if errors.Is(err, os.ErrNotExist) { |
| - return effect() |
| - } |
| - if err != nil { |
| - return err |
| - } |
| - defer s.Close() |
| - return rootstate.WithRelease(ctx, s, effect) |
| -} |
diff --git a/internal/host/systemapply.go b/internal/host/systemapply.go| index 2865e0a3c..9a959dadf 100644 |
| --- a/internal/host/systemapply.go |
| +++ b/internal/host/systemapply.go |
| @@ -135,7 +135,7 @@ func (a *systemApplier) runWithRelease(ctx context.Context, recover bool) error |
| } |
| return a.participationDisk().WithParticipation(ctx, func(ctx context.Context, _ rootparticipation.Ledger) error { |
| return a.Run(ctx, recover) |
| - }, func() error { return withExistingRootRelease(ctx, func() error { return a.Run(ctx, recover) }) }) |
| + }, func() error { return a.Run(ctx, recover) }) |
| } |
| // openSystemStore is the production openStore: the deploy store and the |
diff --git a/internal/host/systemparticipation.go b/internal/host/systemparticipation.go| index cbf244a95..268a66731 100644 |
| --- a/internal/host/systemparticipation.go |
| +++ b/internal/host/systemparticipation.go |
| @@ -201,7 +201,14 @@ func (a *systemApplier) checkUnarmedParticipationRecovery(ctx context.Context) e |
| } |
| recover, reserve := false, false |
| err = a.participationDisk().WithParticipation(ctx, func(ctx context.Context, l rootparticipation.Ledger) error { |
| - _, err := l.UpdateID() |
| + c, err := l.RecoveryCensus() |
| + if err != nil { |
| + return err |
| + } |
| + if c.Pending == "acquisition" { |
| + recover = true |
| + } |
| + _, err = l.UpdateID() |
| if errors.Is(err, os.ErrNotExist) { |
| if j == nil { |
| return nil |
diff --git a/internal/hostguard/generationinventory.go b/internal/hostguard/generationinventory.go| index 5298344b0..e0c6c527b 100644 |
| --- a/internal/hostguard/generationinventory.go |
| +++ b/internal/hostguard/generationinventory.go |
| @@ -51,7 +51,7 @@ func (a *RootAuthority) generationInstallation(ctx context.Context, host string) |
| if err != nil { |
| return FenceView{}, err |
| } |
| - view, err := a.viewLocked(host, Captured{Incarnation: r.Incarnation, Stamp: r.ActiveStamp}) |
| + view, err := a.viewLocked(host, Captured{Incarnation: r.Incarnation, Stamp: r.installation.Stamp}) |
| if err != nil { |
| return FenceView{}, err |
| } |
diff --git a/internal/hostguard/retired_inventory_test.go b/internal/hostguard/retired_inventory_test.go| new file mode 100644 |
| index 000000000..6af88d7f9 |
| --- /dev/null |
| +++ b/internal/hostguard/retired_inventory_test.go |
| @@ -0,0 +1,60 @@ |
| +//go:build unix |
| + |
| +package hostguard |
| + |
| +import ( |
| + "context" |
| + "encoding/json" |
| + "errors" |
| + "os" |
| + "testing" |
| +) |
| + |
| +func TestPlan14RetiredInstallationEnumeration(t *testing.T) { |
| + for _, oldFormat := range []bool{false, true} { |
| + t.Run(map[bool]string{false: "retained", true: "old-cleared-stamp"}[oldFormat], func(t *testing.T) { |
| + f, target, captured := fenceFixture(t) |
| + store := &rootTestStore{dir: t.TempDir()} |
| + f.g.Root = NewRootAuthority(store) |
| + f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "app" |
| + in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "app", Record: "record", Box: target.Box, Enrollment: target.Enrollment, Migration: 1, InventoryRevision: 7, Artifacts: InstallationArtifacts{Schema: 1, StateDirectory: true, Timers: "kept"}} |
| + if err := f.g.EstablishRoot(t.Context(), in, allow); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := f.g.RetireInstallationWith(t.Context(), target.Host, captured, allow, func() error { return os.ErrPermission }); !errors.Is(err, os.ErrPermission) { |
| + t.Fatal(err) |
| + } |
| + if oldFormat { |
| + raw, err := store.Read(target.Host + ".json") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + var r rootRecord |
| + if err := json.Unmarshal(raw, &r); err != nil { |
| + t.Fatal(err) |
| + } |
| + r.Installation.Stamp = 0 |
| + raw, _ = json.Marshal(r) |
| + if err := store.Write(target.Host+".json", raw); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + f.g.Root = NewRootAuthority(&rootTestStore{dir: store.dir}) |
| + for _, enumerate := range []func(context.Context) ([]FenceView, error){f.g.Root.MigrationInstallations, f.g.Root.GenerationInstallations} { |
| + views, err := enumerate(t.Context()) |
| + if err != nil || len(views) != 1 || views[0].Installation != in { |
| + t.Fatal("retired inventory lost", views, err) |
| + } |
| + if err := views[0].admit(target.Host, captured, target.Session); err == nil { |
| + t.Fatal("retired view admitted") |
| + } |
| + } |
| + if err := f.g.With(t.Context(), target.Host, captured, allow, func() error { t.Fatal("retired execution"); return nil }); err == nil { |
| + t.Fatal("retired execution admitted") |
| + } |
| + if err := f.g.RetireInstallationWith(t.Context(), target.Host, captured, allow, func() error { return nil }); err != nil { |
| + t.Fatal("cleanup retry", err) |
| + } |
| + }) |
| + } |
| +} |
diff --git a/internal/hostguard/root.go b/internal/hostguard/root.go| index 116049f71..ac080d6a4 100644 |
| --- a/internal/hostguard/root.go |
| +++ b/internal/hostguard/root.go |
| @@ -65,10 +65,26 @@ func (a *RootAuthority) read(host string) (record, error) { |
| var saved rootRecord |
| dec := json.NewDecoder(bytes.NewReader(b)) |
| dec.DisallowUnknownFields() |
| - if dec.Decode(&saved) != nil || dec.Decode(new(any)) != io.EOF || saved.Schema != 1 || saved.Installation.Host != host || saved.Installation.Incarnation != saved.State.Incarnation || saved.Installation.Stamp != saved.State.ActiveStamp || saved.Installation.Migration == 0 { |
| + if dec.Decode(&saved) != nil || dec.Decode(new(any)) != io.EOF || saved.Schema != 1 || saved.Installation.Host != host || saved.Installation.Incarnation != saved.State.Incarnation || saved.Installation.Migration == 0 { |
| return record{}, ErrMalformedRecord |
| } |
| r := saved.State |
| + if r.ActiveStamp != 0 { |
| + if saved.Installation.Stamp != r.ActiveStamp { |
| + return record{}, ErrMalformedRecord |
| + } |
| + } else { |
| + // Older retired records cleared both stamps. Recover only the last |
| + // exact root-retained tuple, never an agent supplied installation. |
| + if len(saved.Retired) == 0 { |
| + return record{}, ErrMalformedRecord |
| + } |
| + last := saved.Retired[len(saved.Retired)-1] |
| + if last.Incarnation != r.Incarnation || last.Stamp == 0 || (saved.Installation.Stamp != 0 && saved.Installation.Stamp != last.Stamp) { |
| + return record{}, ErrMalformedRecord |
| + } |
| + saved.Installation.Stamp = last.Stamp |
| + } |
| r.migrationDenies = saved.MigrationDenies |
| for _, deny := range r.migrationDenies { |
| if deny.Incarnation == "" || deny.Stamp == 0 || deny.Session == 0 { |
| @@ -95,7 +111,10 @@ func (a *RootAuthority) write(r record) error { |
| if a == nil || a.store == nil || !r.root || r.validate(r.Host) != nil { |
| return ErrMalformedRecord |
| } |
| - r.installation.Host, r.installation.Incarnation, r.installation.Stamp = r.Host, r.Incarnation, r.ActiveStamp |
| + r.installation.Host, r.installation.Incarnation = r.Host, r.Incarnation |
| + if r.ActiveStamp != 0 { |
| + r.installation.Stamp = r.ActiveStamp |
| + } |
| b, err := json.Marshal(rootRecord{MigrationDenies: r.migrationDenies, Quarantined: r.quarantined, RebindCommit: r.rebindCommit, Schema: 1, Installation: r.installation, Retired: r.rootRetired, Barrier: r.barrier, PendingInventory: r.pendingInventory, PendingInstallation: r.pendingInstallation, State: r}) |
| if err != nil { |
| return err |
diff --git a/internal/hostguard/rootmigration_inventory.go b/internal/hostguard/rootmigration_inventory.go| index 6095077be..9048dbc40 100644 |
| --- a/internal/hostguard/rootmigration_inventory.go |
| +++ b/internal/hostguard/rootmigration_inventory.go |
| @@ -30,7 +30,7 @@ func (a *RootAuthority) MigrationInstallations(ctx context.Context) ([]FenceView |
| if err != nil { |
| return nil, err |
| } |
| - view, err := a.viewLocked(host, Captured{Incarnation: r.Incarnation, Stamp: r.ActiveStamp}) |
| + view, err := a.viewLocked(host, Captured{Incarnation: r.Incarnation, Stamp: r.installation.Stamp}) |
| if err != nil { |
| return nil, err |
| } |
diff --git a/internal/provision/testdata/plan14-root-reads-p3.tsv b/internal/provision/testdata/plan14-root-reads-p3.tsv| index f7c50d92e..534ccc61d 100644 |
| --- a/internal/provision/testdata/plan14-root-reads-p3.tsv |
| +++ b/internal/provision/testdata/plan14-root-reads-p3.tsv |
| @@ -24,11 +24,11 @@ internal/host/rootmigration_production_linux.go migrationExecutionEmpty authorit |
| internal/host/rootmigration_production_linux.go publishMigrationReceipt authority G,J4 rev3=PROPOSED-p3;identity=carlos system journal-migrate, offline operator CLI, uid 0;basis=reconstructs immutable key/digest from original signed receipt bytes without changing evidence time db91dbe64b57fe10aa1635fb46938e2ccb585afb6aa138800507acac6a83c377 |
| internal/host/rootmigration_production_linux.go randomMigrationID authority G,J4 rev3=PROPOSED-p3;identity=carlos system journal-migrate, offline operator CLI, uid 0;basis=cryptographic nonce identifies newly collected stop evidence, never authorizes a stop file:Read@64 c2779c80b5628eca7d484ffba0b289c1068317b1f474c06e1ee5b39c3f769196 |
| internal/host/rootmigration_production_linux.go rootMigrationLegacySuspects authority G,J4 rev3=PROPOSED-p3;identity=carlos system journal-migrate, offline operator CLI, uid 0;basis=agent-writable hostguard filenames only identify unexplained hosts to refuse, never authorize inventory file:Readdirnames@277 6bfa73d903baa8a028abb007a7ec1da0066dbcda966e92e29b50a2b56fda10ab |
| -internal/host/rootmigration_production_other.go migrateRootClaimAuthority authority G,J4 rev3=PROPOSED-p3;run=13;identity=offline migration uid 0;basis=takes mandatory release exclusion even before census exists, rechecks canonical cohort eligibility, and refuses active legacy root mutators before state/census transition 327e0adfe387eac7bc74b87258efc9831f5a35ee9c9c9e71640d4e6b54853aae |
| +internal/host/rootmigration_production_other.go migrateRootClaimAuthority authority G,J4 rev3=PROPOSED-p3;run=15;identity=non-Linux offline root migration;basis=unsupported-platform stub refuses only; performs no locking, opening or publication 327e0adfe387eac7bc74b87258efc9831f5a35ee9c9c9e71640d4e6b54853aae |
| internal/host/rootmigration_startup.go var openRootMigrationParticipation authority G,J4 rev3=PROPOSED-p3;identity=root claim controller construction uid 0;basis=fixed production participation opener remains rootparticipation.OpenForUpdate; package tests substitute explicit no-census fixtures without accessing the machine store 53a17d908b433d98cdb817ab72543bd53788962625ccc9cfa0e48f829bbc1aa3 |
| internal/host/rootmigration_startup.go verifyRootMigrationFloors authority G,F,J4 rev3=PROPOSED-p3;run=13;identity=root startup/migration uid 0;basis=requires certified current and guard repair executors and readable local bounds; a bound above installed executors emits the existing alert rather than blocking repair construction 7e558cfe3164a5a2d25a88dad074b85e1a23325ae98e777dac6c6c3543bc1228 |
| internal/host/rootmigration_startup.go verifyRootMigrationRetained authority G,F,J4 rev3=PROPOSED-p3;identity=root claim controller construction uid 0;basis=requires each migrated installation to retain its old-session deny, exact stopped predecessor receipt and complete root inventory f2dc230494f4659e6d4bf71961d617854454bd8f713585a1cd4489414c288cb1 |
| -internal/host/rootmigration_startup.go verifyRootMigrationStartup authority G,F,J4 rev3=PROPOSED-p3;identity=root claim controller construction uid 0;basis=checks root-owned migration records and typed journal revisions before issuing startup grants; absent census leaves main path intact file:Open@337 b5149f8db369dbf474612ec6f6afe55c518f337f619479bdfeb648f518d4c5af |
| +internal/host/rootmigration_startup.go verifyRootMigrationStartup authority G,F,J4 rev3=PROPOSED-p3;run=15;identity=root instance-control startup and recovery uid 0;basis=validates retained recovery census and migration identity, including pending acquisition, then complete installation inventory and repair floors; execution admission remains behind verified broker settlement and complete census file:Open@345 6508eb50ecb7cc517041c7f5eb431a597c282274945c0dab2542e6416b3edc9e |
| internal/host/rootmigration_startup.go verifyRootMigrationState authority G,F,J4 rev3=PROPOSED-p3;run=13;identity=root startup/recovery uid 0;basis=validates retained migration header then complete installations after pending recovery has settled 531c7d47066a7dfad787aed67f08ba6078091c7b9426d8f685872c4948c2d2ba |
| internal/host/rootmigration_stopped_linux.go verifyMigrationStopped authority G,J4 rev3=PROPOSED-p3;identity=carlos system journal-migrate offline operator uid 0;basis=root checks real proc/cgroup mounts, closed writer journal and absence of cgroups and held inventory descriptors; all observations can only refuse recovery file:ReadFile@588 06b82196857608ace8bfaa8ce7108caad961868b530a0d9cb152326a1aaf6819 |
| internal/host/rootmovesfloor.go openRootMovesFloors authority F,J4 rev3=PROPOSED-p3;identity=root adopt refresh, root system update/recovery and root guard floor readers, uid 0;basis=selects fixed moves namespace then delegates checked root store opening 4af0bda66779af3474e64fda54015763761d058feead1b8a2ebf0c2478a05a3d |
| @@ -38,7 +38,7 @@ internal/host/rootmovesfloor.go rootMovesFloors.persist authority F,J4 rev3=PROP |
| internal/host/rootmovesfloor.go rootMovesFloors.projection authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover and root adopt, systemd root units or offline CLI, uid 0;basis=matches public acceptance to private generation, identity and retained componentwise maximum file:Read@109 6b0ed0053db247385ccbabfd95a738fd48a8d27b14882b5e73b4e5a82eda26b6 |
| internal/host/rootmovesfloor.go rootMovesFloors.reconstruct authority F,J4 rev3=PROPOSED-p3;identity=offline root reconstruction capability, uid 0; no production caller installed;basis=requires private root-issued box/generation/nonce token and refuses weakening; no production caller installed 1a6b2923d55208b8a53cfd17d3cf84f01087485f7b5cadc9012cb1b123120038 |
| internal/host/rootmovesfloor.go rootMovesFloors.record authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover and root adopt, systemd root units or offline CLI, uid 0;basis=bounded strict private JSON read requires matching deployment, box and enrollment file:Read@108 2e2bc3e9e915e4a0a70c40933de3928284bbdab25ee223e4cf05500a12913b83 |
| -internal/host/rootmovesfloor.go rootMovesFloors.refresh authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover and root adopt, systemd root units or offline CLI, uid 0;basis=normal refresh ratchets accepted history; unknown history persists only observed denies and never advertises accepted cb70a1eefbb4f8f243090270f23141c9c9a773ee278af002c79a6c3ef6b7c3b7 |
| +internal/host/rootmovesfloor.go rootMovesFloors.refresh authority F,J4 rev3=PROPOSED-p3;run=15;identity=normal root adopt/update refresh uid 0;basis=normal refresh ratchets accepted history; unknown history persists only observed denies and never advertises accepted cb70a1eefbb4f8f243090270f23141c9c9a773ee278af002c79a6c3ef6b7c3b7 |
| internal/host/rootmovesfloor.go rootMovesFloors.selector authority F,J4 rev3=PROPOSED-p3;identity=root apply/recover/adopt units uid 0 and agent edges publisher carlos-edge.service uid carlos;basis=strict bounded canonical protocol/release line supplies independent crash-surviving deny file:Read@73 5c46438590323610c67dc5bbfaa191fc5ba23e3b280b6063dbd48e48e7ce3927 |
| internal/host/rootmovesfloor_authorization.go rootMovesFloors.authorizeReconstruction authority F,J4 rev3=PROPOSED-p3;identity=offline root reconstruction capability, uid 0; no production caller installed;basis=issues private nonce capability for this root identity and next generation under stable lock; no production caller file:Read@426 a5235f6aab5714cee3e3e41604eed5b1eff7aa4355f9444f44c4811f7dfbbc08 |
| internal/host/rootmovesfloor_authorization.go rootMovesFloors.authorizedReconstruction authority F,J4 rev3=PROPOSED-p3;identity=offline root reconstruction capability, uid 0; no production caller installed;basis=requires exact private persisted nonce, box identity and next generation; caller strings cannot authorize file:Read@249 c449d7087a6cbcdf4388b7945e84045a0b895349b1bc14347aca7040c822391c |
| @@ -47,13 +47,13 @@ internal/host/rootmovesfloor_boot.go rootFloorBootCandidate authority F,J4 rev3= |
| internal/host/rootmovesfloor_boot.go rootFloorJournalPresent authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover and root adopt, systemd root units or offline CLI, uid 0;basis=checked authority-file presence selects recovery without opening a deployment store b189d04dc579f4825f5668efd1f93f178acca738bb2ed886a14f3b5321d7755b |
| internal/host/rootmovesfloor_boot.go rootFloorPrevVerdict authority F,J4 rev3=PROPOSED-p3;run=13;identity=root boot/recovery selector uid 0;basis=reauthorizes previous bytes against signed root identities and accepted verdict; boot-local device and inode numbers cannot reject unchanged signed repair bytes file:Read@436 ad5acd71daa00ffbe34fe9b3cb7224b4d6ef1d9b21d1f3e6b2d1d213c88e2e4e |
| internal/host/rootmovesfloor_boot.go withRootFloorGuardCandidate authority F,J4 rev3=PROPOSED-p3;identity=carlos system guard-exec apply/recover, systemd root units uid 0;basis=release then previous-binary exclusion; participation check precedes moves and seeds locks; certified current remains executor fallback file:Open@218 74d00903cf028a74237153d25879f768a3bd8546cb107d85b4d11feb7f77e04c |
| -internal/host/rootmovesfloor_identity.go rememberRootFloorSystemIdentity authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover and root adopt, systemd root units or offline CLI, uid 0;basis=re-fetches and verifies signed pointer equality before retaining its executable identity file:Open@543 b4e56a0637c28e5fb65d3a33b7dfd1fea8bed55acbf5231bf26e0011168141fe |
| +internal/host/rootmovesfloor_identity.go rememberRootFloorSystemIdentity authority F,J4 rev3=PROPOSED-p3;run=15;identity=normal root apply pointer verification uid 0;basis=called from apply pointer verification; re-fetches signed pointer equality before retaining executable identity; never recovery or adopt file:Open@543 b4e56a0637c28e5fb65d3a33b7dfd1fea8bed55acbf5231bf26e0011168141fe |
| internal/host/rootmovesfloor_refresh.go readRootFloorBinary authority F,J4 rev3=PROPOSED-p3;identity=root adopt refresh, root system update/recovery and root guard floor readers, uid 0;basis=verifies checked executable and signed system manifest; reads signed moves and seeds protocol numbers rather than version sidecar file:Open@684,file:Read@941,file:Stat@276 2a23dd9c46c6fa56476ecfa4061712f05c6f1a1a098295e14f395def2d24324a |
| internal/host/rootmovesfloor_refresh.go rootFloorAdvertisementFor authority F,J4 rev3=PROPOSED-p3;identity=carlos agent edgesFileLoop, carlos-edge.service uid carlos;basis=agent reports accepted only for matching generation with signed compatible current and previous verdict file:Read@203 4e464b6add24a06b1c3d7a5859be7a1055750bc25f27c8ca641241be3baa2ef8 |
| internal/host/rootmovesfloor_refresh.go rootFloorBinary.supports authority F,J4 rev3=PROPOSED-p3;identity=root apply/recover/adopt units uid 0 and agent edges publisher carlos-edge.service uid carlos;basis=signed digest/release are required; protocol minimum applies only when a retained floor exists, preserving unknown-history permission 7a6eaf5432f2668aa47f24d9c667355a985f83eadb8c4619a704168288b16fbf |
| internal/host/rootmovesfloor_refresh.go rootFloorDowngradeIdle authority D,E2,F,J4 rev3=PROPOSED-p3;identity=root adopt, guard boot selector and system apply/recover uid 0;basis=uses E network-free retained generation obligations and kernel census under participation before floor; borrowed transaction matches the original root store; no deployment provider read file:Open@171,file:Open@612 d49e47d72f752d492a4c56ed73d4af53ffaacd5e95cbc147998e173fcadf2793 |
| internal/host/rootmovesfloor_refresh.go rootFloorJudge.judge authority F,J4 rev3=PROPOSED-p3;identity=carlos adopt normal refresh and carlos system apply notePrev, uid 0;basis=observes participation before floor lock; writes identity-bound current/prev verdict even under unknown history without blocking repair executor 95062652c0403183b18c074da17d7e4c504f7fd6deab7148668a2dea03711e9c |
| -internal/host/rootmovesfloor_refresh.go rootFloorJudge.refresh authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover and root adopt, systemd root units or offline CLI, uid 0;basis=refreshes retained floor before signed current/previous judgement, even when previous is absent 362798a60b4e3d6fc798db52bfc204979f724efe33a858f3d7ca2c1ca12f6eaa |
| +internal/host/rootmovesfloor_refresh.go rootFloorJudge.refresh authority F,J4 rev3=PROPOSED-p3;run=15;identity=normal root adopt/update refresh uid 0;basis=refreshes retained floor before signed current/previous judgement, even when previous is absent 362798a60b4e3d6fc798db52bfc204979f724efe33a858f3d7ca2c1ca12f6eaa |
| internal/host/rootmovesfloor_refresh.go rootRefreshAndJudgeMovesPrev authority F,J4 rev3=PROPOSED-p3;identity=carlos adopt root pass, root unit uid 0;basis=adopt opens deployment store then refreshes floor before any no-prev return 2d4d1ce1d6143555f3b94b19359c014ffb20272f41f756f7c02bbf6ff29a70a9 |
| internal/host/rootmovesfloor_refresh.go withRootMovesFields authority F,J4 rev3=PROPOSED-p3;identity=carlos agent edgesFileLoop, carlos-edge.service uid carlos;basis=agent publishes only checked root public verdict and explicitly quarantines unreadable participant state file:Open@405 29fb652e22df888f18c691b91db6b83b9781d89d005a2fae431d8102e77b2f88 |
| internal/host/rootmovesfloor_source.go readEffectiveMovesFloor authority F,J4 rev3=PROPOSED-p3;identity=root adopt refresh, root system update/recovery and root guard floor readers, uid 0;basis=census chooses local root protocol bound; no census chooses existing legacy reader a6420f9074a459fa5107cbd60ca18828f0dec132eb53add849152db2a9b0f62a |
| @@ -61,9 +61,9 @@ internal/host/rootmovesfloor_source.go readRootFloorProjection authority F,J4 re |
| internal/host/rootmovesfloor_source.go readRootLegacyProtocolJSON authority F,J4 rev3=PROPOSED-p3;identity=carlos adopt and system apply refresh, uid 0; no recovery or boot-selector call;basis=bounded descriptor read of optional legacy moves or seeds mirror; shape validation and caller denial-only merge prevent acceptance file:ReadAll@441,file:Stat@278 835f913509edbe65d72986bb23e7af580aaf2d4cc222e87064e29d0d16e61103 |
| internal/host/rootmovesfloor_source.go readRootProtocolBound authority F,J4 rev3=PROPOSED-p3;identity=root adopt refresh, root system update/recovery and root guard floor readers, uid 0;basis=reads local root accepted and retained deny state; public-only deny survives missing private history; missing unknown history is not acceptance file:Open@187 2e35ae8bdee1433877d928f22b4a3315dfd2027378a71124ca361284323c22b2 |
| internal/host/rootmovesfloor_source.go readRootProtocolProjection authority F,J4 rev3=PROPOSED-p3;identity=carlos agent in carlos-edge.service uid carlos;basis=checked public root projection must agree with selector and directory sync before advertising acceptance file:Open@86,file:Read@232 36f45f87d3cabebaaf759e2cd2b3c4397a5247247e329a0fd71de291ad1a2bf0 |
| -internal/host/rootmovesfloor_source.go refreshRootMovesFloor authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover and root adopt, systemd root units or offline CLI, uid 0;basis=normal adopt/update combines remote object with independent deny-only legacy evidence under root floor lock a41df2c69e7d869b5232d545440b8135e8c7124d490965e641b8d1fba83cb490 |
| +internal/host/rootmovesfloor_source.go refreshRootMovesFloor authority F,J4 rev3=PROPOSED-p3;run=15;identity=normal root adopt/update refresh uid 0;basis=normal adopt/update combines remote object with independent deny-only legacy evidence under root floor lock a41df2c69e7d869b5232d545440b8135e8c7124d490965e641b8d1fba83cb490 |
| internal/host/rootmovesfloor_source.go rootFloorLegacy authority F,J4 rev3=PROPOSED-p3;identity=carlos adopt and system apply refresh, uid 0; no recovery or boot-selector call;basis=selects existing moves mirror paths for denial-only generic legacy reader e5dacb9fa26be5f5f961e99909624f38643f93c43432ff3dce1cff552f7e486a |
| -internal/host/rootmovesfloor_source.go rootMovesFloorIdentity authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover and root adopt, systemd root units or offline CLI, uid 0;basis=root-only census reader supplies deployment/box/enrollment; edge mode cannot call it 457cdc76e2cff8fdfe4d146c7baf086dd58d54f151ce9416c32efd0a03778d98 |
| +internal/host/rootmovesfloor_source.go rootMovesFloorIdentity authority F,J4 rev3=PROPOSED-p3;run=15;identity=root update/recovery/guard uid 0;basis=identifies root deployment and enrollment from retained RecoveryCensus even during pending acquisition; identifies floor authority without granting execution admission 440cbd8eb3dd525bf55eb0bdd131825565ef5c15003326399e83f84747033f7c |
| internal/host/rootmovesfloor_source.go rootProtocolLegacy authority F,J4 rev3=PROPOSED-p3;identity=carlos adopt and system apply refresh, uid 0; no recovery or boot-selector call;basis=bounded agent-writable JSON and line mirrors contribute only increasing denies; neither can establish accepted state file:ReadAll@729,file:Stat@537 0b62a121d683312c34a5c7af486d60d496482ef25502e8e2ef0ef18b5e7c6fd6 |
| internal/host/rootmovesfloor_source.go withEffectiveMovesFloorLock authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover final activation, uid 0;basis=locks moves then seeds private root stores across final activation; no census uses unchanged legacy lock 752817cc4fc1110aa055324bedb2b9e2b7da7f87445b48c8290ebb53ea3d85d7 |
| internal/host/rootseedsfloor.go judgeRootSeedsPrev authority F,J4 rev3=PROPOSED-p3;identity=carlos adopt and system apply refresh, uid 0; no recovery or boot-selector call;basis=evaluates signed current and prev seeds support under shared durable floor judge and independent downgrade completeness 79ac6f71f9588f9f7757663ca7a9bc2d014253ff93153949ec30fd17d3cbc9d4 |
| @@ -77,9 +77,9 @@ internal/host/rootseedsfloor.go rootSeedsFloorDir authority F,J4 rev3=PROPOSED-p |
| internal/host/rootseedsfloor.go systemApplier.rootProtocolAllowsBinary authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover, root systemd units or offline root CLI, uid 0;basis=final installation restore or forward-completion checks signed image against both local root bounds while activation locks are held 9e0321c02fe9ea45685804b9435f340f75117d9e444c98b794676ea66103955e |
| internal/host/rootseedsfloor.go systemApplier.seedsActivationRefusal authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover, root systemd units or offline root CLI, uid 0;basis=refuses activation below selected local seeds release bound; missing unknown history grants no acceptance projection abc430ca5063fde4bcfdd0a795c1f593324c0dce6f6d9330abbb519917e07aa7 |
| internal/host/rootseedsfloor.go withRootSeedsFields authority F,J4 rev3=PROPOSED-p3;identity=carlos agent in carlos-edge.service uid carlos;basis=advertises only checked synced root seeds projection matching deployment and box; no valid projection stays quarantined file:Open@413,file:Read@531 f53f45621ab1e7bba07315980de981b321826b5985350489ee686d952480bc13 |
| -internal/host/seedsfloor.go systemApplier.armSeedsFloor authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover, root systemd units or offline root CLI, uid 0;basis=normal apply refreshes root seeds history on census participants; nonparticipants retain legacy sync; never called by recovery or boot ace92bbe72e79e998daeebfe5fc2380ce6174cc0241b60a26e0093bea614bce1 |
| +internal/host/seedsfloor.go systemApplier.armSeedsFloor authority F,J4 rev3=PROPOSED-p3;run=15;identity=normal root apply uid 0;basis=normal apply refreshes root seeds history on census participants; nonparticipants retain legacy sync; never called by recovery or boot ace92bbe72e79e998daeebfe5fc2380ce6174cc0241b60a26e0093bea614bce1 |
| internal/host/seedsfloor.go withSeedsFields authority F,J4 rev3=PROPOSED-p3;identity=carlos agent in carlos-edge.service uid carlos;basis=root participation selects checked root projection; legacy agent mirror remains unchanged for nonparticipants file:Lstat@84 57febc09559f80119ba36ca13188febf0ceb27bcdc29d829403798aba1cb6f1d |
| -internal/host/systemapply.go RunSystemApply authority D,F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover root systemd units uid 0;basis=registers local launch verifier and delegates to the census-gated outer release wrapper while retaining local root floor checks and main no-census path 9111d10530cef84980095152bb1a8be5c3299c6fcdc2c0a50b3055957541f0d8 |
| +internal/host/systemapply.go RunSystemApply authority D,F,J4 rev3=PROPOSED-p3;run=15;identity=root apply/recovery uid 0;basis=registers local verifier and invokes census-gated release wrapper; absent census executes main apply directly even with leftover release state 9111d10530cef84980095152bb1a8be5c3299c6fcdc2c0a50b3055957541f0d8 |
| internal/host/systemapply.go systemApplier.activationGuard authority D,F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover root units and offline root CLI uid 0;basis=release exclusion then scoped participation transaction then effective moves/seeds floor and registry; signed candidate checks remain inside guard; nested journal close reuses live exclusion whole:Route@1315 9d8cb52184b6fd2c4a071962515b0162a99af758954c18b72384e8e9c29fd7c8 |
| internal/host/systemapply.go systemApplier.activationRule authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover, root systemd units or offline root CLI, uid 0;basis=preliminary activation refuses against both local protocol bounds before existing hold checks whole:Route@1036,whole:Route@827,whole:Route@834 d7a045107010e06f1b93f2920b42d0b464c6a0a71ed5cad0d15c2473fd81c191 |
| internal/host/systemapply.go systemApplier.armFloor authority F,J4 rev3=PROPOSED-p3;identity=carlos system apply/recover, root systemd units or offline root CLI, uid 0;basis=normal update refreshes root moves and seeds histories in census mode; unknown history remains unacknowledged; no recovery or boot refresh d7e16eaaa3994896bf9acd763777cc4d8998056dfa2d492c5320395102009a16 |
| @@ -97,12 +97,12 @@ internal/host/units/carlos-system-update.service external authority F,J4 rev3=PR |
| internal/hostbox/migration_census_linux.go CheckMigrationRoots authority G,J4 rev3=PROPOSED-p3;identity=root claim retirement or offline migration uid 0; shared helpers also run for legacy agent requests without root authority;basis=checks retained materialized roots against persistent filesystem evidence before census migration accepts coverage;rev3=PROPOSED-p3;run=11;identity=root claim, migration or adoption controller uid 0;basis=merges E FileIdentity callers and P3 authority effects; retains the recorded family checks and both tracks behavioral fixtures file:Lstat@932,file:WalkDir@1202 a9491adef92ee5948b6d8840b047f2ead713c043c65c499c124d5bda62ce51fc |
| internal/hostguard/guard.go record.admits authority G,J4 rev3=PROPOSED-p3;identity=root claim controller construction uid 0;basis=root admission retains migration denial until the exact predecessor session has a completed retained receipt 5864d1fa79a66ca6f69057416079b46524ed4bfdf1ce1de88124b5a24a428418 |
| internal/hostguard/root.go Guard.CommitRootInventoryArtifacts authority G,J4 rev3=PROPOSED-p3;identity=root claim controller construction uid 0;basis=root inventory commit admits a newer migration writer only after its exact predecessor completed; old sessions stay denied 08e42f8810e2645a875b5177fc9e104ed4b307c7d80dc77d204e2516b1bd66de |
| -internal/hostguard/root.go RootAuthority.read authority C,G,J4 rev3=PROPOSED-p3;identity=carlos instance-control, root control service uid 0; denial views also consumed by carlos agent uid carlos;basis=strict checked root record decoding includes retained migration session denials file:Read@175 7ff862f573a05a17951546a56ff791d42962d693331a69e8338bd8eb4f283d6b |
| -internal/hostguard/root.go RootAuthority.write authority C,G,J4 rev3=PROPOSED-p3;identity=carlos instance-control, root control service uid 0; denial views also consumed by carlos agent uid carlos;basis=retains migration denials atomically with the existing root installation, fence and retired-state record 678156528bb5d3e92f8c07835ff7a7d05afb5d498ff604966d2b6d9293867c0c |
| +internal/hostguard/root.go RootAuthority.read authority C,G,J4 rev3=PROPOSED-p3;run=15;identity=root control/startup/generation uid 0;basis=validates active stamp equality; inactive identity is the exact last root-retained retired tuple, including repair of older records with cleared installation stamp file:Read@175 5c2c9ab014458e086f6df9255e1d967a96fd78cd1e599b891d8369c8506421f8 |
| +internal/hostguard/root.go RootAuthority.write authority C,G,J4 rev3=PROPOSED-p3;run=15;identity=root control uid 0;basis=persists inactive state while retaining historical installation stamp, registration and artifacts; only a nonzero active stamp replaces the retained stamp 5f6db65d9de7b98b119f5f124189e0322336835c9c80fb6c253bb828cfc9e9d2 |
| internal/hostguard/rootmigration.go RootAuthority.RetainMigrationDeny authority C,G,J4 rev3=PROPOSED-p3;identity=carlos system journal-migrate, offline operator CLI, uid 0;basis=root retains old incarnation/stamp/session denial under stable per-host lock; grants no launch authority fb821d19b6ca6e1454d025013d27ef8a856a684262421d3ee76804723e875c37 |
| internal/hostguard/rootmigration.go migrationAdmissionDenied authority G,J4 rev3=PROPOSED-p3;identity=root claim controller construction uid 0;basis=old migrated sessions remain denied; a newer same-installation session requires completed receipt for every retained migration predecessor c7312f5e0e67b2b69bbea1b8f432dd48ba06f0b4a26ebf349b6724769651631e |
| internal/hostguard/rootmigration.go migrationDenied authority C,G,J4 rev3=PROPOSED-p3;identity=carlos instance-control, root control service uid 0; denial views also consumed by carlos agent uid carlos;basis=old sessions at/below retained captured stamp refuse root and agent admission 6ccf5c00e33d880aa50ea15d2bce22a3cce14455d5cc7f05197e42f789da8805 |
| -internal/hostguard/rootmigration_inventory.go RootAuthority.MigrationInstallations authority C,G,J4 rev3=PROPOSED-p3;run=13;identity=root migration/startup uid 0;basis=enumerates retained installations including zero-revision inventory; pending installation transactions are refused and must first settle through restricted startup recovery ad855b461cb12e5499ecec74b210f6f41a758a3ed36c0eac0ef5931bc7fad6c2 |
| +internal/hostguard/rootmigration_inventory.go RootAuthority.MigrationInstallations authority C,G,J4 rev3=PROPOSED-p3;run=15;identity=root migration/startup uid 0;basis=enumerates active and retired installations from preserved root installation identity; incomplete inventory remains visible, pending transactions must settle before complete enumeration 8c4dee4b5af31ce13c581b20c5cde79f1917720b80e558dda6766cf8548f2a02 |
| internal/hostguard/rootview.go FenceView.admit authority G,J4 rev3=PROPOSED-p3;identity=root claim controller construction uid 0;basis=read-only root view applies same predecessor-completion requirement as locked admission 5bcc6b69bb3e98a1bda6ace2b0b2a147f8c39b61601c609c30e3fb806231efae |
| internal/hostguard/rootview.go Guard.fenceLocked authority C,G,J4 rev3=PROPOSED-p3;identity=carlos instance-control, root control service uid 0; denial views also consumed by carlos agent uid carlos;basis=projects root-only migration denials into agent admission snapshot without trusting local hostguard records 290295ed69e06548a41e101317098ae2d6aac10c41b260e120af55860686a96d |
| internal/hostguard/rootview.go RootAuthority.viewLocked authority C,G,J4 rev3=PROPOSED-p3;identity=carlos instance-control, root control service uid 0; denial views also consumed by carlos agent uid carlos;basis=projects root migration denials together with canonical installation and existing fences 5db6824b38f6ad3e4072008ffa3ff9fab98db6a5d40ecba59ca30900e331a46f |
| @@ -141,7 +141,7 @@ internal/rootparticipation/transaction.go Ledger.lockTransaction authority D,F,J |
| internal/rootparticipation/transaction.go TransactionLedger authority D,F,J4 rev3=PROPOSED-p3;identity=root system apply/recover uid 0;basis=returns only an active root-minted held ledger; stale and cancelled scopes refuse 2046fbd7b7ec9455794dbaf614ea4cb50b9147929004434a172a67656fb7ff63 |
| internal/rootparticipation/transaction.go type heldTransaction authority D,F,J4 rev3=PROPOSED-p3;identity=root system apply/recover uid 0;basis=binds transaction reuse to a root-opened ledger and live callback lifetime 4769022372ba86be2255c15a53c5d7acd88bc9e50652391e84284a436ae615c7 |
| internal/rootparticipation/transaction.go type transactionKey authority D,F,J4 rev3=PROPOSED-p3;identity=root system apply/recover uid 0;basis=unexported key prevents callers from forging the held participation scope c309e812e20a85927d3cc93c8048d811c7187137797ad76646cc12f892d82a63 |
| -internal/host/systemapply.go systemApplier.runWithRelease authority D,F,J4 rev3=PROPOSED-p3;run=13;identity=root update/recovery uid 0;basis=census updates retain participation release exclusion; legacy updates honor explicit migration exclusion before acquiring apply lock ebdeb0eeb9e4300e3309961994efccab531fc9d2b10fb078f4c34e875d905106 |
| +internal/host/systemapply.go systemApplier.runWithRelease authority D,F,J4 rev3=PROPOSED-p3;run=15;identity=root update/recovery uid 0;basis=retained floor identity permits restricted acquisition recovery; participating execution holds release exclusion; no-census effect is main Run directly 462d91eb211467885431108bc352b1913ea430fca5602434fc9fd7ef39713efa |
| internal/hostfloor/disk.go Disk.withParticipation authority D,F,J4 rev3=PROPOSED-p3;identity=root system apply/recover or root participation caller uid 0;basis=uses a live root-minted scoped ledger only for its original store; otherwise opens the ordinary census; absent census keeps legacy behavior 3bf4a54d1ab3621eee974a797ffa63a988369b9f245ceddde1cb99cc6386ec82 |
| internal/rootparticipation/ledger.go Ledger.Idle authority D,F,J4 rev3=PROPOSED-p3;identity=root system apply/recover or root participation caller uid 0;basis=checks settled execution and local launch completeness under participation; nested activation uses only its already-held store e50d8f839d39f771ad4b6018f315cd7002a6e470424df0a46482906c31ef575e |
| internal/rootparticipation/ledger.go Ledger.RecoverAcquisition authority D,F,J4 rev3=PROPOSED-p3;identity=root system apply/recover or root participation caller uid 0;basis=retains acquisition-recovery checks and census commit under the same participation transaction; no new authority from a missing record d1f44274ba0a53aae7e39ce016da787748123665bda0cdf7a40339a4fc52e9fc |
| @@ -189,7 +189,7 @@ internal/rootparticipation/ledger.go Ledger.RecoverAcquisition authority D,F,J4 |
| @removed internal/rootparticipation/production.go ActivationReady D,G census-ruling-J4 |
| internal/historyreadback/readback.go RequestStatus diagnostic E1 rev3=PROPOSED-p3;identity=diagnostic request library, no production caller;basis=P3-R8 #2; signed readback request only; no reconstruction or retirement d89052b0cdd12c8c8dcbc353e9ae995005caeb88d8e4c70974cb9e6f4bfd46d7 |
| internal/hostbox/migration_census_other.go CheckMigrationRoots hint G,J4 rev3=PROPOSED-p3;identity=non-Linux offline migration caller;basis=P3-R8 #17; unsupported platform refusal only 594ecda260fcf80baf5a2709c9cc5a04e91c655c8bd2f7ac307eef44216eb9c8 |
| -internal/host/rootmigration_production_linux.go migrateRootClaimAuthority authority G,J4,E3 rev3=PROPOSED-p3;run=13;identity=offline migration uid 0;basis=takes mandatory release exclusion even before census exists, rechecks canonical cohort eligibility, and refuses active legacy root mutators before state/census transition file:Open@1768,file:Open@374,file:Open@614,file:Open@7062,file:Open@738,file:Read@2720,file:ReadFile@1570,whole:Route@3033 752f5c566102d49a27c01c8d43ec2638a64b09b7b1868662ef652fca5f6d5f6a |
| +internal/host/rootmigration_production_linux.go migrateRootClaimAuthority authority G,J4,E3 rev3=PROPOSED-p3;run=15;identity=offline root migration uid 0;basis=requires masked inactive root mutator units and no running legacy root command under mandatory release exclusion; ordinary nonparticipants acquire no migration lock file:Open@1769,file:Open@374,file:Open@615,file:Open@7063,file:Open@739,file:Read@2721,file:ReadFile@1571,whole:Route@3034 9be47c70f4fb2a1d3c280814525085b261e9514e79df6f8918a0e67f9e6724d8 |
| internal/host/rootmigration_timers.go rootMigrationTimers.adopt authority G,J4 rev3=PROPOSED-p3;identity=offline migration root uid 0;basis=offline migration binds the exact observed pre-claim registrations to root retained stop evidence and denies; durable intention precedes conditional registry adoption and root inventory completion registry:ClaimOwner@1997,registry:ClaimOwner@2135,registry:ClaimOwnership@1860,registry:ClaimOwnership@1914,registry:ClaimOwnership@4009,registry:ClaimOwnership@4202,registry:ClaimOwnership@4314,registry:Host@1836,registry:Host@4107,registry:Name@1677,registry:Name@4122,registry:Registration@1153,registry:Registration@1785,registry:Registration@1810,registry:Registration@1962,registry:Registration@2117,registry:Registration@4046,whole:ClaimOwnership@1864,whole:ClaimOwnership@1918,whole:ClaimOwnership@1936,whole:ClaimOwnership@4015,whole:ClaimOwnership@4211,whole:ClaimOwnership@4320,whole:Route@2456,whole:Timer@1067,whole:Timer@1073,whole:Timer@1101,whole:Timer@1663,whole:Timer@2304,whole:Timer@3896,whole:Timer@3911,whole:Timer@3980,whole:Timer@3998,whole:Timer@4179,whole:Timer@4190,whole:Timer@4304,whole:Timer@4383,whole:Timer@4394 35914f3267326de830fb7c149bf330638cb49794a3e8c6f41445078a92669a04 |
| internal/host/rootmigration_timers.go rootMigrationTimers.load authority G,J4 rev3=PROPOSED-p3;identity=offline migration root uid 0;basis=reads only the root-private saved adoption intention, requiring the same host, incarnation, stamp, session and enrolled box; preserves exact registration IDs on retry file:Read@145,registry:Registration@844,whole:Timer@668,whole:Timer@846 f58c918185b3c382e5c1545f441cb589157b0c6cf60bb54322ca7edf40f1c1c9 |
| internal/host/rootmigration_timers.go rootMigrationTimers.key authority G,J4 rev3=PROPOSED-p3;identity=offline migration root uid 0;basis=scopes the durable timer adoption intention to the root-observed installation and denied session 76a349236e49d55a5b9866b86ea60aa56a7a5b179b7b77b82d87535f0a305f0c |
| @@ -201,11 +201,11 @@ internal/hostguard/rootmigration.go RootAuthority.CompleteMigrationInventory aut |
| @coverage internal/activator 0a01bc096c64f1886f810f4856d60f9a2642074a8b2ac0c56cbed77d122e76c8 |
| @coverage internal/console 6f4998c39e6d66cd73ea98500000cbecac251dd21dafd58f51a85c2291e0c533 |
| @coverage internal/edgesfile 7b48368150a427e4620f1c9ded49942b688c9e6c9cd983b7da43d3f2c0810b72 |
| -@coverage internal/host cec7e5d95134a61e8485d00a71e9ee1452d2f17938c46905092576f6a307dab5 |
| +@coverage internal/host d5a2aeca145f5881f0543a7bdb8998d3338ebb16e703ba330d58d98ec7da18bf |
| @coverage internal/host/units bf6ae516012701e0ca41908167e4efd60dd3c2c24e9af1555dec077e8be44461 |
| @coverage internal/hostbox f877b123ea1c5c361a3046f5962ea5ef8e1a3b8a534a29aa2c15aac507b37081 |
| @coverage internal/hostfloor 696e5ea4fa37b924b68e8e262235b99298ee19342d875213ba8209e5287dd3c6 |
| -@coverage internal/hostguard c9458bdbbd122ae58dbe6581e6823c9379c397661dbc7a35513ccd656aca344d |
| +@coverage internal/hostguard d115a22e2a8410adef697af3e73f420b663337dc827fcca82e181e15fdeb8834 |
| @coverage internal/release 4bf58e7d369ab2ceca1a6605ff71eabf453af7c1ae4b30565c72d6833bcea369 |
| @coverage internal/rootparticipation ffa18f535e706793976bb235426fa5852e65baba211be5077666c1dc738e7f0b |
| @coverage internal/rootstate 2cedbf9f0230a1e6e2ed41afe421a33796a58cdf50a37f0f709dabea24c9088b |
| @@ -305,18 +305,18 @@ internal/rootstate/state.go FileIdentity.Same authority E2 PROPOSED E51: Persist |
| internal/host/claimwakeassembly.go type ClaimLaunchInputs authority C,J4 rev3=PROPOSED-p3;run=12;basis=agent launch selection; preserves E named-unit inputs and seed own-write CAS callback together f64420b0bc9f2479961d965b297b86274ef69b31894b0dbe056e19850eb5c3b8 |
| internal/host/movesfiles.go movesFiles.Establish authority C,J4 rev3=PROPOSED-p3;run=12;basis=agent files preparation; retains seed admitted-writer and ordinary origin proof with one C8 commit; H3 is inventory bookkeeping, C owns the enabled effect 172fe8888da88c249b7990bd9fb4bd2821bd402fb96ab52af13c58500f31d50f |
| internal/hostbox/servinghandover.go ServingHandover.Run authority C,J4 rev3=PROPOSED-p3;run=12;identity=root handover controller uid 0;basis=preserves seed fresh boot-aware directory verification and persists its checked full filesystem identity in successor inventory only; immutable predecessor retained registry:Account@2154,registry:Addr@2373,registry:Addrs@2391,registry:App@2198,registry:Backing@2410,registry:Canary@2467,registry:ClaimOwner@5643,registry:ClaimOwnership@2841,registry:ClaimOwnership@3057,registry:ClaimOwnership@3090,registry:ClaimRowID@3120,registry:ClaimRowID@5714,registry:ClaimRowID@5888,registry:DBPath@2286,registry:Kind@2128,registry:Name@3174,registry:Name@5853,registry:Platform@2451,registry:Replica@2481,registry:Route@2841,registry:Route@3090,registry:Route@5643,registry:Route@5714,registry:Socket@2234,registry:Timers@3039,registry:Timers@3057,registry:Timers@3120,registry:Timers@3174,registry:Timers@5796,registry:Tunnel@2431,registry:Unit@2355,whole:ClaimOwnership@2853,whole:ClaimOwnership@3073,whole:ClaimOwnership@3102,whole:ColdRows@3514,whole:ColdRows@3522,whole:ColdRows@3530,whole:ColdRows@3537,whole:ColdRows@5645,whole:ColdRows@5716,whole:ColdRows@5798,whole:ColdRows@6379,whole:ColdRows@6389,whole:Route@2114,whole:Route@2643,whole:Route@2651,whole:Route@2701,whole:Route@2709,whole:Route@2847,whole:Route@3096,whole:Route@5651,whole:Route@5722,whole:Timer@2656,whole:Timer@2664,whole:Timer@2714,whole:Timer@2752,whole:Timer@3045,whole:Timer@3063,whole:Timer@3126,whole:Timer@3180,whole:Timer@5804 c1367b936e3e0caa9e2bbd0da117a9b89ce3b7b7d4f518069e7d265a48b89a04 |
| -internal/host/claimactivation_preflight.go claimActivationVerified authority G rev3=PROPOSED-p3;run=13;identity=agent uid carlos; root migration/adopt/control uid 0;basis=reads strict canonical deployment records in this placement scope; root additionally refuses retained unit configuration, bindings or materializations while 4a is incomplete cec48f637f31d76872d4d27c5e4391d8423c44de00a1bbd4aac63dcb70baa063 |
| +internal/host/claimactivation_preflight.go claimActivationVerified authority G rev3=PROPOSED-p3;run=15;identity=agent uid carlos; root migration/adopt/control uid 0;basis=canonical records, installed local units and retained root bindings independently refuse incomplete named-unit activation before claim state 8420c4b5b416bfb653788d69fbdf0f2c3acaad739c85be8bb2c1462f2ad56474 |
| internal/host/claimactivation_preflight.go var openClaimActivationState authority G rev3=PROPOSED-p3;run=13;identity=root migration/adopt/control uid 0;basis=opens retained root unit namespaces read-only without creating claim state file:Open@31 9b4c4aeddcc3756f3c7fbb267096147f5670511fc4fda809d88d98540ad685f6 |
| internal/host/claimstop_run.go RunInstanceControl authority C,D,G rev3=PROPOSED-p3;run=13;identity=instance-control uid 0;basis=requires retained recovery census before construction and complete launch verification after pending recovery before serving d55919f2c48d02f39bb2d0eda76c7854aaf2c7684b8c782483d8fb62e5f5bff4 |
| internal/host/claimstop_run.go claimRecoveryReady authority C,G rev3=PROPOSED-p3;run=13;identity=instance-control uid 0;basis=requires valid retained recovery census but defers complete launch inventory until restricted recovery has settled 627ab800a8f5772b81c3a893cf546438d7f76ae30ed29949ae6af52b5c2a9a7b |
| internal/host/claimunit_binding.go claimUnitBindings.establish authority E2 rev3=PROPOSED-p3;run=13;identity=root unit controller uid 0;basis=production admission refuses binding while named-unit 4a is incomplete, including backing=unit ef56f2db27a3d4a95803407a4b389b402592ed5b450bb6723aeffac42a041cab |
| internal/host/claimunit_dispatch.go claimUnitDispatch.forStart authority E2 rev3=PROPOSED-p3;run=13;identity=root launch controller uid 0;basis=verifies effective ordinary templates as well as retained named bindings before start and at the reservation permit recheck 3d358cb14cd248cea66750f64a4ea49be91eddb563e6ca389c12c7664f6c6706 |
| internal/host/claimunit_dropin_linux.go openClaimUnitDropin authority E2 rev3=PROPOSED-p3;run=13;identity=root launch controller uid 0;basis=opens only the exact reservation drop-in under checked root ancestry for valid named or ordinary run units file:Fstat@694,file:Fstat@984,file:Open@290,file:Openat@496 f9a9915d67e7f8e4a540a484a7c978bf2d22eb803c04564349f9db5df119de6b |
| -internal/host/claimunit_dropin_other.go openClaimUnitDropin authority E2 rev3=PROPOSED-p3;run=13;identity=root launch controller uid 0;basis=opens only the exact reservation drop-in under checked root ancestry for valid named or ordinary run units bb3a29d1ad8840139f7236971ecb2bae93e6323007f2fdd625114bb3ff105397 |
| +internal/host/claimunit_dropin_other.go openClaimUnitDropin authority E2 rev3=PROPOSED-p3;run=15;identity=non-Linux root launch controller;basis=unsupported-platform stub refuses only; performs no locking, opening or publication bb3a29d1ad8840139f7236971ecb2bae93e6323007f2fdd625114bb3ff105397 |
| internal/host/claimunit_effective.go claimEffectiveUnit authority E2 rev3=PROPOSED-p3;run=13;identity=root launch controller uid 0;basis=ordinary launches require exact embedded template bytes, only the reservation drop-in and checked effective systemd identity, capabilities and containment; bound launches retain definition checks file:ReadAll@3614,file:ReadAll@643,file:ReadFile@457 7f02ae5a7047e09fffb5ad8dd8fd5e4336236f5f5128b77d41bef043171b66eb |
| internal/host/claimunit_effective.go claimReservedDropin authority E2 rev3=PROPOSED-p3;run=13;identity=root launch controller uid 0;basis=derives exact host reservation mounts and trampoline arguments; ordinary units retain systemd instance expansion data-literal@382 bf9e5d43b589d965ee9d9af968610bab7342e274eba33dcea3a1f76c3bda50d3 |
| internal/host/claimunit_files_linux.go projectClaimBoundSocket authority E2 rev3=PROPOSED-p3;run=13;identity=root unit materializer uid 0;basis=selects and publishes the root-bound socket symlink and synchronizes its parent; this is publication authority file:Readlinkat@433 4f3c8daab1f68102e7be9511d1ec29d89c584c99aed79641f2b740031e503ab4 |
| -internal/host/claimunit_files_other.go projectClaimBoundSocket authority E2 rev3=PROPOSED-p3;run=13;identity=root unit materializer uid 0;basis=selects and publishes the root-bound socket symlink and synchronizes its parent; this is publication authority 3f72016917b749fed665db738fbb54d90b40f59eec09aa3751b6abbb2f14319a |
| +internal/host/claimunit_files_other.go projectClaimBoundSocket authority E2 rev3=PROPOSED-p3;run=15;identity=non-Linux root unit materializer;basis=unsupported-platform stub refuses only; performs no locking, opening or publication 3f72016917b749fed665db738fbb54d90b40f59eec09aa3751b6abbb2f14319a |
| internal/host/claimunit_materialize.go claimUnitMaterializer.install authority E2 rev3=PROPOSED-p3;run=13;identity=root unit controller uid 0;basis=production admission refuses materialization while named-unit 4a is incomplete, including backing=unit ed99fda682170367aee0a3d6270153b7051ae03849aa93dc24ecca60eb7afec4 |
| internal/host/claimunit_production.go bindProductionClaimUnits authority E2 rev3=PROPOSED-p3;run=13;identity=root control startup uid 0;basis=installs effective verification for ordinary and bound starts; incomplete 4a refuses production binding and materialization for named and backing units file:Read@1275 e4b9166fe33bcbafe91efb09d99d139d1f697df35dd7cff6765c9e483972d4c5 |
| internal/host/proofroot.go configureRootProof authority E1,B3 rev3=PROPOSED-p3;run=13;identity=root adopt uid 0;basis=checks participation before opening history or installing root proof policy; errors deny minting without falling back 322c7da5d034cd1cc870e748c499864796fa8bc11e4bbed6f96f7312d004dbd1 |
| @@ -326,5 +326,10 @@ internal/host/rootinventory_startup.go recoverRootInventoryStartup authority C,G |
| internal/host/rootmigration_production_linux.go quiesceLegacyRootMutators authority G rev3=PROPOSED-p3;run=13;identity=offline migration uid 0;basis=under mandatory release exclusion scans root process command lines and refuses already-running legacy adopt/apply/guard-arm before census creation file:ReadDir@77,file:ReadFile@537,file:Stat@381 5b8d7964c595634d80699448aa5f052fc2e8c215e3c9bd8549378d37ce25d2ca |
| internal/host/rootmigration_startup.go readRootMigrationState authority G rev3=PROPOSED-p3;run=13;identity=root startup/recovery uid 0;basis=validates retained census box/enrollment/generation and complete migration record before restricted recovery; grants no launch file:Read@425 9a8ddf2b605833b7fa4c0908ade98a6fc2efbe87c26e92ede289bbcc1d05beec |
| internal/host/rootmovesfloor_boot.go sameRootFloorImage authority F rev3=PROPOSED-p3;run=13;identity=root boot/recovery uid 0;basis=compares freshly verified signed digest, release and protocols, not saved boot-local file numbers 532078238827331f9282d85d1e17e146ed4842a215db057238aa5f6cb40ba137 |
| -internal/host/rootreleaselock.go withClaimReleaseCensus authority E2 rev3=PROPOSED-p3;run=13;identity=root adopt/guard uid 0;basis=census paths require release exclusion; legacy paths use an already-created migration exclusion without creating state 514b5e479c5340b02ec78106cdc19b90a03ec4a3ac229eb82c654f09bc6a92e5 |
| -internal/host/rootreleaselock.go withExistingRootRelease authority G rev3=PROPOSED-p3;run=13;identity=root legacy adopt/update uid 0;basis=unchanged legacy effect when migration exclusion does not exist; otherwise serializes with explicit migration before any mutation file:Lstat@93,file:Open@225 be32aad37d2b0a17979c74ed097d00728da6fedefd30862b8ec5537745bf55ae |
| +internal/host/rootreleaselock.go withClaimReleaseCensus authority E2 rev3=PROPOSED-p3;run=15;identity=root adopt/guard uid 0;basis=no census invokes main effect directly without opening release state, creating a file or waiting on any migration lock; participating effects retain release exclusion 35bcc5e18e89f9755cf1f749bbaa4cbf493380b75500183687476abd71c946b6 |
| +internal/host/claimactivation_preflight.go claimActivationLocalUnits authority G rev3=PROPOSED-p3;run=15;identity=agent uid carlos; root migration/adopt/control uid 0;basis=refusal-only inventory of backing unit instances, local named services and drop-ins, legacy app environment configuration and both systemd backing state directories; registry omission cannot confer eligibility file:ReadDir@215,file:ReadFile@1206,file:ReadFile@1289,file:WalkDir@603 bfe1faa17fcbe6d2ea25252698e925e26616b6cdaef55759de0c291b1b30749b |
| +internal/host/claimassembly.go ClaimAssembly.Build authority C,G rev3=PROPOSED-p3;run=15;identity=agent uid carlos; root control/adopt/recovery uid 0;basis=supplies root registration client, guard, enrollment and typed inventory; local root obligations and exact intention bind inventory revision; service SQL contributes refusals only file:Prepare@3854,file:Prepare@3871,file:Prepare@4583,file:Read@4513,whole:Timer@2044 b77068fb6ba255c31031e3328b6a7ae976369cd5098c51602b0c235effbed253 |
| +internal/host/rootgeneration_inventory.go readProductionGenerationInventory authority E2 rev3=PROPOSED-p3;run=15;identity=root generation/update/recovery uid 0;basis=local=true reads retained census, fences, obligations and journals without deployment store; local=false opens deployment store for canonical records; SQL remains refusal-only 0a9180acd9e78efc5905fe37df25d877c7343fcec037786f699b03182acaedc3 |
| +internal/host/rootmigration_production_linux.go verifyOfflineRootMigration authority G rev3=PROPOSED-p3;run=15;identity=offline root migration uid 0;basis=requires offline masked inactive adopt, stage, config-sync, restart, update and recovery services and absence of concurrent legacy root mutators before creating census state 300a81411020b33318dd8e15418acc95654c3980eb03c8581276d40a951a1a15 |
| +internal/host/systemparticipation.go systemApplier.checkUnarmedParticipationRecovery authority G,F rev3=PROPOSED-p3;run=15;identity=root system update/recovery uid 0;basis=pending acquisition triggers production controller settlement even without an update journal; normal ledger admission stays refused until verified recovery clears the marker 4360d1c0c50c554afc06b3a9ad216acc646728f97750e00ca1fe038b1bccce51 |
| +internal/hostguard/generationinventory.go RootAuthority.generationInstallation authority E2 rev3=PROPOSED-p3;run=15;identity=root generation verification uid 0;basis=enumerates using preserved installation stamp including exact retired tuples; resyncs retained authority without admitting inactive installations or dropping payload obligations fc5ef57b638ee0eba8ae49b724ef9397b2a3ae097d1e892cab888880480eacd2 |