WIP checkpoint (not for merge): claim Plan 14 track E checkpoint 26: E2 payload-inventory reconciliation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
8 files changed,
+420
−11
internal/host/plan14_generation_inventory_root_test.go+14 −1internal/host/plan14_generation_inventory_test.go+11 −4internal/host/plan14_generation_payload_test.go+129 −0internal/host/rootgeneration_inventory.go+49 −1internal/hostbox/generationpayload.go+87 −0internal/hostbox/plan14_generation_payload_test.go+121 −0internal/provision/plan14_e_write_census_test.go+1 −0internal/provision/testdata/plan14-root-reads-e.tsv+8 −5
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 351.477s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 3.019s
ok github.com/carlosframework/platform/cmd/moveprobe 93.314s
ok github.com/carlosframework/platform/cmd/zddprobe 0.707s
ok github.com/carlosframework/platform/internal/activator 233.763s
ok github.com/carlosframework/platform/internal/agentsock 0.017s
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.327s
ok github.com/carlosframework/platform/internal/apperr 0.070s
ok github.com/carlosframework/platform/internal/applog 0.128s
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.669s
? 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.714s
ok github.com/carlosframework/platform/internal/changelog 0.292s
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 365.763s
? 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.393s
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.003s
ok github.com/carlosframework/platform/internal/docsite 0.005s
ok github.com/carlosframework/platform/internal/domains 2.147s
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.154s
? 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.125s
ok github.com/carlosframework/platform/internal/gate 1.655s
ok github.com/carlosframework/platform/internal/genselect 0.015s
ok github.com/carlosframework/platform/internal/geo 0.016s
ok github.com/carlosframework/platform/internal/geoip 0.031s
ok github.com/carlosframework/platform/internal/historyreadback 0.155s
ok github.com/carlosframework/platform/internal/host 132.913s
ok github.com/carlosframework/platform/internal/hostactors 0.292s
ok github.com/carlosframework/platform/internal/hostbox 2.245s
ok github.com/carlosframework/platform/internal/hostclaim 0.525s
ok github.com/carlosframework/platform/internal/hostevidence 0.011s
ok github.com/carlosframework/platform/internal/hostexec 4.945s
ok github.com/carlosframework/platform/internal/hostfence 0.030s
ok github.com/carlosframework/platform/internal/hostfiles 10.498s
ok github.com/carlosframework/platform/internal/hostfloor 0.014s
ok github.com/carlosframework/platform/internal/hostguard 1.017s
ok github.com/carlosframework/platform/internal/hostops 1.483s
? 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.002s
ok github.com/carlosframework/platform/internal/hostrelocate 0.459s
ok github.com/carlosframework/platform/internal/hostresolve 0.127s
ok github.com/carlosframework/platform/internal/hostwrite 0.003s
ok github.com/carlosframework/platform/internal/infraview 0.002s
ok github.com/carlosframework/platform/internal/instancectl 1.821s
ok github.com/carlosframework/platform/internal/instanceexec 2.734s
ok github.com/carlosframework/platform/internal/instancefs 0.006s
ok github.com/carlosframework/platform/internal/instancekey 0.004s
ok github.com/carlosframework/platform/internal/instanceupdates 0.010s
ok github.com/carlosframework/platform/internal/layout 1.440s
ok github.com/carlosframework/platform/internal/lease 9.447s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.005s
ok github.com/carlosframework/platform/internal/ledger 0.013s
ok github.com/carlosframework/platform/internal/mail 0.161s
ok github.com/carlosframework/platform/internal/maintenance 0.076s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.529s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.074s
ok github.com/carlosframework/platform/internal/metrics 0.005s
ok github.com/carlosframework/platform/internal/moves 21.050s
ok github.com/carlosframework/platform/internal/moves/archive 0.777s
ok github.com/carlosframework/platform/internal/objstore 0.162s
ok github.com/carlosframework/platform/internal/ops 0.183s
ok github.com/carlosframework/platform/internal/park 0.208s
ok github.com/carlosframework/platform/internal/privatefile 0.006s
ok github.com/carlosframework/platform/internal/provision 12.731s
ok github.com/carlosframework/platform/internal/publicrelease 0.010s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 4.040s
ok github.com/carlosframework/platform/internal/relay 7.231s
ok github.com/carlosframework/platform/internal/release 21.803s
ok github.com/carlosframework/platform/internal/rollout 3.735s
ok github.com/carlosframework/platform/internal/rolloutwords 0.005s
ok github.com/carlosframework/platform/internal/rootaws 0.009s
ok github.com/carlosframework/platform/internal/rootparticipation 0.007s
ok github.com/carlosframework/platform/internal/rootstate 0.024s
ok github.com/carlosframework/platform/internal/s3compat 0.005s
ok github.com/carlosframework/platform/internal/safefile 0.004s
ok github.com/carlosframework/platform/internal/schedule 2.310s
ok github.com/carlosframework/platform/internal/sim/hostlife 9.546s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.952s
ok github.com/carlosframework/platform/internal/status 0.042s
ok github.com/carlosframework/platform/internal/steering 0.005s
ok github.com/carlosframework/platform/internal/stopevidence 0.220s
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.005s
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.002s
ok github.com/carlosframework/platform/internal/vet 1.561s
=== 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.024s
=== RUN TestT4StorageProbeMinio
2026/10/14 17:15:32 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DE74CEB1B6EC91, 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.021s
minio contract: PASS
diff --git a/internal/host/plan14_generation_inventory_root_test.go b/internal/host/plan14_generation_inventory_root_test.go| index 97012a507..3417e984c 100644 |
| --- a/internal/host/plan14_generation_inventory_root_test.go |
| +++ b/internal/host/plan14_generation_inventory_root_test.go |
| @@ -38,11 +38,24 @@ func TestPlan14GenerationInventoryCheckedRootReader(t *testing.T) { |
| if err != nil { |
| t.Fatal(err) |
| } |
| + service, err := hostbox.NewJournalService(journal) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + in := hostbox.StopInventory{Complete: true, Host: "retained.example", Incarnation: "inc", InstallationStamp: 2, Box: c.Box, Enrollment: c.Enrollment, DirectoryRevision: "1"} |
| + if err := service.Inventory.WriteStopInventory(t.Context(), in); err != nil { |
| + t.Fatal(err) |
| + } |
| journal.Close() |
| got, err := generationInventoryAt(t.Context(), censusPath, fencesPath, journalPath) |
| - if err != nil || got.Census != c || len(got.Installations) != 0 || len(got.Launches) != 0 { |
| + if err != nil || got.Census != c || len(got.Installations) != 0 || len(got.Launches) != 0 || len(got.Payloads) != 1 || got.Payloads[0].Revision != 1 || got.Payloads[0].Inventory.Host != in.Host { |
| t.Fatalf("checked snapshot: %+v %v", got, err) |
| } |
| + // An independently discovered payload without its installation cannot be |
| + // certified, even though the payload itself has Complete set. |
| + if err := generationPayloadBindings(got.Payloads, nil); err == nil { |
| + t.Fatal("orphan payload granted completeness") |
| + } |
| for _, attack := range []string{"writable-db", "service-db", "symlink-db", "writable-wal", "service-wal", "missing-db", "missing-census"} { |
| t.Run(attack, func(t *testing.T) { |
| switch attack { |
diff --git a/internal/host/plan14_generation_inventory_test.go b/internal/host/plan14_generation_inventory_test.go| index 4082afa96..2ce1f35fa 100644 |
| --- a/internal/host/plan14_generation_inventory_test.go |
| +++ b/internal/host/plan14_generation_inventory_test.go |
| @@ -32,7 +32,7 @@ func TestPlan14GenerationInventoryCompleteness(t *testing.T) { |
| if err != nil { |
| t.Fatal(err) |
| } |
| - snapshot := generationInventory{Census: rootparticipation.Census{Schema: 1, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: []hostguard.FenceView{view}, Launches: []hostbox.LaunchInventory{launch}} |
| + snapshot := generationInventory{Census: rootparticipation.Census{Schema: 1, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}, Payloads: generationPayloadFixture([]hostguard.FenceView{view}), Installations: []hostguard.FenceView{view}, Launches: []hostbox.LaunchInventory{launch}} |
| records := []release.GenerationReservation{r} |
| switch attack { |
| case "empty-reservations": |
| @@ -121,7 +121,7 @@ func TestPlan14GenerationInventoryEmptyNeedsIndependentSources(t *testing.T) { |
| } |
| func TestPlan14GenerationInventoryPinConsumer(t *testing.T) { |
| - for _, attack := range []string{"none", "omitted-reservation", "kernel", "lost-census"} { |
| + for _, attack := range []string{"none", "omitted-reservation", "kernel", "lost-census", "missing-payload", "payload-revision"} { |
| t.Run(attack, func(t *testing.T) { |
| f, disk, _ := generationLaunchFixture(t) |
| if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil { |
| @@ -143,7 +143,14 @@ func TestPlan14GenerationInventoryPinConsumer(t *testing.T) { |
| if attack == "lost-census" { |
| c.Complete = false |
| } |
| - return generationInventory{Census: c, Installations: []hostguard.FenceView{view}, Launches: []hostbox.LaunchInventory{launch}}, nil |
| + payloads := generationPayloadFixture([]hostguard.FenceView{view}) |
| + if attack == "missing-payload" { |
| + payloads = nil |
| + } |
| + if attack == "payload-revision" { |
| + payloads[0].Revision++ |
| + } |
| + return generationInventory{Census: c, Payloads: payloads, Installations: []hostguard.FenceView{view}, Launches: []hostbox.LaunchInventory{launch}}, nil |
| }, kernel: func(context.Context) (string, string, error) { |
| if attack == "kernel" { |
| return "", "", os.ErrPermission |
| @@ -256,7 +263,7 @@ func TestPlan14GenerationInventoryLifecycle(t *testing.T) { |
| if mode == "pending-wrong-controller" { |
| launches[0].ControllerPID++ |
| } |
| - return generationInventory{Census: rootparticipation.Census{Schema: 1, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: views, Launches: launches}, nil |
| + return generationInventory{Census: rootparticipation.Census{Schema: 1, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}, Payloads: generationPayloadFixture(views), Installations: views, Launches: launches}, nil |
| }, kernel: func(context.Context) (string, string, error) { return "boot", r.Binding.Digest, nil }} |
| source := generationPinSource{open: func() (rootstate.Store, error) { return disk, nil }, reconcile: v.reconcile} |
| pins, err := source.StagedPins(t.Context(), f.root.generations.optDir) |
diff --git a/internal/host/plan14_generation_payload_test.go b/internal/host/plan14_generation_payload_test.go| new file mode 100644 |
| index 000000000..5a89732ad |
| --- /dev/null |
| +++ b/internal/host/plan14_generation_payload_test.go |
| @@ -0,0 +1,129 @@ |
| +package host |
| + |
| +import ( |
| + "context" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/hostbox" |
| + "github.com/carlosframework/platform/internal/hostguard" |
| + "github.com/carlosframework/platform/internal/release" |
| + "github.com/carlosframework/platform/internal/rootparticipation" |
| +) |
| + |
| +func generationPayloadFixture(views []hostguard.FenceView) []hostbox.GenerationPayload { |
| + var out []hostbox.GenerationPayload |
| + for _, v := range views { |
| + in := v.Installation |
| + stamps := append([]hostguard.Captured{{Incarnation: in.Incarnation, Stamp: in.Stamp}}, v.Retired...) |
| + for _, stamp := range stamps { |
| + p := hostbox.GenerationPayload{Revision: in.InventoryRevision, Inventory: hostbox.StopInventory{Complete: true, Host: in.Host, Incarnation: stamp.Incarnation, InstallationStamp: stamp.Stamp, Box: in.Box, Enrollment: in.Enrollment, DirectoryRevision: "1"}} |
| + if !in.Empty { |
| + p.Inventory.Entries = []hostbox.StopInventoryEntry{{Kind: "route", ArtifactID: "route", ManagedRoot: "registry", RelativePath: in.Host, RowIdentity: "row", Ownership: "owner", Materialization: "materialized"}} |
| + } |
| + out = append(out, p) |
| + } |
| + } |
| + return out |
| +} |
| + |
| +func TestPlan14GenerationPayloadCompleteness(t *testing.T) { |
| + for _, attack := range []string{"none", "missing", "wrong-revision", "wrong-host", "wrong-incarnation", "wrong-stamp", "wrong-box", "wrong-enrollment", "incomplete", "empty", "duplicate", "extra-predecessor", "changed-payload", "changed-revision"} { |
| + t.Run(attack, func(t *testing.T) { |
| + f, disk, _ := generationLaunchFixture(t) |
| + if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil { |
| + t.Fatal(err) |
| + } |
| + r := readGenerationLaunch(t, disk) |
| + views, err := f.root.Guard.Root.GenerationInstallations(t.Context()) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + launch, err := f.journal.Read(t.Context(), f.target) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + snapshot := generationInventory{Census: rootparticipation.Census{Schema: 1, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: views, Launches: []hostbox.LaunchInventory{launch}, Payloads: generationPayloadFixture(views)} |
| + p := &snapshot.Payloads[0] |
| + switch attack { |
| + case "missing": |
| + snapshot.Payloads = nil |
| + case "wrong-revision": |
| + p.Revision++ |
| + case "wrong-host": |
| + p.Inventory.Host = "other.example" |
| + case "wrong-incarnation": |
| + p.Inventory.Incarnation = "other" |
| + case "wrong-stamp": |
| + p.Inventory.InstallationStamp++ |
| + case "wrong-box": |
| + p.Inventory.Box = "other" |
| + case "wrong-enrollment": |
| + p.Inventory.Enrollment = "other" |
| + case "incomplete": |
| + p.Inventory.Complete = false |
| + case "empty": |
| + p.Inventory.Entries = nil |
| + case "duplicate": |
| + snapshot.Payloads = append(snapshot.Payloads, *p) |
| + case "extra-predecessor": |
| + extra := *p |
| + extra.Inventory.InstallationStamp += 5 |
| + snapshot.Payloads = append(snapshot.Payloads, extra) |
| + } |
| + reads := 0 |
| + verifier := generationReconciler{read: func(context.Context) (generationInventory, error) { |
| + reads++ |
| + if reads > 1 { |
| + if attack == "changed-payload" { |
| + snapshot.Payloads[0].Inventory.DirectoryRevision = "2" |
| + } |
| + if attack == "changed-revision" { |
| + snapshot.Payloads[0].Revision++ |
| + } |
| + } |
| + return snapshot, nil |
| + }, kernel: func(context.Context) (string, string, error) { return "boot", r.Binding.Digest, nil }} |
| + e, err := verifier.reconcile(t.Context(), []release.GenerationReservation{r}) |
| + if (err == nil) != (attack == "none") { |
| + t.Fatalf("payload %s: evidence=%+v err=%v", attack, e, err) |
| + } |
| + if attack == "none" { |
| + snapshot.Payloads[0].Inventory.DirectoryRevision = "2" |
| + next, err := verifier.reconcile(t.Context(), []release.GenerationReservation{r}) |
| + if err != nil || next.Installations == e.Installations { |
| + t.Fatalf("changed retained payload reused evidence: %+v %v", next, err) |
| + } |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestPlan14GenerationPayloadRetainedPredecessor(t *testing.T) { |
| + for _, attack := range []string{"none", "missing-old", "foreign-old", "extra-incarnation", "wrong-empty", "fresh-empty"} { |
| + t.Run(attack, func(t *testing.T) { |
| + v := hostguard.FenceView{Host: "tenant.example", Installation: hostguard.RootInstallation{Host: "tenant.example", Incarnation: "inc", Stamp: 5, Box: "box", Enrollment: "en", InventoryRevision: 7, Empty: true}, Retired: []hostguard.Captured{{Incarnation: "inc", Stamp: 2}}} |
| + payloads := generationPayloadFixture([]hostguard.FenceView{v}) |
| + payloads[1].Revision = 3 |
| + payloads[1].Inventory.Entries = []hostbox.StopInventoryEntry{{Kind: "path", ArtifactID: "old-state", ManagedRoot: "data", RelativePath: "instances/tenant.example/state", Ownership: "owner", Materialization: "materialized", Device: 1, Inode: 2, Type: 0100000}} |
| + switch attack { |
| + case "missing-old": |
| + payloads = payloads[:1] |
| + case "foreign-old": |
| + payloads[1].Inventory.Enrollment = "other" |
| + case "extra-incarnation": |
| + extra := payloads[1] |
| + extra.Inventory.Incarnation = "other" |
| + payloads = append(payloads, extra) |
| + case "wrong-empty": |
| + v.Installation.Empty = false |
| + case "fresh-empty": |
| + v.Retired = nil |
| + payloads = payloads[:1] |
| + } |
| + err := generationPayloadBindings(payloads, map[string]hostguard.FenceView{v.Host: v}) |
| + if (err == nil) != (attack == "none" || attack == "fresh-empty") { |
| + t.Fatalf("%s: %v", attack, err) |
| + } |
| + }) |
| + } |
| +} |
diff --git a/internal/host/rootgeneration_inventory.go b/internal/host/rootgeneration_inventory.go| index b41cbe314..13bf24679 100644 |
| --- a/internal/host/rootgeneration_inventory.go |
| +++ b/internal/host/rootgeneration_inventory.go |
| @@ -21,6 +21,7 @@ type generationInventory struct { |
| Census rootparticipation.Census |
| Installations []hostguard.FenceView |
| Launches []hostbox.LaunchInventory |
| + Payloads []hostbox.GenerationPayload |
| } |
| type generationReconciler struct { |
| read func(context.Context) (generationInventory, error) |
| @@ -73,6 +74,9 @@ func (v generationReconciler) reconcile(ctx context.Context, records []release.G |
| } |
| installs[in.Host] = view |
| } |
| + if err := generationPayloadBindings(before.Payloads, installs); err != nil { |
| + return zero, err |
| + } |
| matched := make([]bool, len(records)) |
| hosts := map[string]bool{} |
| for _, launch := range before.Launches { |
| @@ -170,7 +174,47 @@ func (v generationReconciler) reconcile(ctx context.Context, records []release.G |
| if snapshotDigest != generationInventoryDigest(after) { |
| return zero, os.ErrPermission |
| } |
| - return release.GenerationEvidence{Box: c.Box, Enrollment: c.Enrollment, Migration: c.Migration, Boot: boot, Installations: generationInventoryDigest(before.Installations), Launches: generationInventoryDigest([]any{c.LaunchRevision, before.Launches}), Kernel: kernel}, nil |
| + return release.GenerationEvidence{Box: c.Box, Enrollment: c.Enrollment, Migration: c.Migration, Boot: boot, Installations: generationInventoryDigest([]any{before.Installations, before.Payloads}), Launches: generationInventoryDigest([]any{c.LaunchRevision, before.Launches}), Kernel: kernel}, nil |
| +} |
| + |
| +func generationPayloadBindings(payloads []hostbox.GenerationPayload, installs map[string]hostguard.FenceView) error { |
| + type key struct { |
| + host, incarnation string |
| + stamp uint64 |
| + } |
| + seen := map[key]bool{} |
| + for _, p := range payloads { |
| + in := p.Inventory |
| + view, ok := installs[in.Host] |
| + captured := hostguard.Captured{Incarnation: in.Incarnation, Stamp: in.InstallationStamp} |
| + current := captured.Incarnation == view.Installation.Incarnation && captured.Stamp == view.Installation.Stamp |
| + known := current |
| + for _, old := range view.Retired { |
| + known = known || old == captured |
| + } |
| + k := key{in.Host, in.Incarnation, in.InstallationStamp} |
| + if !ok || !known || seen[k] || !in.Complete || p.Revision == 0 || in.Box != view.Installation.Box || in.Enrollment != view.Installation.Enrollment || in.DirectoryRevision == "" { |
| + return os.ErrPermission |
| + } |
| + if current && (p.Revision != view.Installation.InventoryRevision || view.Installation.Empty != (len(in.Entries) == 0)) { |
| + return os.ErrPermission |
| + } |
| + seen[k] = true |
| + } |
| + for host, view := range installs { |
| + in := view.Installation |
| + if !seen[key{host, in.Incarnation, in.Stamp}] { |
| + return os.ErrPermission |
| + } |
| + // Rebind preserves predecessor payload obligations. A current empty |
| + // installation cannot erase the inventory of an older retained stamp. |
| + for _, old := range view.Retired { |
| + if !seen[key{host, old.Incarnation, old.Stamp}] { |
| + return os.ErrPermission |
| + } |
| + } |
| + } |
| + return nil |
| } |
| func productionGenerationInventory(ctx context.Context) (generationInventory, error) { |
| @@ -204,6 +248,10 @@ func generationInventoryAt(ctx context.Context, censusPath, fencesPath, journalP |
| } |
| defer journal.Close() |
| out.Launches, err = journal.GenerationLaunches(ctx) |
| + if err != nil { |
| + return out, err |
| + } |
| + out.Payloads, err = journal.GenerationPayloads(ctx) |
| return out, err |
| } |
diff --git a/internal/hostbox/generationpayload.go b/internal/hostbox/generationpayload.go| new file mode 100644 |
| index 000000000..d282450b9 |
| --- /dev/null |
| +++ b/internal/hostbox/generationpayload.go |
| @@ -0,0 +1,87 @@ |
| +package hostbox |
| + |
| +import ( |
| + "bytes" |
| + "context" |
| + "encoding/json" |
| + "io" |
| + "path/filepath" |
| +) |
| + |
| +type GenerationPayload struct { |
| + Inventory StopInventory |
| + Revision uint64 |
| +} |
| + |
| +// GenerationPayloads reads both sides of the inventory/revision relation. |
| +// An inner join would silently discard an obligation with a missing revision. |
| +// The caller still has to bind these records to root installations and inspect |
| +// kernel references; Complete in a stored payload cannot establish that alone. |
| +func (j *LaunchJournal) GenerationPayloads(ctx context.Context) ([]GenerationPayload, error) { |
| + if j == nil || j.db == nil || j.remote != nil { |
| + return nil, ErrUnsupported |
| + } |
| + if err := journalIdentity(j.path, j.identity); err != nil { |
| + return nil, err |
| + } |
| + rows, err := journalExecutorFor(ctx, j.db).QueryContext(ctx, ` |
| +SELECT k.host,k.incarnation,k.stamp,i.body,v.revision |
| +FROM (SELECT host,incarnation,stamp FROM stop_inventory |
| + UNION SELECT host,incarnation,stamp FROM journal_inventory_versions) k |
| +LEFT JOIN stop_inventory i USING(host,incarnation,stamp) |
| +LEFT JOIN journal_inventory_versions v USING(host,incarnation,stamp) |
| +ORDER BY k.host,k.incarnation,k.stamp`) |
| + if err != nil { |
| + return nil, err |
| + } |
| + defer rows.Close() |
| + var out []GenerationPayload |
| + for rows.Next() { |
| + var host, incarnation string |
| + var stamp uint64 |
| + var raw []byte |
| + var p GenerationPayload |
| + if err := rows.Scan(&host, &incarnation, &stamp, &raw, &p.Revision); err != nil { |
| + return nil, err |
| + } |
| + if len(raw) > 1<<20 || len(out) >= 8192 { |
| + return nil, ErrTypedInventory |
| + } |
| + dec := json.NewDecoder(bytes.NewReader(raw)) |
| + dec.DisallowUnknownFields() |
| + if dec.Decode(&p.Inventory) != nil || dec.Decode(new(any)) != io.EOF { |
| + return nil, ErrTypedInventory |
| + } |
| + in := p.Inventory |
| + if !component(host) || incarnation == "" || stamp == 0 || p.Revision == 0 || in.Host != host || in.Incarnation != incarnation || in.InstallationStamp != stamp || !in.Complete || in.Box == "" || in.Enrollment == "" || in.DirectoryRevision == "" { |
| + return nil, ErrTypedInventory |
| + } |
| + ids := map[string]bool{} |
| + for _, e := range in.Entries { |
| + if !component(e.ArtifactID) || ids[e.ArtifactID] || !filepath.IsLocal(e.RelativePath) || e.Ownership == "" || (e.Materialization != "intended" && e.Materialization != "materialized") { |
| + return nil, ErrTypedInventory |
| + } |
| + ids[e.ArtifactID] = true |
| + switch e.Kind { |
| + case "route", "timer": |
| + if e.ManagedRoot != "registry" || e.RowIdentity == "" || e.Device != 0 || e.Inode != 0 || e.Type != 0 { |
| + return nil, ErrTypedInventory |
| + } |
| + case "path": |
| + if e.ManagedRoot != "data" || e.RowIdentity != "" || (e.Materialization == "materialized" && (e.Device == 0 || e.Inode == 0 || e.Type == 0)) { |
| + return nil, ErrTypedInventory |
| + } |
| + default: |
| + return nil, ErrTypedInventory |
| + } |
| + } |
| + out = append(out, p) |
| + } |
| + if err := rows.Err(); err != nil { |
| + return nil, err |
| + } |
| + if err := journalIdentity(j.path, j.identity); err != nil { |
| + return nil, err |
| + } |
| + return out, nil |
| +} |
diff --git a/internal/hostbox/plan14_generation_payload_test.go b/internal/hostbox/plan14_generation_payload_test.go| new file mode 100644 |
| index 000000000..a43fa27f9 |
| --- /dev/null |
| +++ b/internal/hostbox/plan14_generation_payload_test.go |
| @@ -0,0 +1,121 @@ |
| +package hostbox |
| + |
| +import ( |
| + "encoding/json" |
| + "os" |
| + "path/filepath" |
| + "reflect" |
| + "testing" |
| +) |
| + |
| +func TestPlan14GenerationPayloadInventory(t *testing.T) { |
| + for _, attack := range []string{"none", "empty", "missing-payload", "missing-revision", "missing-table", "unknown-field", "trailing-json", "wrong-key", "incomplete", "oversize", "unknown-kind", "duplicate-entry", "missing-identity", "foreign-root", "replaced-path"} { |
| + t.Run(attack, func(t *testing.T) { |
| + j, err := OpenLaunchJournal(filepath.Join(t.TempDir(), "journal.db")) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer j.Close() |
| + s, err := NewJournalService(j) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + in := StopInventory{Complete: true, Host: "tenant.example", Incarnation: "inc", InstallationStamp: 1, Box: "box", Enrollment: "en", DirectoryRevision: "1", Entries: []StopInventoryEntry{{Kind: "path", ArtifactID: "payload", ManagedRoot: "data", RelativePath: "instances/tenant.example/state", Ownership: "owner", Materialization: "materialized", Device: 1, Inode: 2, Type: 0100000}}} |
| + if attack != "empty" { |
| + if err := s.Inventory.WriteStopInventory(t.Context(), in); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + query := "" |
| + switch attack { |
| + case "missing-payload": |
| + query = "DELETE FROM stop_inventory" |
| + case "missing-revision": |
| + query = "DELETE FROM journal_inventory_versions" |
| + case "missing-table": |
| + query = "DROP TABLE stop_inventory" |
| + case "unknown-field": |
| + query = "UPDATE stop_inventory SET body=json_set(body,'$.Unknown',true)" |
| + case "trailing-json": |
| + query = "UPDATE stop_inventory SET body=CAST(body || '{}' AS BLOB)" |
| + case "wrong-key": |
| + query = "UPDATE stop_inventory SET body=json_set(body,'$.Host','other.example')" |
| + case "incomplete": |
| + query = "UPDATE stop_inventory SET body=json_set(body,'$.Complete',json('false'))" |
| + case "oversize": |
| + query = "UPDATE stop_inventory SET body=zeroblob(1048577)" |
| + case "unknown-kind": |
| + in.Entries[0].Kind = "unknown" |
| + case "duplicate-entry": |
| + in.Entries = append(in.Entries, in.Entries[0]) |
| + case "missing-identity": |
| + in.Entries[0].Inode = 0 |
| + case "foreign-root": |
| + in.Entries[0].ManagedRoot = "opt" |
| + case "replaced-path": |
| + if err := os.Rename(j.path, j.path+".old"); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := os.WriteFile(j.path, []byte("other"), 0600); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + if query != "" { |
| + if _, err := j.db.Exec(query); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + if attack == "unknown-kind" || attack == "duplicate-entry" || attack == "missing-identity" || attack == "foreign-root" { |
| + raw, err := json.Marshal(in) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if _, err := j.db.Exec("UPDATE stop_inventory SET body=?", raw); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + // Use the production read-only connection, except for the retained-inode test. |
| + reader := j |
| + if attack != "replaced-path" { |
| + reader, err = OpenGenerationLaunchReader(j.path) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer reader.Close() |
| + } |
| + got, err := reader.GenerationPayloads(t.Context()) |
| + switch attack { |
| + case "none": |
| + if err != nil || len(got) != 1 || got[0].Revision != 1 || !reflect.DeepEqual(got[0].Inventory, in) { |
| + t.Fatalf("payloads=%+v err=%v", got, err) |
| + } |
| + case "empty": |
| + if err != nil || len(got) != 0 { |
| + t.Fatalf("empty=%+v err=%v", got, err) |
| + } |
| + default: |
| + if err == nil { |
| + t.Fatalf("unsafe %s payloads=%+v", attack, got) |
| + } |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestPlan14GenerationPayloadReaderDoesNotInitialize(t *testing.T) { |
| + path := filepath.Join(t.TempDir(), "missing.db") |
| + if err := os.WriteFile(path, nil, 0600); err != nil { |
| + t.Fatal(err) |
| + } |
| + j, err := OpenGenerationLaunchReader(path) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer j.Close() |
| + if _, err := j.GenerationPayloads(t.Context()); err == nil { |
| + t.Fatal("missing inventory schema accepted") |
| + } |
| + if st, err := os.Stat(path); err != nil || st.Size() != 0 { |
| + t.Fatalf("reader wrote schema: %v %v", st, err) |
| + } |
| +} |
diff --git a/internal/provision/plan14_e_write_census_test.go b/internal/provision/plan14_e_write_census_test.go| index 340ad96b1..ff7f42b5f 100644 |
| --- a/internal/provision/plan14_e_write_census_test.go |
| +++ b/internal/provision/plan14_e_write_census_test.go |
| @@ -2,6 +2,7 @@ package provision |
| // Track E overlays the reviewed Part 1 write census without changing its baseline. |
| var plan14EWriteCensusOverlay = []censusEntry{ |
| + {"internal/host/rootgeneration_inventory.go", "generationReconciler.reconcile", "key-site", "key:RecordKey", "f5966ea6b565fa1f881537874a884d9fe7781e44460fde961613b38baaf54ee3"}, |
| {"internal/host/rootgenerations_launch.go", "rootGenerationSource.resolve", "key-site", "key:RecordKey", "d69a3c0c86b03d5469708ac8bf084f4f516c0b61a18519d46cc3c0a0616f5381"}, |
| {"internal/historyreadback/workflow.go", "Operation.deliverGrant", "nonrecord", "sel:Put", "fb410ef6274ef7de0997a804c837ad1ecf29d8826ba1c4dcda00c2ae986fc434"}, |
| {"internal/host/rootinventory.go", "ClaimRootHTTP.commitRootInventoryPending", "key-site", "key:RecordKey", "cbc0c1fa8be968c6dd18a42ff200fe5765f25d35c07ed3ece3b18ad4d619d64c"}, |
diff --git a/internal/provision/testdata/plan14-root-reads-e.tsv b/internal/provision/testdata/plan14-root-reads-e.tsv| index 5e004d0c8..42cad1a09 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 a164d7f7e44edd30db1657361141593e267d4982d1d0cbca63677722429c5e0d |
| +@coverage internal/host 04670b9151cb86612fafacc8bdbe4bff1ab0324066d7914516d3317067c213d1 |
| @coverage internal/release 2bb546a86c1ab49b02e424eb0b366b46776857a1249baa2d6fb61ead4209d6bd |
| @coverage internal/rootstate 6338f74a6b2059cf77cf478bd6111b37b441eb9d65a309464c378359d4bdac05 |
| 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 |
| @@ -292,21 +292,24 @@ internal/release/materializeheld.go serveHeldStageRequest authority E2 E23: old |
| internal/host/heldstage.go heldStagePass authority E2 E23: old held-stage relay forwards the root generation source before artifact writes and outside publication exclusion. Request fields retain their prior hint/authorization checks; only root pins authorize destructive staging on census boxes. No-census source yields empty pins. Fixtures: TestPlan14GenerationLegacyRelay, TestHeldStagePassIsANoOpWithoutRequests, TestHeldStagePassDropsAnUnauthorizedBuild, TestServeHeldStageRequestsRefusesStaleAndTraversal. Production source wiring is inspected; privileged completeness is not claimed. 759f041ad504375f3000acccc10b5fba80aaa44ab574c3491d1df2cb49a35087 |
| internal/host/rootgeneration_census_linux.go checkedGenerationTree 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 tests are runner-pending. file:Lstat@467,file:Open@84,file:WalkDir@192 2a9f530f41143ce64723797ef4c5628b536caa6fefb11bcbe57adbec38f9a012 |
| internal/host/rootgeneration_census_linux.go generationKernel.census 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 tests are runner-pending. file:Open@170 5c43de2497824076701fb6275b86d629f0a1035be212d41754d90ba39c431140 |
| -internal/host/rootgeneration_inventory.go generationInventoryAt 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 tests are runner-pending. file:Open@173,file:Open@424 0fdfb9575baf9e9f5099e3ff463223d7b5100060fe3f6cf2908f20639e0e486e |
| +internal/host/rootgeneration_inventory.go generationInventoryAt 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 remains runner-pending; running-tenant containment remains unfinished. file:Open@173,file:Open@424 094f659671b83f647aa57b613b9b2d51a723cc6d00e49a6e19c71ff9f63c2087 |
| internal/hostbox/generationinventory.go LaunchJournal.GenerationLaunches 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 tests are runner-pending. file:QueryContext@270 6694bd3a35b8d91f53e67134145c44854086f08593110ed1b905bca9587be290 |
| internal/hostbox/generationinventory.go OpenGenerationLaunchReader 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 tests are runner-pending. file:Lstat@218,file:Open@283 552d8629a7932c4ae5dca9d2beef4559f7e8a2864259d11e2ff98c699fdcd999 |
| -@coverage internal/hostbox 9e63382b4bf1376b75aef3040de0ed079cff0de80ba77d81598c0c06b62041e5 |
| +@coverage internal/hostbox 2350dd34cc3415255fb3a95a0ddf2e140970c2742136abcaf6bff2e7e5745f5c |
| internal/host/rootgeneration_census_linux.go productionGenerationKernelCensus 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 tests are runner-pending. 85f085029e0cd557c8ff8eb2d522fdf131c388229c14515fda0f45453d40eebb |
| internal/host/rootgeneration_census_other.go productionGenerationKernelCensus 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 tests are runner-pending. a1217cf881e9d05336fc16d6050f7f7a1a2184a0c20ff2988d0cf0f9012eaa45 |
| internal/host/rootgeneration_inventory.go generationInstallation 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 tests are runner-pending. 3e5e15d50752510acb44b10eb29621757a87bff54ceae015f704dd7f3a848ecb |
| internal/host/rootgeneration_inventory.go generationInventoryDigest 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 tests are runner-pending. 36ff5a31c6a013ac907d85e59b754cf4088df4600646ba7df177cf14e990177a |
| 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 tests are runner-pending. 69f89153837761f13116937b330ffbf406891299d5580296ea4aade905aa02ce |
| -internal/host/rootgeneration_inventory.go generationReconciler.reconcile 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 tests are runner-pending. 35fba4c4b286b530288f83d150f93a49980cb5a587bdea1b69c4f71a90442fbf |
| +internal/host/rootgeneration_inventory.go generationReconciler.reconcile 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 remains runner-pending; running-tenant containment remains unfinished. f5966ea6b565fa1f881537874a884d9fe7781e44460fde961613b38baaf54ee3 |
| internal/host/rootgeneration_inventory.go openGenerationLaunchInventory 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 tests are runner-pending. 522445e6d8acbe083d9d8f001719c4dbd3af57c56429e6b73b05c55a3826cb8b |
| internal/host/rootgeneration_inventory.go productionGenerationInventory 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 tests are runner-pending. 8c888114e1d40fa8d5496775a7dd136dc1c7a732c997369af61449b2a0f55a41 |
| -internal/host/rootgeneration_inventory.go type generationInventory 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 tests are runner-pending. 398337e54b33efb37b21b181520f1bf9bcce48c34a0d66430f2e58505ae7bf00 |
| +internal/host/rootgeneration_inventory.go type generationInventory 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 remains runner-pending; running-tenant containment remains unfinished. f8951e4663d33e2f28a6df8071ea87f571acadf9188978faf5b713855f98a94c |
| 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 tests are runner-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 tests are runner-pending. 593768e6ba937cf2cfa2635c8f796f767d6c2354737a72073cc351f514faed20 |
| internal/host/rootgeneration_retirement.go generationRetirement.closure authority E2 E25: validate installation, controller, exact unit and process before retaining an active or prelaunch pin. Only closed writers plus a settled unit reach durable retirement and independent kernel absence. Use the checked read-only journal and continue past active pins. Fixtures: TestPlan14GenerationRetirementKeepsActivePin, TestPlan14GenerationRetirementKeepsPrelaunchPin, TestPlan14GenerationRetirementContinuesPastActivePin, TestPlan14GenerationRetirementBindingProcessCrash, TestPlan14GenerationRetirementCensusGate. Running-tenant completeness remains unfinished. f698ed949535287a55d5a8fc103f7dc5bf0f2c5020e27ff96552f42852024d03 |
| internal/host/rootgeneration_retirement.go generationRetirement.retireAll authority E2 E25: validate installation, controller, exact unit and process before retaining an active or prelaunch pin. Only closed writers plus a settled unit reach durable retirement and independent kernel absence. Use the checked read-only journal and continue past active pins. Fixtures: TestPlan14GenerationRetirementKeepsActivePin, TestPlan14GenerationRetirementKeepsPrelaunchPin, TestPlan14GenerationRetirementContinuesPastActivePin, TestPlan14GenerationRetirementBindingProcessCrash, TestPlan14GenerationRetirementCensusGate. Running-tenant completeness remains unfinished. c2ef0459f6836cceddd5dab65127f10323ffc797fe738d2c5f6040fadb1a6465 |
| internal/host/rootgeneration_kernel_linux.go generationTaskFDs authority E2 E25: keep the descriptor-listing handle open through inspection, including when scanning the current process. Check every listed descriptor against paths and artifact identities. Fixtures: TestPlan14GenerationKernelOwnDescriptors and TestPlan14GenerationKernelReferences. Root-runner acceptance pending: TestPlan14GenerationKernelCensusRootRunner and TestPlan14GenerationKernelRetirementRootRunner. file:Open@143,file:Readdirnames@367 06417eff13beac0b675b55f2664183f938afee33a733d51bb571c9a0ad3d50dc |
| +internal/host/rootgeneration_inventory.go generationPayloadBindings 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 remains runner-pending; running-tenant containment remains unfinished. 7f2f97393112ab29c308438a294deb306bdd6a4cd6df2c11cdce386ea3696095 |
| +internal/hostbox/generationpayload.go LaunchJournal.GenerationPayloads 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 remains runner-pending; running-tenant containment remains unfinished. file:QueryContext@272 c4aa5c5794f91bda7416b569e02bcd6003640b1256e5f8201aafc987fa681626 |
| +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 remains runner-pending; running-tenant containment remains unfinished. 992921ba14660af87a8099649ac589a7c2587467c05df052710fa45a9a5aa8bb |