carlos / platform Public

WIP claim Plan 14 track E checkpoint 34 (not for merge)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev c3d0435b5cec7d1c9e6b412d60e6fd52100977bc parent 5454450
7 files changed, +340 −8
  • internal/host/plan14_generation_creation_test.go +177 −0
  • internal/host/plan14_generation_requirements_linux_test.go +1 −1
  • internal/host/plan14_generation_requirements_root_linux_test.go +128 −1
  • internal/host/rootgeneration_inventory.go +1 −1
  • internal/host/rootgeneration_requirements.go +28 −1
  • internal/provision/plan14_e_write_census_test.go +1 −1
  • internal/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