WIP claim Plan 14 track E checkpoint 34 (not for merge)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 files changed,
+340
−8
internal/host/plan14_generation_creation_test.go+177 −0internal/host/plan14_generation_requirements_linux_test.go+1 −1internal/host/plan14_generation_requirements_root_linux_test.go+128 −1internal/host/rootgeneration_inventory.go+1 −1internal/host/rootgeneration_requirements.go+28 −1internal/provision/plan14_e_write_census_test.go+1 −1internal/provision/testdata/plan14-root-reads-e.tsv+4 −3
CI passed — run details
CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok amadan.net/carlos/platform.git/launchctx 0.013s
? amadan.net/carlos/platform.git/launchctx/controlname [no test files]
? amadan.net/carlos/platform.git/launchctx/wireid [no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok github.com/carlosframework/platform 357.992s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 4.041s
ok github.com/carlosframework/platform/cmd/moveprobe 92.914s
ok github.com/carlosframework/platform/cmd/zddprobe 0.732s
ok github.com/carlosframework/platform/internal/activator 234.863s
ok github.com/carlosframework/platform/internal/agentsock 0.058s
ok github.com/carlosframework/platform/internal/alerting 0.005s
ok github.com/carlosframework/platform/internal/analytics 0.022s
ok github.com/carlosframework/platform/internal/appconfig 2.573s
ok github.com/carlosframework/platform/internal/apperr 0.137s
ok github.com/carlosframework/platform/internal/applog 0.259s
ok github.com/carlosframework/platform/internal/awsinv 0.012s
ok github.com/carlosframework/platform/internal/boxchoice 0.005s
ok github.com/carlosframework/platform/internal/boxid 1.147s
? 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.033s
ok github.com/carlosframework/platform/internal/caps 0.034s
ok github.com/carlosframework/platform/internal/certwarm 0.004s
ok github.com/carlosframework/platform/internal/cgroup 0.759s
ok github.com/carlosframework/platform/internal/changelog 0.454s
ok github.com/carlosframework/platform/internal/clamd 0.070s
? github.com/carlosframework/platform/internal/cliflag [no test files]
ok github.com/carlosframework/platform/internal/console 369.901s
? 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 6.184s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.016s
ok github.com/carlosframework/platform/internal/dbverify 0.007s
ok github.com/carlosframework/platform/internal/dnsrec 0.004s
ok github.com/carlosframework/platform/internal/docsite 0.009s
ok github.com/carlosframework/platform/internal/domains 2.417s
ok github.com/carlosframework/platform/internal/doorkey 0.019s
ok github.com/carlosframework/platform/internal/economics 0.009s
ok github.com/carlosframework/platform/internal/edge 27.575s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.026s
ok github.com/carlosframework/platform/internal/fleetapi 0.197s
ok github.com/carlosframework/platform/internal/gate 1.741s
ok github.com/carlosframework/platform/internal/genselect 0.034s
ok github.com/carlosframework/platform/internal/geo 0.057s
ok github.com/carlosframework/platform/internal/geoip 0.065s
ok github.com/carlosframework/platform/internal/historyreadback 0.294s
ok github.com/carlosframework/platform/internal/host 137.034s
ok github.com/carlosframework/platform/internal/hostactors 0.581s
ok github.com/carlosframework/platform/internal/hostbox 3.030s
ok github.com/carlosframework/platform/internal/hostclaim 1.148s
ok github.com/carlosframework/platform/internal/hostevidence 0.010s
ok github.com/carlosframework/platform/internal/hostexec 7.197s
ok github.com/carlosframework/platform/internal/hostfence 0.058s
ok github.com/carlosframework/platform/internal/hostfiles 10.913s
ok github.com/carlosframework/platform/internal/hostfloor 0.020s
ok github.com/carlosframework/platform/internal/hostguard 1.097s
ok github.com/carlosframework/platform/internal/hostops 2.194s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.002s
ok github.com/carlosframework/platform/internal/hostquarantine 0.016s
ok github.com/carlosframework/platform/internal/hostrelocate 0.876s
ok github.com/carlosframework/platform/internal/hostresolve 0.242s
ok github.com/carlosframework/platform/internal/hostwrite 0.008s
ok github.com/carlosframework/platform/internal/infraview 0.008s
ok github.com/carlosframework/platform/internal/instancectl 1.859s
ok github.com/carlosframework/platform/internal/instanceexec 2.821s
ok github.com/carlosframework/platform/internal/instancefs 0.015s
ok github.com/carlosframework/platform/internal/instancekey 0.008s
ok github.com/carlosframework/platform/internal/instanceupdates 0.032s
ok github.com/carlosframework/platform/internal/layout 2.006s
ok github.com/carlosframework/platform/internal/lease 9.585s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.011s
ok github.com/carlosframework/platform/internal/ledger 0.021s
ok github.com/carlosframework/platform/internal/mail 0.369s
ok github.com/carlosframework/platform/internal/maintenance 0.096s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.760s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.116s
ok github.com/carlosframework/platform/internal/metrics 0.006s
ok github.com/carlosframework/platform/internal/moves 22.371s
ok github.com/carlosframework/platform/internal/moves/archive 1.260s
ok github.com/carlosframework/platform/internal/objstore 0.330s
ok github.com/carlosframework/platform/internal/ops 0.241s
ok github.com/carlosframework/platform/internal/park 0.362s
ok github.com/carlosframework/platform/internal/privatefile 0.004s
ok github.com/carlosframework/platform/internal/provision 14.276s
ok github.com/carlosframework/platform/internal/publicrelease 0.014s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 5.215s
ok github.com/carlosframework/platform/internal/relay 7.242s
ok github.com/carlosframework/platform/internal/release 23.548s
ok github.com/carlosframework/platform/internal/rollout 4.241s
ok github.com/carlosframework/platform/internal/rolloutwords 0.004s
ok github.com/carlosframework/platform/internal/rootaws 0.008s
ok github.com/carlosframework/platform/internal/rootparticipation 0.009s
ok github.com/carlosframework/platform/internal/rootstate 0.034s
ok github.com/carlosframework/platform/internal/s3compat 0.006s
ok github.com/carlosframework/platform/internal/safefile 0.005s
ok github.com/carlosframework/platform/internal/schedule 2.929s
ok github.com/carlosframework/platform/internal/sim/hostlife 15.260s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.945s
ok github.com/carlosframework/platform/internal/status 0.046s
ok github.com/carlosframework/platform/internal/steering 0.011s
ok github.com/carlosframework/platform/internal/stopevidence 0.522s
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.007s
ok github.com/carlosframework/platform/internal/unixdial 0.009s
ok github.com/carlosframework/platform/internal/updatewords 0.004s
ok github.com/carlosframework/platform/internal/usage 0.007s
ok github.com/carlosframework/platform/internal/version 0.003s
ok github.com/carlosframework/platform/internal/vet 1.872s
=== 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
--- 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.023s
=== RUN TestT4StorageProbeMinio
2026/10/14 19:39:25 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DE7CA88C911206, 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.022s
minio contract: PASS
diff --git a/internal/host/plan14_generation_creation_test.go b/internal/host/plan14_generation_creation_test.go| new file mode 100644 |
| index 000000000..396d2ecab |
| --- /dev/null |
| +++ b/internal/host/plan14_generation_creation_test.go |
| @@ -0,0 +1,177 @@ |
| +package host |
| + |
| +import ( |
| + "context" |
| + "os" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/hostclaim" |
| + "github.com/carlosframework/platform/internal/hostguard" |
| + "github.com/carlosframework/platform/internal/release" |
| + "github.com/carlosframework/platform/internal/rootparticipation" |
| + "github.com/carlosframework/platform/internal/rootstate" |
| +) |
| + |
| +func TestPlan14GenerationEmptyInstallationRequiresCreation(t *testing.T) { |
| + for _, attack := range []string{"none", "missing", "erased", "corrupt", "consumed", "wrong-installation", "wrong-host", "visible-sync", "changed-during-kernel"} { |
| + t.Run(attack, func(t *testing.T) { |
| + f, disk, rec := generationLaunchFixture(t) |
| + rec.Schema = 1 |
| + plan14PutRecord(t, f.root.generations.store, rec) |
| + views, err := f.root.Guard.Root.GenerationInstallations(t.Context()) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + views[0].Installation.Empty = true |
| + in := views[0].Installation |
| + snapshot := generationInventory{Census: rootparticipation.Census{Schema: 1, Box: in.Box, Enrollment: in.Enrollment, Migration: in.Migration, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: views, Payloads: generationPayloadFixture(views)} |
| + historyDisk := &generationDisk{rollbackDisk: &rollbackDisk{dir: t.TempDir()}} |
| + history := &release.RootHistory{Store: historyDisk} |
| + binding := release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App} |
| + create := func() { |
| + t.Helper() |
| + if err := history.RecordCreation(binding, rootHistoryInstallation(in)); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + create() |
| + active := false |
| + fresh := func(in hostguard.RootInstallation) error { |
| + ok, err := history.FreshInstallation(release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App}, rootHistoryInstallation(in)) |
| + if err != nil { |
| + return err |
| + } |
| + if !ok { |
| + return os.ErrPermission |
| + } |
| + return nil |
| + } |
| + v := generationReconciler{read: func(ctx context.Context) (generationInventory, error) { |
| + return generationRequiredInventory(ctx, f.root.generations.store, snapshot, fresh) |
| + }, kernel: func(context.Context) (string, string, error) { |
| + if active && attack == "changed-during-kernel" { |
| + if err := history.ConsumeCreation(binding); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + return "boot", generationInventoryDigest("kernel"), nil |
| + }} |
| + source := generationPinSource{open: func() (rootstate.Store, error) { return disk, nil }, reconcile: v.reconcile} |
| + ledger := &release.RootGenerations{Store: disk} |
| + accept := func() { |
| + t.Helper() |
| + pins, err := source.StagedPins(t.Context(), f.root.generations.optDir) |
| + if err != nil || len(pins) != 0 { |
| + t.Fatalf("new installation: %v %v", pins, err) |
| + } |
| + if c, err := ledger.Completeness(); err != nil || !c.Complete { |
| + t.Fatalf("creation not certified: %+v %v", c, err) |
| + } |
| + } |
| + accept() |
| + if attack == "none" { |
| + return |
| + } |
| + before, _ := ledger.Completeness() |
| + names, err := historyDisk.Names() |
| + if err != nil || len(names) != 1 { |
| + t.Fatalf("history names: %v %v", names, err) |
| + } |
| + raw, err := historyDisk.Read(names[0]) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + switch attack { |
| + case "visible-sync": |
| + historyDisk.fail = 2 |
| + case "missing": |
| + fresh = nil |
| + case "erased": |
| + if err := historyDisk.Remove(names[0]); err != nil { |
| + t.Fatal(err) |
| + } |
| + case "corrupt": |
| + if err := historyDisk.Write(names[0], []byte("{")); err != nil { |
| + t.Fatal(err) |
| + } |
| + case "consumed": |
| + if err := history.ConsumeCreation(binding); err != nil { |
| + t.Fatal(err) |
| + } |
| + case "wrong-installation", "wrong-host": |
| + if err := historyDisk.Remove(names[0]); err != nil { |
| + t.Fatal(err) |
| + } |
| + other, identity := binding, rootHistoryInstallation(in) |
| + if attack == "wrong-host" { |
| + other.Host = "other.example" |
| + } else { |
| + identity = "other-installation" |
| + } |
| + if err := history.RecordCreation(other, identity); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + active = true |
| + if pins, err := source.StagedPins(t.Context(), f.root.generations.optDir); err == nil || pins != nil { |
| + t.Fatalf("unknown creation certified: %v %v", pins, err) |
| + } |
| + if c, err := ledger.Completeness(); err != nil || c.Complete || c.Revision <= before.Revision { |
| + t.Fatalf("creation refusal retained certificate: %+v %v", c, err) |
| + } |
| + if attack == "visible-sync" { |
| + if pins, err := source.StagedPins(t.Context(), f.root.generations.optDir); err == nil || pins != nil { |
| + t.Fatalf("visible unsynced creation accepted on retry: %v %v", pins, err) |
| + } |
| + } |
| + if err := historyDisk.Write(names[0], raw); err != nil { |
| + t.Fatal(err) |
| + } |
| + active = false |
| + if attack != "missing" { |
| + accept() |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestPlan14GenerationCreationFromRegistration(t *testing.T) { |
| + f, root, q := plan14InstallRegistration(t, hostclaim.KindServing, "") |
| + history := &release.RootHistory{Store: &rollbackDisk{dir: t.TempDir()}} |
| + root.history = func(ctx context.Context, fn func(*release.RootHistory) error) error { return fn(history) } |
| + if _, err := root.commitRootInventory(t.Context(), q); err != nil { |
| + t.Fatal(err) |
| + } |
| + views, err := root.Stop.Coordinator.Guard.Root.GenerationInstallations(t.Context()) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + payloads, err := f.journal.GenerationPayloads(t.Context()) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if len(views) != 1 || !views[0].Installation.Empty || len(payloads) != 1 || len(payloads[0].Inventory.Entries) != 0 { |
| + t.Fatal("first registration did not persist an empty installation") |
| + } |
| + in := views[0].Installation |
| + v := generationReconciler{read: func(ctx context.Context) (generationInventory, error) { |
| + return generationRequiredInventory(ctx, f.claims, generationInventory{Census: rootparticipation.Census{Schema: 1, Box: in.Box, Enrollment: in.Enrollment, Migration: in.Migration, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: views, Payloads: payloads}, func(in hostguard.RootInstallation) error { |
| + fresh, err := history.FreshInstallation(release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App}, rootHistoryInstallation(in)) |
| + if err != nil { |
| + return err |
| + } |
| + if !fresh { |
| + return os.ErrPermission |
| + } |
| + return nil |
| + }) |
| + }, kernel: func(context.Context) (string, string, error) { return "boot", generationInventoryDigest("kernel"), nil }} |
| + disk := &rollbackDisk{dir: t.TempDir()} |
| + source := generationPinSource{open: func() (rootstate.Store, error) { return disk, nil }, reconcile: v.reconcile} |
| + if pins, err := source.StagedPins(t.Context(), root.Roots["opt"]); err != nil || len(pins) != 0 { |
| + t.Fatalf("registered creation refused: %v %v", pins, err) |
| + } |
| + if c, err := (&release.RootGenerations{Store: disk}).Completeness(); err != nil || !c.Complete { |
| + t.Fatalf("registered creation incomplete: %+v %v", c, err) |
| + } |
| +} |
diff --git a/internal/host/plan14_generation_requirements_linux_test.go b/internal/host/plan14_generation_requirements_linux_test.go| index 12ff56ca5..c5843b702 100644 |
| --- a/internal/host/plan14_generation_requirements_linux_test.go |
| +++ b/internal/host/plan14_generation_requirements_linux_test.go |
| @@ -100,7 +100,7 @@ func TestPlan14GenerationRequiredArtifacts(t *testing.T) { |
| } |
| attackActive := false |
| v := generationReconciler{read: func(ctx context.Context) (generationInventory, error) { |
| - return generationRequiredInventory(ctx, store, snapshot) |
| + return generationRequiredInventory(ctx, store, snapshot, nil) |
| }, kernel: func(context.Context) (string, string, error) { |
| if attackActive && attack == "changed-record-during-kernel" { |
| changeRecord() |
diff --git a/internal/host/plan14_generation_requirements_root_linux_test.go b/internal/host/plan14_generation_requirements_root_linux_test.go| index cac272570..0fc659b31 100644 |
| --- a/internal/host/plan14_generation_requirements_root_linux_test.go |
| +++ b/internal/host/plan14_generation_requirements_root_linux_test.go |
| @@ -120,6 +120,7 @@ func TestPlan14GenerationRequiredArtifactsRootRunner(t *testing.T) { |
| t.Fatalf("private journal directory: %v %v", info, err) |
| } |
| generationRunnerEmptyRegistry(t, dir, uid, gid) |
| + generationRunnerCreation(t, dir) |
| rec := provision.Record{Schema: 1, Host: "retained.example", Account: "bab", App: "shop", Channel: "stable", CreatedAt: time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC)} |
| store, err := openDeployStore(t.Context()) |
| if err != nil { |
| @@ -226,7 +227,7 @@ func TestPlan14GenerationRequiredArtifactsRootRunner(t *testing.T) { |
| snapshot, readErr := generationInventoryAt(t.Context(), rootparticipation.Namespace, "/var/lib/carlos-claims/authority/fences", "/var/lib/carlos-claims/journals.db") |
| t.Logf("root inventory: %v", readErr) |
| if readErr == nil { |
| - snapshot, readErr = generationRequiredInventory(t.Context(), store, snapshot) |
| + snapshot, readErr = generationRequiredInventory(t.Context(), store, snapshot, productionGenerationCreation) |
| t.Logf("required artifacts: %v", readErr) |
| } |
| if readErr == nil { |
| @@ -417,3 +418,129 @@ func generationRunnerEmptyRegistry(t *testing.T, opt string, uid, gid int) { |
| }) |
| } |
| } |
| + |
| +func generationRunnerCreation(t *testing.T, opt string) { |
| + t.Helper() |
| + ctx := t.Context() |
| + store, err := openDeployStore(ctx) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + rec := provision.Record{Schema: 1, Host: "new.example", Account: "bab", App: "shop", Channel: "stable", CreatedAt: time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC)} |
| + plan14PutRecord(t, store, rec) |
| + inc, err := hostclaim.Incarnation(rec.Account, rec.App, rec.Host, rec.CreatedAt) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + in := hostguard.RootInstallation{Host: rec.Host, Account: rec.Account, App: rec.App, Record: provision.RecordKey(rec.Account, rec.App, rec.Host), Incarnation: inc, Stamp: 1, Box: "runner", Enrollment: "en", Migration: 1, InventoryRevision: 1, Empty: true} |
| + fences, err := rootstate.Open("/var/lib/carlos-claims/authority/fences", false, false) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer fences.Close() |
| + raw, err := json.Marshal(map[string]any{"Schema": 1, "Installation": in, "State": map[string]any{"v": 1, "host": in.Host, "incarnation": inc, "active_stamp": in.Stamp}}) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := fences.Write(in.Host+".json", raw); err != nil { |
| + t.Fatal(err) |
| + } |
| + journal, err := hostbox.OpenLaunchJournal("/var/lib/carlos-claims/journals.db") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + service, err := hostbox.NewJournalService(journal) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := service.Inventory.WriteStopInventory(ctx, hostbox.StopInventory{Complete: true, Host: in.Host, Incarnation: inc, InstallationStamp: in.Stamp, Box: in.Box, Enrollment: in.Enrollment, DirectoryRevision: "1"}); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := journal.Close(); err != nil { |
| + t.Fatal(err) |
| + } |
| + historyState, err := rootstate.Open(release.RootHistoryDir, true, false) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer historyState.Close() |
| + history := &release.RootHistory{Store: historyState} |
| + binding := release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App} |
| + check := func(refuse bool) { |
| + t.Helper() |
| + err := withClaimRootRelease(ctx, true, func() (rootstate.Store, error) { return rootstate.Open(rootReleasePrivate, false, false) }, func() error { |
| + pins, err := productionRootGenerationPins().StagedPins(ctx, opt) |
| + if len(pins) != 0 { |
| + t.Fatalf("creation fixture has pins: %v", pins) |
| + } |
| + return err |
| + }) |
| + if (err != nil) != refuse { |
| + t.Fatalf("creation census: refuse=%v error=%v", refuse, err) |
| + } |
| + state, err := rootstate.Open(release.RootGenerationsDir, false, false) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer state.Close() |
| + c, err := (&release.RootGenerations{Store: state}).Completeness() |
| + if err != nil || c.Complete == refuse { |
| + t.Fatalf("creation certificate: %+v %v", c, err) |
| + } |
| + } |
| + check(true) |
| + if err := history.RecordCreation(binding, rootHistoryInstallation(in)); err != nil { |
| + t.Fatal(err) |
| + } |
| + check(false) |
| + names, err := historyState.Names() |
| + if err != nil || len(names) != 1 { |
| + t.Fatalf("creation files: %v %v", names, err) |
| + } |
| + saved, err := historyState.Read(names[0]) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + for _, attack := range []string{"erased", "corrupt", "consumed", "wrong-installation"} { |
| + t.Run("creation-"+attack, func(t *testing.T) { |
| + switch attack { |
| + case "erased", "wrong-installation": |
| + if err := historyState.Remove(names[0]); err != nil { |
| + t.Fatal(err) |
| + } |
| + if attack == "wrong-installation" { |
| + if err := history.RecordCreation(binding, "other"); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + case "corrupt": |
| + if err := historyState.Write(names[0], []byte("{")); err != nil { |
| + t.Fatal(err) |
| + } |
| + case "consumed": |
| + if err := history.ConsumeCreation(binding); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + check(true) |
| + if err := historyState.Write(names[0], saved); err != nil { |
| + t.Fatal(err) |
| + } |
| + check(false) |
| + }) |
| + } |
| + // This fixture owns the isolated stores. Remove its complete fixture before |
| + // the caller installs its separate predecessor case. |
| + if err := fences.Remove(in.Host + ".json"); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := historyState.Remove(names[0]); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := os.Remove("/var/lib/carlos-claims/journals.db"); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := store.Delete(ctx, in.Record); err != nil { |
| + t.Fatal(err) |
| + } |
| +} |
diff --git a/internal/host/rootgeneration_inventory.go b/internal/host/rootgeneration_inventory.go| index 0aea2774c..1a58bf67c 100644 |
| --- a/internal/host/rootgeneration_inventory.go |
| +++ b/internal/host/rootgeneration_inventory.go |
| @@ -241,7 +241,7 @@ func productionGenerationInventory(ctx context.Context) (generationInventory, er |
| if err != nil { |
| return generationInventory{}, err |
| } |
| - snapshot, err = generationRequiredInventory(ctx, store, snapshot) |
| + snapshot, err = generationRequiredInventory(ctx, store, snapshot, productionGenerationCreation) |
| if err != nil { |
| return generationInventory{}, err |
| } |
diff --git a/internal/host/rootgeneration_requirements.go b/internal/host/rootgeneration_requirements.go| index 573f94317..e8527fb69 100644 |
| --- a/internal/host/rootgeneration_requirements.go |
| +++ b/internal/host/rootgeneration_requirements.go |
| @@ -9,6 +9,7 @@ import ( |
| "github.com/carlosframework/platform/internal/hostguard" |
| "github.com/carlosframework/platform/internal/provision" |
| "github.com/carlosframework/platform/internal/release" |
| + "github.com/carlosframework/platform/internal/rootstate" |
| ) |
| type generationRecord struct { |
| @@ -16,7 +17,7 @@ type generationRecord struct { |
| Record provision.Record |
| } |
| -func generationRequiredInventory(ctx context.Context, store release.Store, snapshot generationInventory) (generationInventory, error) { |
| +func generationRequiredInventory(ctx context.Context, store release.Store, snapshot generationInventory, fresh func(hostguard.RootInstallation) error) (generationInventory, error) { |
| var zero generationInventory |
| if store == nil { |
| return zero, os.ErrPermission |
| @@ -62,6 +63,16 @@ func generationRequiredInventory(ctx context.Context, store release.Store, snaps |
| } |
| } |
| if !required { |
| + // Retained predecessors are checked below as nonempty obligations. |
| + // Without one, an empty payload needs root's creation evidence. |
| + if len(view.Retired) == 0 { |
| + if fresh == nil { |
| + return zero, os.ErrPermission |
| + } |
| + if err := fresh(in); err != nil { |
| + return zero, err |
| + } |
| + } |
| continue |
| } |
| state, err := filepath.Rel(dataDir(), provision.InstanceDir(dataDir(), rec.Host)) |
| @@ -90,3 +101,19 @@ func generationRequiredInventory(ctx context.Context, store release.Store, snaps |
| } |
| return snapshot, ctx.Err() |
| } |
| + |
| +func productionGenerationCreation(in hostguard.RootInstallation) error { |
| + state, err := rootstate.Open(release.RootHistoryDir, false, false) |
| + if err != nil { |
| + return err |
| + } |
| + defer state.Close() |
| + fresh, err := (&release.RootHistory{Store: state}).FreshInstallation(release.HistoryBinding{Host: in.Host, Account: in.Account, App: in.App}, rootHistoryInstallation(in)) |
| + if err != nil { |
| + return err |
| + } |
| + if !fresh { |
| + return os.ErrPermission |
| + } |
| + return nil |
| +} |
diff --git a/internal/provision/plan14_e_write_census_test.go b/internal/provision/plan14_e_write_census_test.go| index e017df494..63a671941 100644 |
| --- a/internal/provision/plan14_e_write_census_test.go |
| +++ b/internal/provision/plan14_e_write_census_test.go |
| @@ -2,7 +2,7 @@ package provision |
| // Track E overlays the reviewed Part 1 write census without changing its baseline. |
| var plan14EWriteCensusOverlay = []censusEntry{ |
| - {"internal/host/rootgeneration_requirements.go", "generationRequiredInventory", "key-site", "key:RecordKey", "aaa70feaad4ed0331e7d45433bb0b6476601bbed68783294215cce16eaa5a3eb"}, |
| + {"internal/host/rootgeneration_requirements.go", "generationRequiredInventory", "key-site", "key:RecordKey", "d08773649c51f37ae31f03822741da485d306529177d5bf6450aea1ded9f778b"}, |
| {"internal/host/rootgeneration_inventory.go", "generationReconciler.reconcile", "key-site", "key:RecordKey", "d928288910227cc14f158595e44d052436a23c407e89682dd0930ee1011d7e57"}, |
| {"internal/host/rootgenerations_launch.go", "rootGenerationSource.resolve", "key-site", "key:RecordKey", "d69a3c0c86b03d5469708ac8bf084f4f516c0b61a18519d46cc3c0a0616f5381"}, |
| {"internal/historyreadback/workflow.go", "Operation.deliverGrant", "nonrecord", "sel:Put", "fb410ef6274ef7de0997a804c837ad1ecf29d8826ba1c4dcda00c2ae986fc434"}, |
diff --git a/internal/provision/testdata/plan14-root-reads-e.tsv b/internal/provision/testdata/plan14-root-reads-e.tsv| index 2409213e1..76bf2fa25 100644 |
| --- a/internal/provision/testdata/plan14-root-reads-e.tsv |
| +++ b/internal/provision/testdata/plan14-root-reads-e.tsv |
| @@ -54,7 +54,7 @@ internal/release/roothistory_replace.go replacementReconstruction authority E1 S |
| internal/release/signedhead.go StageSignedHead authority E1 Canonical route is supplied by the root unit verifier. Verifies signed channel, digest ratchet and manifest/artifact before staging; unavailable pin census never authorizes restaging differing bytes. Fixtures: TestPlan14Incident20261006WithLedger and TestPlan14SignedNamedRecoveryAuthority. registry:Account@202,registry:Account@302,registry:Account@403,registry:App@214,registry:App@415,registry:Channel@222,registry:Host@434 ca02f04e55556ba0233dfa0848b0c533a9d9aa06cad6abd33cfd2968b3bd838a |
| internal/rootstate/link_linux.go ReadTrustedLink authority A,E1 Checks the full root-owned parent chain before descriptor-based live-link reading. Fixture: TestPlan14TrustedLiveLink; privileged acceptance remains required. file:Open@156 90f8e8de4e2c71f07b5d749cbe4bb1c1dfa426576a82138823449f56fc9ec5ab |
| internal/rootstate/link_linux.go directory.readLink authority A,E1 Pins the symlink inode with O_PATH and checks owner/type/link count before readlinkat. Fixture: TestPlan14TrustedLiveLink. file:Fstat@356,file:Openat@183,file:Readlinkat@554 a8559fe63dce48830916004ecfe7fbbe5d419568c7daf7a3073cf1b5bb12c3d7 |
| -@coverage internal/host c438d1079a8cf374015b6a3e1a35db5f5425e3527984ab8f24c084330a1582f8 |
| +@coverage internal/host 1d5852649dddca1c23b871db7106675702d92e2dfc88a2c53856ececc93a42c0 |
| @coverage internal/release 2bb546a86c1ab49b02e424eb0b366b46776857a1249baa2d6fb61ead4209d6bd |
| @coverage internal/rootstate a773d6dbc0e4150e0054f7a94d0e1493c88d0d57f4f4530138b3427e2c217bc5 |
| internal/release/roothistory_status.go RootHistory.MigrationStatus authority E1 Exact completion binds the accepted authorization digest from durable private history to enrolled-root signed readback. Pending originals, changed evidence and canonical sources cannot supply retirement completion. Fixtures: TestPlan14HistoryReconstructionCompletion, TestPlan14HistoryRetirementCompletion, TestPlan14ReadReconstructionCompletion, TestPlan14ReadRetirementCompletion, TestPlan14RecoveryRetirementProductionCallback, TestPlan14RootHistoryReconstructionCrash, TestPlan14HistoryRetirementProcessCrash. file:Read@1062,file:Read@3891 f0ab7ad2d2d70f8c22ebfd575f5a1c6931f7eba30ccdba80ad43d0efffc88154 |
| @@ -303,7 +303,7 @@ internal/host/rootgeneration_inventory.go generationInventoryDigest authority E2 |
| internal/host/rootgeneration_inventory.go generationOwner authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel witnesses passed on the E27 root runner. E29 physical-payload and installed-unit witnesses passed on the controller runner; E30 required-artifact witness pending. 69f89153837761f13116937b330ffbf406891299d5580296ea4aade905aa02ce |
| internal/host/rootgeneration_inventory.go generationReconciler.reconcile authority E2 E30: derive route and ordinary-instance state-container requirements from schema- and shape-validated canonical deployment records bound to retained installation incarnation; include record contents and ETag in both source reads and the certificate digest. Missing, mismatched, omitted or changed evidence refuses without retiring pins. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationProductionModeGate. Production fixed-path witness TestPlan14GenerationRequiredArtifactsRootRunner is runner-pending. Timer classes, parked artifacts, positive empty-installation evidence and running-tenant containment remain incomplete. d928288910227cc14f158595e44d052436a23c407e89682dd0930ee1011d7e57 |
| internal/host/rootgeneration_inventory.go openGenerationLaunchInventory authority E2 E33: census-only generation journal reads accept private root-owned ancestry without chmod or repair. Every ancestor remains descriptor-checked, root-owned and non-writable by group/other; leaves remain single-link regular root-owned files, checked for DB/WAL/SHM. Public-file callers remain unchanged. Fixtures: TestPlan14AuthorityFilePrivateAncestors, TestPlan14AuthorityFileAbsoluteBoundary; TestPlan14GenerationPrivateJournalRootRunner and TestPlan14GenerationRequiredArtifactsRootRunner are runner-pending. Pre-flight 76, 78, 82 and 89. 2b54531e16282caeb0ddbc1dc92b18d6cb2db652589eb3757daefe10daa63a53 |
| -internal/host/rootgeneration_inventory.go productionGenerationInventory authority E2 E32: census-only E2 completeness discovers the bounded union of route and timer hosts, including inactive orphan timers, through the service-user read-only parser. Unknown hosts refuse even with an empty root installation list; discovery supplies no authority and never removes a retained obligation. Pre-flight 33, 80, 82, 83, 89 and 90. Fixtures: TestArtifactHosts, TestRemoteReadOnlyWALRegistry, TestPlan14GenerationRegistryRows, TestPlan14GenerationRegistryProductionReader; extended TestPlan14GenerationRequiredArtifactsRootRunner remains runner-pending. 2e0f4082ea9916f06b9dc50854a571274713cd53cc904da31ae5410eb1ee33f9 |
| +internal/host/rootgeneration_inventory.go productionGenerationInventory authority E2 E32: census-only E2 completeness discovers the bounded union of route and timer hosts, including inactive orphan timers, through the service-user read-only parser. Unknown hosts refuse even with an empty root installation list; discovery supplies no authority and never removes a retained obligation. Pre-flight 33, 80, 82, 83, 89 and 90. Fixtures: TestArtifactHosts, TestRemoteReadOnlyWALRegistry, TestPlan14GenerationRegistryRows, TestPlan14GenerationRegistryProductionReader; extended TestPlan14GenerationRequiredArtifactsRootRunner remains runner-pending. E34: census-only generation completeness requires E1 root creation evidence for an empty installation without retained predecessors. The exact host/account/app/installation must remain fresh on both sides of kernel inspection. Existing predecessor obligations remain mandatory. Production root history uses checked private ancestry and recommits before granting freshness, so read or sync failure refuses. Pre-flight 20, 33, 79 and 80. Fixtures: TestPlan14GenerationEmptyInstallationRequiresCreation, TestPlan14GenerationCreationFromRegistration, TestPlan14GenerationRequiredArtifacts; production checked-path TestPlan14GenerationRequiredArtifactsRootRunner remains runner-pending. No new history initialization or claims-off call. 43f12726911af472eeeec8253fac21ef4bfac2c19c66f9bfe6fc28036c557842 |
| internal/host/rootgeneration_inventory.go type generationInventory authority E2 E30: derive route and ordinary-instance state-container requirements from schema- and shape-validated canonical deployment records bound to retained installation incarnation; include record contents and ETag in both source reads and the certificate digest. Missing, mismatched, omitted or changed evidence refuses without retiring pins. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationProductionModeGate. Production fixed-path witness TestPlan14GenerationRequiredArtifactsRootRunner is runner-pending. Timer classes, parked artifacts, positive empty-installation evidence and running-tenant containment remain incomplete. 4de1dc2a2c9261b1e7bbd811d4d78a271d2fc5a37aeca196177f3e9527d0408e |
| internal/host/rootgeneration_inventory.go type generationReconciler authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel witnesses passed on the E27 root runner. E29 physical-payload and installed-unit witnesses passed on the controller runner; E30 required-artifact witness pending. a979cdff1cd6311cf57d4a93e4501fb8dc2f65772d0013e622175b2a1f610d91 |
| internal/hostguard/generationinventory.go RootAuthority.GenerationInstallations authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel witnesses passed on the E27 root runner. E29 physical-payload and installed-unit witnesses passed on the controller runner; E30 required-artifact witness pending. 593768e6ba937cf2cfa2635c8f796f767d6c2354737a72073cc351f514faed20 |
| @@ -315,7 +315,7 @@ internal/hostbox/generationpayload.go LaunchJournal.GenerationPayloads authority |
| internal/hostbox/generationpayload.go type GenerationPayload authority E2 E26: checked read-only payload enumeration includes inventory and revision orphans; completeness binds current and retained predecessor inventories, exact current revision and both source reads. Fixtures: TestPlan14GenerationPayloadCompleteness, TestPlan14GenerationPayloadRetainedPredecessor, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationPayloadInventory, TestPlan14GenerationPayloadReaderDoesNotInitialize. Checked root reader acceptance passed at E27; running-tenant containment remains unfinished. 992921ba14660af87a8099649ac589a7c2587467c05df052710fa45a9a5aa8bb |
| internal/host/rootgeneration_payload_linux.go generationPayloadFiles authority E2 E28: retained data payloads require exact device/inode/type through no-symlink descriptor opens before and after kernel inspection. Missing, replaced and intended payloads remain unresolved. Tests: TestPlan14GenerationPayloadPhysicalIdentity; privileged TestPlan14GenerationPayloadPhysicalRootRunner remains runner-pending. Route/timer and running-tenant containment are not accepted by this check. file:Fstat@1333,file:Openat2@1120,file:Openat2@917 7344256ef22fb8dea05e47f70c05094008739c14ae0f95336573c4f23d3e1837 |
| internal/host/rootgeneration_payload_other.go generationPayloadFiles authority E2 E28: retained data payloads require exact device/inode/type through no-symlink descriptor opens before and after kernel inspection. Missing, replaced and intended payloads remain unresolved. Tests: TestPlan14GenerationPayloadPhysicalIdentity; privileged TestPlan14GenerationPayloadPhysicalRootRunner remains runner-pending. Route/timer and running-tenant containment are not accepted by this check. candidate:Kind@183,candidate:Kind@204 182189d8409113d671cdc76fc5d2d73e7056057bca230d546e65300c428b2c18 |
| -internal/host/rootgeneration_requirements.go generationRequiredInventory authority E2 E30: derive route and ordinary-instance state-container requirements from schema- and shape-validated canonical deployment records bound to retained installation incarnation; include record contents and ETag in both source reads and the certificate digest. Missing, mismatched, omitted or changed evidence refuses without retiring pins. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationProductionModeGate. Production fixed-path witness TestPlan14GenerationRequiredArtifactsRootRunner is runner-pending. Timer classes, parked artifacts, positive empty-installation evidence and running-tenant containment remain incomplete. aaa70feaad4ed0331e7d45433bb0b6476601bbed68783294215cce16eaa5a3eb |
| +internal/host/rootgeneration_requirements.go generationRequiredInventory authority E2 E30: derive route and ordinary-instance state-container requirements from schema- and shape-validated canonical deployment records bound to retained installation incarnation; include record contents and ETag in both source reads and the certificate digest. Missing, mismatched, omitted or changed evidence refuses without retiring pins. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationProductionModeGate. Production fixed-path witness TestPlan14GenerationRequiredArtifactsRootRunner is runner-pending. Timer classes, parked artifacts, positive empty-installation evidence and running-tenant containment remain incomplete. E34: census-only generation completeness requires E1 root creation evidence for an empty installation without retained predecessors. The exact host/account/app/installation must remain fresh on both sides of kernel inspection. Existing predecessor obligations remain mandatory. Production root history uses checked private ancestry and recommits before granting freshness, so read or sync failure refuses. Pre-flight 20, 33, 79 and 80. Fixtures: TestPlan14GenerationEmptyInstallationRequiresCreation, TestPlan14GenerationCreationFromRegistration, TestPlan14GenerationRequiredArtifacts; production checked-path TestPlan14GenerationRequiredArtifactsRootRunner remains runner-pending. No new history initialization or claims-off call. d08773649c51f37ae31f03822741da485d306529177d5bf6450aea1ded9f778b |
| internal/host/rootgeneration_requirements.go type generationRecord authority E2 E30: derive route and ordinary-instance state-container requirements from schema- and shape-validated canonical deployment records bound to retained installation incarnation; include record contents and ETag in both source reads and the certificate digest. Missing, mismatched, omitted or changed evidence refuses without retiring pins. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationProductionModeGate. Production fixed-path witness TestPlan14GenerationRequiredArtifactsRootRunner is runner-pending. Timer classes, parked artifacts, positive empty-installation evidence and running-tenant containment remain incomplete. ff50bc959af62afbd73b01e9640346f9c79d6b6578e03cb3a7d6bdff4d90fc28 |
| internal/host/rootgeneration_rows.go generationRegistryInventory authority E2 E32: census-only E2 completeness discovers the bounded union of route and timer hosts, including inactive orphan timers, through the service-user read-only parser. Unknown hosts refuse even with an empty root installation list; discovery supplies no authority and never removes a retained obligation. Pre-flight 33, 80, 82, 83, 89 and 90. Fixtures: TestArtifactHosts, TestRemoteReadOnlyWALRegistry, TestPlan14GenerationRegistryRows, TestPlan14GenerationRegistryProductionReader; extended TestPlan14GenerationRequiredArtifactsRootRunner remains runner-pending. registry:ClaimOwnership@1976,registry:ClaimOwnership@2355,registry:ClaimRowID@2408,registry:Host@1954,registry:Host@2333,registry:Name@2274,registry:Registration@2386,whole:ClaimOwnership@1252,whole:ClaimOwnership@1316,whole:ClaimOwnership@1330,whole:ClaimOwnership@1344,whole:ClaimOwnership@1853,whole:ClaimOwnership@1982,whole:ClaimOwnership@2000,whole:ClaimOwnership@2055,whole:ClaimOwnership@2304,whole:ClaimOwnership@2361,whole:ClaimOwnership@2379,whole:ClaimOwnership@2474,whole:ClaimOwnership@2502,whole:Route@1871,whole:Timer@2160,whole:Timer@2238 ec488db0d8eae89879f0b0d628d20c74bcd35907599b32067a17db72707b69a3 |
| @coverage internal/registry b8bc7442621fce6ae121cf8557f44289803ec85971ee30581bd4c85ffad480bd |
| @@ -325,3 +325,4 @@ internal/registry/remote_dispatch.go Registry.dispatchRemote authority E2 Mixed |
| internal/rootstate/authorityfile_linux.go OpenAuthorityFile authority E2 E33: census-only generation journal reads accept private root-owned ancestry without chmod or repair. Every ancestor remains descriptor-checked, root-owned and non-writable by group/other; leaves remain single-link regular root-owned files, checked for DB/WAL/SHM. Public-file callers remain unchanged. Fixtures: TestPlan14AuthorityFilePrivateAncestors, TestPlan14AuthorityFileAbsoluteBoundary; TestPlan14GenerationPrivateJournalRootRunner and TestPlan14GenerationRequiredArtifactsRootRunner are runner-pending. Pre-flight 76, 78, 82 and 89. file:Open@181 39a50bbf5bc21a32c76647548c19af937055158ec583b1620a21645f9391500c |
| internal/rootstate/authorityfile_linux.go openAuthorityFile authority E2 E33: census-only generation journal reads accept private root-owned ancestry without chmod or repair. Every ancestor remains descriptor-checked, root-owned and non-writable by group/other; leaves remain single-link regular root-owned files, checked for DB/WAL/SHM. Public-file callers remain unchanged. Fixtures: TestPlan14AuthorityFilePrivateAncestors, TestPlan14AuthorityFileAbsoluteBoundary; TestPlan14GenerationPrivateJournalRootRunner and TestPlan14GenerationRequiredArtifactsRootRunner are runner-pending. Pre-flight 76, 78, 82 and 89. file:Openat@505 c7a9d1950ab15676b18afb750939c3e7e7204d4396be07959a61c97836720f99 |
| internal/rootstate/authorityfile_other.go OpenAuthorityFile authority E2 E33: census-only generation journal reads accept private root-owned ancestry without chmod or repair. Every ancestor remains descriptor-checked, root-owned and non-writable by group/other; leaves remain single-link regular root-owned files, checked for DB/WAL/SHM. Public-file callers remain unchanged. Fixtures: TestPlan14AuthorityFilePrivateAncestors, TestPlan14AuthorityFileAbsoluteBoundary; TestPlan14GenerationPrivateJournalRootRunner and TestPlan14GenerationRequiredArtifactsRootRunner are runner-pending. Pre-flight 76, 78, 82 and 89. a22c1b26fc03197a6cb312ac5c92bb1038d8fde1df00f444f232a0cd7a54878c |
| +internal/host/rootgeneration_requirements.go productionGenerationCreation authority E2 E34: census-only generation completeness requires E1 root creation evidence for an empty installation without retained predecessors. The exact host/account/app/installation must remain fresh on both sides of kernel inspection. Existing predecessor obligations remain mandatory. Production root history uses checked private ancestry and recommits before granting freshness, so read or sync failure refuses. Pre-flight 20, 33, 79 and 80. Fixtures: TestPlan14GenerationEmptyInstallationRequiresCreation, TestPlan14GenerationCreationFromRegistration, TestPlan14GenerationRequiredArtifacts; production checked-path TestPlan14GenerationRequiredArtifactsRootRunner remains runner-pending. No new history initialization or claims-off call. file:Open@88 0d2db2e90e40eab39d3c975fe32803464d0d821b1e135f4affd6eaa586d8ac4d |