carlos / platform Public

WIP claim Plan 14 track E checkpoint 37 (not for merge): the three E2 completeness gaps closed, with regression tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev 62a99c1926ca2e084b73278eaed91dd760397883 parent 4588e23
18 files changed, +480 −29
  • internal/host/plan14_generation_exact_retirement_root_linux_test.go +1 −1
  • internal/host/plan14_generation_requirements_linux_test.go +12 −1
  • internal/host/plan14_generation_requirements_root_linux_test.go +17 −4
  • internal/host/plan14_generation_rows_test.go +4 −3
  • internal/host/plan14_inventory_requirements_test.go +106 −0
  • internal/host/plan14_root_generations_test.go +1 −1
  • internal/host/rootgeneration_requirements.go +4 −1
  • internal/host/rootgeneration_rows.go +3 −0
  • internal/host/roothistory_creation.go +1 −0
  • internal/host/rootinventory.go +6 −1
  • internal/host/rootinventory_requirements.go +113 −0
  • internal/host/rootinventory_timer_test.go +11 −3
  • internal/hostguard/generationinventory.go +23 −5
  • internal/hostguard/plan14_artifacts_process_test.go +139 −0
  • internal/hostguard/root.go +19 −0
  • internal/hostguard/rootrebind.go +1 −0
  • internal/provision/plan14_e_write_census_test.go +2 −2
  • internal/provision/testdata/plan14-root-reads-e.tsv +17 −7

CI passed — run details

CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok  	amadan.net/carlos/platform.git/launchctx	0.012s
?   	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.840s
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	3.148s
ok  	github.com/carlosframework/platform/cmd/moveprobe	93.295s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.713s
ok  	github.com/carlosframework/platform/internal/activator	233.732s
ok  	github.com/carlosframework/platform/internal/agentsock	0.017s
ok  	github.com/carlosframework/platform/internal/alerting	0.004s
ok  	github.com/carlosframework/platform/internal/analytics	0.025s
ok  	github.com/carlosframework/platform/internal/appconfig	2.334s
ok  	github.com/carlosframework/platform/internal/apperr	0.085s
ok  	github.com/carlosframework/platform/internal/applog	0.155s
ok  	github.com/carlosframework/platform/internal/awsinv	0.005s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.005s
ok  	github.com/carlosframework/platform/internal/boxid	0.821s
?   	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.014s
ok  	github.com/carlosframework/platform/internal/certwarm	0.002s
ok  	github.com/carlosframework/platform/internal/cgroup	0.710s
ok  	github.com/carlosframework/platform/internal/changelog	0.316s
ok  	github.com/carlosframework/platform/internal/clamd	0.061s
?   	github.com/carlosframework/platform/internal/cliflag	[no test files]
ok  	github.com/carlosframework/platform/internal/console	365.695s
?   	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.164s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.018s
ok  	github.com/carlosframework/platform/internal/dbverify	0.010s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.006s
ok  	github.com/carlosframework/platform/internal/docsite	0.010s
ok  	github.com/carlosframework/platform/internal/domains	2.142s
ok  	github.com/carlosframework/platform/internal/doorkey	0.010s
ok  	github.com/carlosframework/platform/internal/economics	0.008s
ok  	github.com/carlosframework/platform/internal/edge	26.170s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.014s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.128s
ok  	github.com/carlosframework/platform/internal/gate	1.657s
ok  	github.com/carlosframework/platform/internal/genselect	0.019s
ok  	github.com/carlosframework/platform/internal/geo	0.013s
ok  	github.com/carlosframework/platform/internal/geoip	0.032s
ok  	github.com/carlosframework/platform/internal/historyreadback	0.154s
ok  	github.com/carlosframework/platform/internal/host	134.663s
ok  	github.com/carlosframework/platform/internal/hostactors	0.461s
ok  	github.com/carlosframework/platform/internal/hostbox	2.178s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.506s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.014s
ok  	github.com/carlosframework/platform/internal/hostexec	4.971s
ok  	github.com/carlosframework/platform/internal/hostfence	0.029s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.553s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.014s
ok  	github.com/carlosframework/platform/internal/hostguard	1.018s
ok  	github.com/carlosframework/platform/internal/hostops	1.460s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.014s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.015s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.448s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.128s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.014s
ok  	github.com/carlosframework/platform/internal/infraview	0.014s
ok  	github.com/carlosframework/platform/internal/instancectl	1.816s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.736s
ok  	github.com/carlosframework/platform/internal/instancefs	0.019s
ok  	github.com/carlosframework/platform/internal/instancekey	0.016s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.022s
ok  	github.com/carlosframework/platform/internal/layout	1.448s
ok  	github.com/carlosframework/platform/internal/lease	9.463s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.019s
ok  	github.com/carlosframework/platform/internal/ledger	0.010s
ok  	github.com/carlosframework/platform/internal/mail	0.162s
ok  	github.com/carlosframework/platform/internal/maintenance	0.078s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.536s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.070s
ok  	github.com/carlosframework/platform/internal/metrics	0.005s
ok  	github.com/carlosframework/platform/internal/moves	21.025s
ok  	github.com/carlosframework/platform/internal/moves/archive	0.777s
ok  	github.com/carlosframework/platform/internal/objstore	0.160s
ok  	github.com/carlosframework/platform/internal/ops	0.158s
ok  	github.com/carlosframework/platform/internal/park	0.195s
ok  	github.com/carlosframework/platform/internal/privatefile	0.003s
ok  	github.com/carlosframework/platform/internal/provision	13.912s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.006s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	4.487s
ok  	github.com/carlosframework/platform/internal/relay	7.239s
ok  	github.com/carlosframework/platform/internal/release	21.488s
ok  	github.com/carlosframework/platform/internal/rollout	3.811s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.004s
ok  	github.com/carlosframework/platform/internal/rootaws	0.010s
ok  	github.com/carlosframework/platform/internal/rootparticipation	0.007s
ok  	github.com/carlosframework/platform/internal/rootstate	0.026s
ok  	github.com/carlosframework/platform/internal/s3compat	0.005s
ok  	github.com/carlosframework/platform/internal/safefile	0.005s
ok  	github.com/carlosframework/platform/internal/schedule	2.307s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	9.935s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	4.758s
ok  	github.com/carlosframework/platform/internal/status	0.046s
ok  	github.com/carlosframework/platform/internal/steering	0.006s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.239s
ok  	github.com/carlosframework/platform/internal/svcrole	0.002s
ok  	github.com/carlosframework/platform/internal/telemetry	0.005s
ok  	github.com/carlosframework/platform/internal/termfence	0.004s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.004s
ok  	github.com/carlosframework/platform/internal/unixdial	0.002s
ok  	github.com/carlosframework/platform/internal/updatewords	0.004s
ok  	github.com/carlosframework/platform/internal/usage	0.004s
ok  	github.com/carlosframework/platform/internal/version	0.002s
ok  	github.com/carlosframework/platform/internal/vet	1.558s
=== 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 20:30:31 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DE7F7297228C53, 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_exact_retirement_root_linux_test.go b/internal/host/plan14_generation_exact_retirement_root_linux_test.go
index 838435859..3bfa6ced6 100644
--- a/internal/host/plan14_generation_exact_retirement_root_linux_test.go
+++ b/internal/host/plan14_generation_exact_retirement_root_linux_test.go
@@ -126,7 +126,7 @@ func TestPlan14GenerationExactRetirementRootRunner(t *testing.T) {
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}
+ in := hostguard.RootInstallation{Artifacts: hostguard.InstallationArtifacts{Schema: 1}, 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}
// Enrollment, installation, journal closure and group identities are fixtures.
// Kernel references, private stores, reconciliation and sweep are production.
write(fences, in.Host+".json", map[string]any{"Schema": 1, "Installation": in, "State": map[string]any{"v": 1, "host": in.Host, "incarnation": inc, "active_stamp": in.Stamp}})
diff --git a/internal/host/plan14_generation_requirements_linux_test.go b/internal/host/plan14_generation_requirements_linux_test.go
index c5843b702..6a262ddf1 100644
--- a/internal/host/plan14_generation_requirements_linux_test.go
+++ b/internal/host/plan14_generation_requirements_linux_test.go
@@ -21,7 +21,7 @@ import (
)
func TestPlan14GenerationRequiredArtifacts(t *testing.T) {
- for _, attack := range []string{"none", "service", "record-next-pass", "route-omitted", "state-omitted", "state-child-only", "state-wrong-host", "state-wrong-root", "state-file", "duplicate-route", "duplicate-state", "false-empty", "predecessor-omitted", "record-schema", "record-kind", "record-missing", "record-malformed", "record-account", "record-app", "record-host", "record-incarnation", "changed-record-during-kernel"} {
+ for _, attack := range []string{"none", "service", "record-next-pass", "route-omitted", "state-omitted", "historical-state-omitted", "requirements-missing", "requirements-schema", "requirements-malformed", "state-child-only", "state-wrong-host", "state-wrong-root", "state-file", "duplicate-route", "duplicate-state", "false-empty", "predecessor-omitted", "record-schema", "record-kind", "record-missing", "record-malformed", "record-account", "record-app", "record-host", "record-incarnation", "changed-record-during-kernel"} {
t.Run(attack, func(t *testing.T) {
f, disk, rec := generationLaunchFixture(t)
if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil {
@@ -123,6 +123,7 @@ func TestPlan14GenerationRequiredArtifacts(t *testing.T) {
}
}
if attack == "service" {
+ snapshot.Installations[0].Installation.Artifacts.StateDirectory = false
rec.Kind, rec.Addr = "service", "127.0.0.1:8080"
plan14PutRecord(t, store, rec)
snapshot.Payloads[0].Inventory.Entries = snapshot.Payloads[0].Inventory.Entries[:1]
@@ -147,8 +148,18 @@ func TestPlan14GenerationRequiredArtifacts(t *testing.T) {
}
entries := &snapshot.Payloads[0].Inventory.Entries
switch attack {
+ case "requirements-missing":
+ snapshot.Installations[0].Installation.Artifacts = hostguard.InstallationArtifacts{}
+ case "requirements-schema":
+ snapshot.Installations[0].Installation.Artifacts.Schema++
+ case "requirements-malformed":
+ snapshot.Installations[0].Installation.Artifacts.Timers = "bad/path"
case "route-omitted":
*entries = (*entries)[1:]
+ case "historical-state-omitted":
+ rec.Kind, rec.Addr = "service", "127.0.0.1:8080"
+ plan14PutRecord(t, store, rec)
+ *entries = (*entries)[:1]
case "state-omitted":
*entries = (*entries)[:1]
case "state-child-only", "state-wrong-host":
diff --git a/internal/host/plan14_generation_requirements_root_linux_test.go b/internal/host/plan14_generation_requirements_root_linux_test.go
index 0fc659b31..f326a9f7e 100644
--- a/internal/host/plan14_generation_requirements_root_linux_test.go
+++ b/internal/host/plan14_generation_requirements_root_linux_test.go
@@ -131,7 +131,7 @@ func TestPlan14GenerationRequiredArtifactsRootRunner(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- in := hostguard.RootInstallation{Host: rec.Host, Incarnation: inc, Stamp: 5, Account: rec.Account, App: rec.App, Record: provision.RecordKey(rec.Account, rec.App, rec.Host), Box: "runner", Enrollment: "en", Migration: 1, InventoryRevision: 1, Empty: true}
+ in := hostguard.RootInstallation{Artifacts: hostguard.InstallationArtifacts{Schema: 1, StateDirectory: true, Timers: "backup"}, Host: rec.Host, Incarnation: inc, Stamp: 5, Account: rec.Account, App: rec.App, Record: provision.RecordKey(rec.Account, rec.App, rec.Host), Box: "runner", Enrollment: "en", Migration: 1, InventoryRevision: 1, Empty: true}
write("/var/lib/carlos-claims/authority/fences", in.Host+".json", map[string]any{
"Schema": 1, "Installation": in, "Retired": []hostguard.Captured{{Incarnation: inc, Stamp: 2}},
"State": map[string]any{"v": 1, "host": in.Host, "incarnation": inc, "active_stamp": 5, "retired_stamps": []uint64{2}},
@@ -240,13 +240,24 @@ func TestPlan14GenerationRequiredArtifactsRootRunner(t *testing.T) {
}
}
accept()
- for _, omit := range []string{"route", "state", "timer"} {
+ for _, omit := range []string{"route", "state", "timer", "historical-state", "timer-both"} {
t.Run(omit, func(t *testing.T) {
initial := certificate()
+ omitEntry := omit
+ if omit == "historical-state" {
+ omitEntry = "state"
+ changed := rec
+ changed.Kind, changed.Addr = "service", "127.0.0.1:8080"
+ plan14PutRecord(t, store, changed)
+ }
+ if omit == "timer-both" {
+ omitEntry = "timer"
+ rows(route, nil)
+ }
missing := payload
missing.Entries = nil
for _, entry := range payload.Entries {
- if entry.ArtifactID != omit {
+ if entry.ArtifactID != omitEntry {
missing.Entries = append(missing.Entries, entry)
}
}
@@ -261,6 +272,8 @@ func TestPlan14GenerationRequiredArtifactsRootRunner(t *testing.T) {
t.Fatalf("omission damaged retained payload: %v", err)
}
put(payload)
+ plan14PutRecord(t, store, rec)
+ rows(route, []registry.Timer{timer})
accept()
})
}
@@ -432,7 +445,7 @@ func generationRunnerCreation(t *testing.T, opt string) {
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}
+ in := hostguard.RootInstallation{Artifacts: hostguard.InstallationArtifacts{Schema: 1, StateDirectory: true}, 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)
diff --git a/internal/host/plan14_generation_rows_test.go b/internal/host/plan14_generation_rows_test.go
index 8fce06122..9595eade2 100644
--- a/internal/host/plan14_generation_rows_test.go
+++ b/internal/host/plan14_generation_rows_test.go
@@ -17,7 +17,7 @@ import (
)
func TestPlan14GenerationRegistryRows(t *testing.T) {
- for _, attack := range []string{"none", "predecessor", "unknown-route-host", "unknown-timer-host", "unknown-host-during-kernel", "route-missing", "route-incarnation", "route-stamp", "route-owner", "route-id", "timer-missing", "timer-incarnation", "timer-stamp", "timer-owner", "timer-id", "timer-registration", "timer-extra", "route-entry-missing", "timer-entry-missing", "duplicate-entry", "timer-other-host", "wrong-root", "empty-installation", "unreadable", "changed-during-kernel", "row-next-pass", "timer-delivery-progress", "closed-registry"} {
+ for _, attack := range []string{"none", "predecessor", "unknown-route-host", "unknown-timer-host", "unknown-host-during-kernel", "route-missing", "route-incarnation", "route-stamp", "route-owner", "route-id", "timer-missing", "timer-both-omitted", "timer-incarnation", "timer-stamp", "timer-owner", "timer-id", "timer-registration", "timer-extra", "route-entry-missing", "timer-entry-missing", "duplicate-entry", "timer-other-host", "wrong-root", "empty-installation", "unreadable", "changed-during-kernel", "row-next-pass", "timer-delivery-progress", "closed-registry"} {
t.Run(attack, func(t *testing.T) {
f, disk, _ := generationLaunchFixture(t)
if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil {
@@ -37,6 +37,7 @@ func TestPlan14GenerationRegistryRows(t *testing.T) {
views[0].Installation.Stamp++
views[0].Installation.Empty = true
}
+ views[0].Installation.Artifacts.Timers = "backup"
payloads := generationPayloadFixture(views)
index := 0
if attack == "predecessor" {
@@ -145,7 +146,7 @@ func TestPlan14GenerationRegistryRows(t *testing.T) {
tm.Registration = "other"
case "route-entry-missing":
snapshot.Payloads[index].Inventory.Entries = snapshot.Payloads[index].Inventory.Entries[1:]
- case "timer-entry-missing":
+ case "timer-entry-missing", "timer-both-omitted":
snapshot.Payloads[index].Inventory.Entries = snapshot.Payloads[index].Inventory.Entries[:1]
case "duplicate-entry":
snapshot.Payloads[index].Inventory.Entries = append(snapshot.Payloads[index].Inventory.Entries, snapshot.Payloads[index].Inventory.Entries[1])
@@ -168,7 +169,7 @@ func TestPlan14GenerationRegistryRows(t *testing.T) {
tm.LastStatus = "running"
}
timers := []registry.Timer{tm}
- if attack == "timer-missing" {
+ if attack == "timer-missing" || attack == "timer-both-omitted" {
timers = nil
}
if attack == "timer-extra" {
diff --git a/internal/host/plan14_inventory_requirements_test.go b/internal/host/plan14_inventory_requirements_test.go
new file mode 100644
index 000000000..7f72a81f9
--- /dev/null
+++ b/internal/host/plan14_inventory_requirements_test.go
@@ -0,0 +1,106 @@
+package host
+
+import (
+ "context"
+ "crypto/sha256"
+ "encoding/hex"
+ "encoding/json"
+ "testing"
+
+ "github.com/carlosframework/platform/internal/hostbox"
+ "github.com/carlosframework/platform/internal/hostclaim"
+ "github.com/carlosframework/platform/internal/hostguard"
+ "github.com/carlosframework/platform/internal/provision"
+ "github.com/carlosframework/platform/internal/registry"
+)
+
+func TestPlan14InventoryRequirementsRegistration(t *testing.T) {
+ for _, kind := range []string{"stateful", "service"} {
+ t.Run(kind, func(t *testing.T) {
+ f, root, q := plan14InstallRegistration(t, hostclaim.KindServing, "")
+ // Use the claim's canonical account/app, independently of fixture defaults.
+ h := f.read().Claim.Holder
+ rec, _, err := provision.GetRecordETag(t.Context(), f.claims, h.Account, h.App, q.Target.Host)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if kind == "service" {
+ rec.Kind, rec.Addr = "service", "127.0.0.1:8080"
+ plan14PutRecord(t, f.claims, *rec)
+ _, q.RecordETag, err = f.claims.Get(t.Context(), q.RecordKey)
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ _, initial := hostbox.RemoteJournals(func(ctx context.Context, prepared hostbox.JournalRequest) (hostbox.JournalResponse, error) {
+ q = prepared
+ return root.commitRootInventory(ctx, prepared)
+ })
+ if _, err := initial.WriteExpected(t.Context(), q.Inventory, hostbox.InventoryExpectation{RecordKey: q.RecordKey, RecordETag: q.RecordETag, Target: q.Target}); err != nil {
+ t.Fatal(err)
+ }
+ views, err := root.Stop.Coordinator.Guard.Root.GenerationInstallations(t.Context())
+ if err != nil || len(views) != 1 {
+ t.Fatal(views, err)
+ }
+ want := hostguard.InstallationArtifacts{Schema: 1, StateDirectory: kind == "stateful"}
+ if views[0].Installation.Artifacts != want {
+ t.Fatalf("root requirements: %+v want %+v", views[0].Installation.Artifacts, want)
+ }
+ if _, err := root.commitRootInventory(t.Context(), q); err != nil {
+ t.Fatal("replay", err)
+ }
+ rec.Kind, rec.Addr = "service", "127.0.0.1:8080"
+ plan14PutRecord(t, f.claims, *rec)
+ _, etag, err := f.claims.Get(t.Context(), q.RecordKey)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, client := hostbox.RemoteJournals(root.commitRootInventory)
+ if _, err := client.WriteExpected(t.Context(), q.Inventory, hostbox.InventoryExpectation{Revision: 1, RecordKey: q.RecordKey, RecordETag: etag, Target: q.Target}); err != nil {
+ t.Fatal(err)
+ }
+ views, err = root.Stop.Coordinator.Guard.Root.GenerationInstallations(t.Context())
+ if err != nil || views[0].Installation.Artifacts != want {
+ t.Fatalf("shape change erased history: %+v %v", views, err)
+ }
+ })
+ }
+}
+
+func TestPlan14InventoryRequirementsUnknownAndRatchet(t *testing.T) {
+ prior := hostguard.RootInstallation{}
+ row := registry.Route{DBPath: "/data/instances/tenant.example/state.db"}
+ in := hostbox.StopInventory{Host: "tenant.example", Entries: []hostbox.StopInventoryEntry{{Kind: "timer", RelativePath: "tenant.example/backup"}}}
+ if got, err := rootInventoryArtifacts(prior, row, in, 2); err != nil || got != (hostguard.InstallationArtifacts{}) {
+ t.Fatalf("unknown history initialized: %+v %v", got, err)
+ }
+ prior.Artifacts = hostguard.InstallationArtifacts{Schema: 1, StateDirectory: true, Timers: "backup"}
+ got, err := rootInventoryArtifacts(prior, registry.Route{}, hostbox.StopInventory{Host: in.Host}, 2)
+ if err != nil || got != prior.Artifacts {
+ t.Fatal("lost old requirements", got, err)
+ }
+ for _, bad := range []hostguard.InstallationArtifacts{{}, {Schema: 2}, {Schema: 1, Timers: "backup\nbackup"}, {Schema: 1, Timers: "z\na"}, {Schema: 1, Timers: "../other"}, {Schema: 1, Timers: "backup\n"}} {
+ if _, err := inventoryTimerNames(bad); err == nil {
+ t.Fatalf("accepted malformed requirements: %+v", bad)
+ }
+ }
+}
+
+func TestPlan14InventoryRequirementsHistoryIdentity(t *testing.T) {
+ in := hostguard.RootInstallation{Host: "tenant.example", Incarnation: "inc", Stamp: 3, Account: "bab", App: "shop", Record: "record", Box: "box", Enrollment: "en", Migration: 1}
+ // This is the pre-E37 wire shape, including its field order.
+ raw := []byte(`{"Registration":{"Op":"","Kind":"","Phase":"","RowID":"","Session":0},"Empty":false,"Host":"tenant.example","Incarnation":"inc","Account":"bab","App":"shop","Record":"record","Box":"box","Enrollment":"en","Stamp":3,"Migration":1,"InventoryRevision":0}`)
+ var check hostguard.RootInstallation
+ if err := json.Unmarshal(raw, &check); err != nil || check != in {
+ t.Fatal(check, err)
+ }
+ sum := sha256.Sum256(raw)
+ want := hex.EncodeToString(sum[:])
+ for _, a := range []hostguard.InstallationArtifacts{{}, {Schema: 1, StateDirectory: true, Timers: "backup"}} {
+ in.Artifacts = a
+ if got := rootHistoryInstallation(in); got != want {
+ t.Fatalf("installation identity changed: %s want %s", got, want)
+ }
+ }
+}
diff --git a/internal/host/plan14_root_generations_test.go b/internal/host/plan14_root_generations_test.go
index 877ea6dc4..ff1827930 100644
--- a/internal/host/plan14_root_generations_test.go
+++ b/internal/host/plan14_root_generations_test.go
@@ -70,7 +70,7 @@ func generationLaunchFixture(t *testing.T) (*launchFixture, *generationDisk, pro
c.Holder.Admissions = []hostclaim.AdmissionRecord{{Box: f.target.Box, Region: f.target.Region, Session: f.target.Session, Generation: c.Holder.Generation}}
})
f.root.Guard.Root = hostguard.NewRootAuthority(&rollbackDisk{dir: t.TempDir()})
- in := hostguard.RootInstallation{Host: rec.Host, Account: rec.Account, App: rec.App, Record: provision.RecordKey(rec.Account, rec.App, rec.Host), Incarnation: inc, Stamp: f.target.Stamp, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, InventoryRevision: 1}
+ in := hostguard.RootInstallation{Artifacts: hostguard.InstallationArtifacts{Schema: 1, StateDirectory: true}, Host: rec.Host, Account: rec.Account, App: rec.App, Record: provision.RecordKey(rec.Account, rec.App, rec.Host), Incarnation: inc, Stamp: f.target.Stamp, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, InventoryRevision: 1}
if err := f.root.Guard.EstablishRoot(t.Context(), in, func(*hostclaim.Claim) error { return nil }); err != nil {
t.Fatal(err)
}
diff --git a/internal/host/rootgeneration_requirements.go b/internal/host/rootgeneration_requirements.go
index e8527fb69..4eb25f12e 100644
--- a/internal/host/rootgeneration_requirements.go
+++ b/internal/host/rootgeneration_requirements.go
@@ -35,6 +35,9 @@ func generationRequiredInventory(ctx context.Context, store release.Store, snaps
return zero, err
}
in := view.Installation
+ if _, err := inventoryTimerNames(in.Artifacts); err != nil {
+ return zero, err
+ }
if in.Record != provision.RecordKey(in.Account, in.App, in.Host) {
return zero, os.ErrPermission
}
@@ -94,7 +97,7 @@ func generationRequiredInventory(ctx context.Context, store release.Store, snaps
directories++
}
}
- if routes != 1 || (rec.DerivesPaths() && directories != 1) {
+ if routes != 1 || ((rec.DerivesPaths() || in.Artifacts.StateDirectory) && directories != 1) {
return zero, os.ErrPermission
}
}
diff --git a/internal/host/rootgeneration_rows.go b/internal/host/rootgeneration_rows.go
index f42b69310..7fb2a1aa8 100644
--- a/internal/host/rootgeneration_rows.go
+++ b/internal/host/rootgeneration_rows.go
@@ -25,6 +25,9 @@ func generationRegistryInventory(ctx context.Context, reg *registry.Registry, sn
if reg == nil {
return zero, os.ErrPermission
}
+ if err := generationRetainedTimers(snapshot); err != nil {
+ return zero, err
+ }
type key struct{ kind, host, name string }
expected := map[key]registry.ClaimOwnership{}
hosts := map[string]bool{}
diff --git a/internal/host/roothistory_creation.go b/internal/host/roothistory_creation.go
index 4ead4a102..ddf66355a 100644
--- a/internal/host/roothistory_creation.go
+++ b/internal/host/roothistory_creation.go
@@ -40,6 +40,7 @@ func productionCreationHistory(ctx context.Context, effect func(*release.RootHis
// Inventory additions do not change the identity of the installation.
func rootHistoryInstallation(in hostguard.RootInstallation) string {
+ in.Artifacts = hostguard.InstallationArtifacts{}
in.Empty = false
in.InventoryRevision = 0
in.Registration = hostguard.InstallationRegistration{}
diff --git a/internal/host/rootinventory.go b/internal/host/rootinventory.go
index c9c06951b..f77be991e 100644
--- a/internal/host/rootinventory.go
+++ b/internal/host/rootinventory.go
@@ -112,7 +112,7 @@ func (h ClaimRootHTTP) commitRootInventoryPending(ctx context.Context, q hostbox
return out, err
}
replayed := false
- err = guard.CommitRootInventory(ctx, installation, raw, func(c *hostclaim.Claim) error {
+ err = guard.CommitRootInventoryArtifacts(ctx, installation, raw, func(c *hostclaim.Claim) error {
if !reflect.DeepEqual(c, observed.Claim) {
return hostbox.ErrChanged
}
@@ -171,6 +171,9 @@ func (h ClaimRootHTTP) commitRootInventoryPending(ctx context.Context, q hostbox
} else if err := hostbox.ValidateAgentInventory(in, &canonical, h.Roots["data"]); err != nil {
return err
}
+ if err := h.verifyInventoryTimers(in); err != nil {
+ return err
+ }
for _, e := range in.Entries {
if e.Ownership != canonical.ClaimOwner {
return hostbox.ErrTypedInventory
@@ -288,6 +291,8 @@ func (h ClaimRootHTTP) commitRootInventoryPending(ctx context.Context, q hostbox
out, err = h.Journals.Execute(ctx, actor.Box+"/"+actor.Enrollment, q)
}
return err
+ }, func(prior hostguard.RootInstallation) (hostguard.InstallationArtifacts, error) {
+ return rootInventoryArtifacts(prior, canonical, in, q.Revision)
})
return out, err
}
diff --git a/internal/host/rootinventory_requirements.go b/internal/host/rootinventory_requirements.go
new file mode 100644
index 000000000..a132bda55
--- /dev/null
+++ b/internal/host/rootinventory_requirements.go
@@ -0,0 +1,113 @@
+package host
+
+import (
+ "os"
+ "sort"
+ "strings"
+
+ "github.com/carlosframework/platform/internal/hostbox"
+ "github.com/carlosframework/platform/internal/hostguard"
+ "github.com/carlosframework/platform/internal/registry"
+)
+
+func inventoryTimerNames(a hostguard.InstallationArtifacts) ([]string, error) {
+ if a.Schema != 1 || len(a.Timers) > 1<<20 {
+ return nil, os.ErrPermission
+ }
+ if a.Timers == "" {
+ return nil, nil
+ }
+ names := strings.Split(a.Timers, "\n")
+ if len(names) > 8192 {
+ return nil, os.ErrPermission
+ }
+ for i, name := range names {
+ if name == "" || name == "." || name == ".." || strings.ContainsAny(name, "/\\\r\x00") || (i > 0 && names[i-1] >= name) {
+ return nil, os.ErrPermission
+ }
+ }
+ return names, nil
+}
+
+func rootInventoryArtifacts(prior hostguard.RootInstallation, canonical registry.Route, in hostbox.StopInventory, revision uint64) (hostguard.InstallationArtifacts, error) {
+ a := prior.Artifacts
+ if a == (hostguard.InstallationArtifacts{}) {
+ // Only the independently checked first registration establishes absence.
+ // Older installations remain unknown; today's shape cannot reconstruct them.
+ if revision != 0 {
+ return a, nil
+ }
+ a.Schema = 1
+ }
+ names, err := inventoryTimerNames(a)
+ if err != nil {
+ return a, err
+ }
+ a.StateDirectory = a.StateDirectory || canonical.DBPath != ""
+ set := map[string]bool{}
+ for _, name := range names {
+ set[name] = true
+ }
+ for _, e := range in.Entries {
+ if e.Kind == "timer" {
+ set[strings.TrimPrefix(e.RelativePath, in.Host+"/")] = true
+ }
+ }
+ names = nil
+ for name := range set {
+ names = append(names, name)
+ }
+ sort.Strings(names)
+ a.Timers = strings.Join(names, "\n")
+ _, err = inventoryTimerNames(a)
+ return a, err
+}
+
+func (h ClaimRootHTTP) verifyInventoryTimers(in hostbox.StopInventory) error {
+ timers, err := h.Reconcile.Registry.TimersFor(in.Host)
+ if err != nil {
+ return err
+ }
+ if len(timers) > 8192 {
+ return hostbox.ErrTypedInventory
+ }
+ for _, timer := range timers {
+ found := false
+ for _, e := range in.Entries {
+ if e.Kind == "timer" && e.RelativePath == in.Host+"/"+timer.Name {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return hostbox.ErrTypedInventory
+ }
+ }
+ return nil
+}
+
+func generationRetainedTimers(snapshot generationInventory) error {
+ for _, view := range snapshot.Installations {
+ names, err := inventoryTimerNames(view.Installation.Artifacts)
+ if err != nil {
+ return err
+ }
+ present := map[string]bool{}
+ for _, p := range snapshot.Payloads {
+ if p.Inventory.Host != view.Host {
+ continue
+ }
+ for _, e := range p.Inventory.Entries {
+ if e.Kind == "timer" {
+ present[strings.TrimPrefix(e.RelativePath, view.Host+"/")] = true
+ }
+ }
+ }
+ for _, name := range names {
+ if !present[name] {
+ return os.ErrPermission
+ }
+ }
+ }
+ return nil
+}
diff --git a/internal/host/rootinventory_timer_test.go b/internal/host/rootinventory_timer_test.go
index d6f47ae7d..4721c895b 100644
--- a/internal/host/rootinventory_timer_test.go
+++ b/internal/host/rootinventory_timer_test.go
@@ -15,7 +15,7 @@ import (
)
func TestPlan14RootTimerInventory(t *testing.T) {
- for _, attack := range []string{"none", "intended", "missing", "row identity", "owner", "path", "root", "shape"} {
+ for _, attack := range []string{"none", "intended", "missing", "omitted-timer", "row identity", "owner", "path", "root", "shape"} {
t.Run(attack, func(t *testing.T) {
t.Setenv("CARLOS_HOST_CLAIM", "1")
f := newRecoveryFixture(t)
@@ -50,7 +50,7 @@ func TestPlan14RootTimerInventory(t *testing.T) {
}
g := f.guard
g.Root = hostguard.NewRootAuthority(&rollbackDisk{dir: t.TempDir()})
- if err = g.EstablishRoot(t.Context(), hostguard.RootInstallation{Host: o.Host, Incarnation: h.Incarnation, Stamp: h.Stamp, Account: h.Account, App: h.App, Record: h.Key, Box: "box-1", Enrollment: "en", Migration: 1, InventoryRevision: 1}, func(*hostclaim.Claim) error { return nil }); err != nil {
+ if err = g.EstablishRoot(t.Context(), hostguard.RootInstallation{Artifacts: hostguard.InstallationArtifacts{Schema: 1, StateDirectory: true}, Host: o.Host, Incarnation: h.Incarnation, Stamp: h.Stamp, Account: h.Account, App: h.App, Record: h.Key, Box: "box-1", Enrollment: "en", Migration: 1, InventoryRevision: 1}, func(*hostclaim.Claim) error { return nil }); err != nil {
t.Fatal(err)
}
actor := hostbox.Actor{Box: "box-1", Enrollment: "en", Revision: 1}
@@ -75,7 +75,9 @@ func TestPlan14RootTimerInventory(t *testing.T) {
case "shape":
e.Inode = 1
}
- in.Entries = append(in.Entries, e)
+ if attack != "omitted-timer" {
+ in.Entries = append(in.Entries, e)
+ }
_, client := hostbox.RemoteJournals(root.commitRootInventory)
_, err = client.WriteExpected(t.Context(), in, hostbox.InventoryExpectation{Revision: 1, RecordKey: h.Key, RecordETag: etag, Target: hostguard.FenceTarget{Host: o.Host, Incarnation: h.Incarnation, Stamp: h.Stamp, Session: h.Session, Box: actor.Box, Enrollment: actor.Enrollment, ClaimETag: o.ETag, Op: o.Claim.Op.ID, Phase: o.Claim.Op.Phase}})
valid := attack == "none" || attack == "intended"
@@ -87,6 +89,12 @@ func TestPlan14RootTimerInventory(t *testing.T) {
if valid {
want = 2
}
+ if valid {
+ views, readErr := g.Root.GenerationInstallations(t.Context())
+ if readErr != nil || len(views) != 1 || views[0].Installation.Artifacts.Timers != "daily" {
+ t.Fatalf("timer obligation not retained: %+v %v", views, readErr)
+ }
+ }
if revision != want {
t.Fatal(revision, want)
}
diff --git a/internal/hostguard/generationinventory.go b/internal/hostguard/generationinventory.go
index 0851eb378..5298344b0 100644
--- a/internal/hostguard/generationinventory.go
+++ b/internal/hostguard/generationinventory.go
@@ -29,11 +29,7 @@ func (a *RootAuthority) GenerationInstallations(ctx context.Context) ([]FenceVie
return nil, ErrMalformedRecord
}
host := strings.TrimSuffix(name, ".json")
- r, err := a.read(host)
- if err != nil {
- return nil, err
- }
- view, err := a.viewLocked(host, Captured{Incarnation: r.Incarnation, Stamp: r.ActiveStamp})
+ view, err := a.generationInstallation(ctx, host)
if err != nil {
return nil, err
}
@@ -44,3 +40,25 @@ func (a *RootAuthority) GenerationInstallations(ctx context.Context) ([]FenceVie
}
return out, nil
}
+
+func (a *RootAuthority) generationInstallation(ctx context.Context, host string) (FenceView, error) {
+ unlock, err := a.store.Lock(ctx, host+".lock")
+ if err != nil {
+ return FenceView{}, err
+ }
+ defer unlock()
+ r, err := a.read(host)
+ if err != nil {
+ return FenceView{}, err
+ }
+ view, err := a.viewLocked(host, Captured{Incarnation: r.Incarnation, Stamp: r.ActiveStamp})
+ if err != nil {
+ return FenceView{}, err
+ }
+ // A visible final rename may have failed its directory sync. Recommit under
+ // the same host lock before these requirements can certify a generation read.
+ if err := a.write(r); err != nil {
+ return FenceView{}, err
+ }
+ return view, nil
+}
diff --git a/internal/hostguard/plan14_artifacts_process_test.go b/internal/hostguard/plan14_artifacts_process_test.go
new file mode 100644
index 000000000..a77a0c5ff
--- /dev/null
+++ b/internal/hostguard/plan14_artifacts_process_test.go
@@ -0,0 +1,139 @@
+//go:build unix
+
+package hostguard
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "testing"
+
+ "github.com/carlosframework/platform/internal/hostclaim"
+)
+
+type artifactsCrashStore struct {
+ *rootTestStore
+ phase string
+}
+
+func (s *artifactsCrashStore) Write(name string, raw []byte) error {
+ if err := s.rootTestStore.Write(name, raw); err != nil {
+ return err
+ }
+ var r rootRecord
+ if json.Unmarshal(raw, &r) != nil {
+ return ErrMalformedRecord
+ }
+ if s.phase == "intent" && r.PendingInstallation != nil {
+ os.Exit(77)
+ }
+ if s.phase == "sync" && r.PendingInstallation == nil {
+ return os.ErrPermission
+ }
+ return nil
+}
+
+func TestPlan14InventoryArtifactsCrash(t *testing.T) {
+ if dir := os.Getenv("PLAN14_E37_ARTIFACT_CHILD"); dir != "" {
+ f, target, _ := fenceFixture(t)
+ f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "app"
+ store := &artifactsCrashStore{rootTestStore: &rootTestStore{dir: dir}, phase: os.Getenv("PLAN14_E37_PHASE")}
+ f.g.Root = NewRootAuthority(store)
+ r, err := f.g.Root.read(target.Host)
+ if err != nil {
+ t.Fatal(err)
+ }
+ in := r.installation
+ in.InventoryRevision++
+ err = f.g.CommitRootInventoryArtifacts(t.Context(), in, []byte("request"), allow, nil, func(RootInstallation) error {
+ if err := os.WriteFile(filepath.Join(dir, "effect.lock"), []byte("committed"), 0600); err != nil {
+ return err
+ }
+ if store.phase == "commit" {
+ os.Exit(77)
+ }
+ return nil
+ }, func(prior RootInstallation) (InstallationArtifacts, error) {
+ a := prior.Artifacts
+ a.Timers = "backup"
+ return a, nil
+ })
+ if store.phase == "sync" && errors.Is(err, os.ErrPermission) {
+ os.Exit(77)
+ }
+ t.Fatal("crash boundary not reached", err)
+ }
+ for _, phase := range []string{"intent", "commit", "sync"} {
+ t.Run(phase, func(t *testing.T) {
+ f, target, captured := fenceFixture(t)
+ dir := t.TempDir()
+ store := &rootTestStore{dir: dir}
+ f.g.Root = NewRootAuthority(store)
+ f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "app"
+ old := RootInstallation{Artifacts: InstallationArtifacts{Schema: 1, StateDirectory: true}, Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "app", Record: "record", Box: target.Box, Enrollment: target.Enrollment, Migration: 1, InventoryRevision: 1}
+ if err := f.g.EstablishRoot(t.Context(), old, allow); err != nil {
+ t.Fatal(err)
+ }
+ binary, err := os.Executable()
+ if err != nil {
+ t.Fatal(err)
+ }
+ cmd := exec.CommandContext(t.Context(), binary, "-test.run=^TestPlan14InventoryArtifactsCrash$", "-test.count=1")
+ cmd.Dir = dir
+ cmd.Env = append(os.Environ(), "PLAN14_E37_ARTIFACT_CHILD="+dir, "PLAN14_E37_PHASE="+phase)
+ output, err := cmd.CombinedOutput()
+ var exit *exec.ExitError
+ if !errors.As(err, &exit) || exit.ExitCode() != 77 {
+ t.Fatalf("child: %v %s", err, output)
+ }
+ f.g.Root = NewRootAuthority(store)
+ r, err := f.g.Root.read(target.Host)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := InstallationArtifacts{Schema: 1, StateDirectory: true, Timers: "backup"}
+ if phase != "sync" {
+ if r.pendingInstallation == nil || r.pendingInstallation.Artifacts != want {
+ t.Fatal("intent lost requirements", r, err)
+ }
+ if _, err := f.g.Root.GenerationInstallations(t.Context()); err == nil {
+ t.Fatal("pending inventory certified")
+ }
+ } else {
+ if r.installation.Artifacts != want {
+ t.Fatal("visible commit lost requirements")
+ }
+ failing := NewRootAuthority(&artifactsCrashStore{rootTestStore: store, phase: "sync"})
+ for i := 0; i < 2; i++ {
+ if _, err := failing.GenerationInstallations(t.Context()); !errors.Is(err, os.ErrPermission) {
+ t.Fatal("unsynced requirements certified", err)
+ }
+ }
+ }
+ next := old
+ next.InventoryRevision++
+ if err := f.g.CommitRootInventoryArtifacts(t.Context(), next, []byte("request"), allow, nil, func(RootInstallation) error { return nil }, func(RootInstallation) (InstallationArtifacts, error) { return want, nil }); err != nil {
+ t.Fatal("resume", err)
+ }
+ views, err := f.g.Root.GenerationInstallations(t.Context())
+ if err != nil || len(views) != 1 || views[0].Installation.Artifacts != want {
+ t.Fatal("reopen lost requirements", views, err)
+ }
+ f.obs.Claim.Holder.Stamp++
+ f.obs.Claim.Holder.Session++
+ next.Stamp++
+ next.InventoryRevision = 1
+ next.Artifacts = InstallationArtifacts{}
+ if err := f.g.RebindRootInventory(t.Context(), captured, 2, next, []byte("rebind"), func(*hostclaim.Claim) error { return nil }, func() error { return nil }); err != nil {
+ t.Fatal(err)
+ }
+ views, err = f.g.Root.GenerationInstallations(context.Background())
+ if err != nil || len(views) != 1 || views[0].Installation.Artifacts != want || len(views[0].Retired) != 1 {
+ t.Fatal("rebind erased requirements", views, err)
+ }
+ })
+ }
+}
diff --git a/internal/hostguard/root.go b/internal/hostguard/root.go
index 3ffed074f..b7ae573bc 100644
--- a/internal/hostguard/root.go
+++ b/internal/hostguard/root.go
@@ -18,7 +18,14 @@ type InstallationRegistration struct {
Session uint64
}
+type InstallationArtifacts struct {
+ Schema int
+ StateDirectory bool
+ Timers string
+}
+
type RootInstallation struct {
+ Artifacts InstallationArtifacts `json:",omitzero"`
Registration InstallationRegistration `json:",omitempty"`
Empty bool
Host, Incarnation, Account, App, Record, Box, Enrollment string
@@ -109,6 +116,10 @@ func (g Guard) CommitRootInstallation(ctx context.Context, in RootInstallation,
return g.CommitRootInventory(ctx, in, nil, check, nil, func(RootInstallation) error { return commit() })
}
func (g Guard) CommitRootInventory(ctx context.Context, in RootInstallation, intent []byte, check func(*hostclaim.Claim) error, validate func(RootInstallation) error, commit func(RootInstallation) error) error {
+ return g.CommitRootInventoryArtifacts(ctx, in, intent, check, validate, commit, nil)
+}
+
+func (g Guard) CommitRootInventoryArtifacts(ctx context.Context, in RootInstallation, intent []byte, check func(*hostclaim.Claim) error, validate func(RootInstallation) error, commit func(RootInstallation) error, artifacts func(RootInstallation) (InstallationArtifacts, error)) error {
if g.Root == nil || check == nil || commit == nil || in.Host == "" || in.Incarnation == "" || in.Stamp == 0 || in.Migration == 0 || in.Account == "" || in.App == "" || in.Record == "" || in.Box == "" || in.Enrollment == "" {
return ErrInvalidInput
}
@@ -165,6 +176,14 @@ func (g Guard) CommitRootInventory(ctx context.Context, in RootInstallation, int
if prior.Incarnation == in.Incarnation && prior.Stamp == in.Stamp && in.Registration == (InstallationRegistration{}) {
in.Registration = prior.Registration
}
+ if artifacts != nil {
+ in.Artifacts, err = artifacts(prior)
+ if err != nil {
+ return err
+ }
+ } else if prior.Incarnation == in.Incarnation && in.Artifacts == (InstallationArtifacts{}) {
+ in.Artifacts = prior.Artifacts
+ }
if r.pendingInstallation != nil && (*r.pendingInstallation != in || !bytes.Equal(r.pendingInventory, intent)) {
return ErrFenced
}
diff --git a/internal/hostguard/rootrebind.go b/internal/hostguard/rootrebind.go
index 69850bea2..a94e49693 100644
--- a/internal/hostguard/rootrebind.go
+++ b/internal/hostguard/rootrebind.go
@@ -42,6 +42,7 @@ func (g Guard) RebindRootInventory(ctx context.Context, prior Captured, revision
return ErrFenced
}
}
+ next.Artifacts = r.installation.Artifacts
if r.installation == next && r.pendingInstallation == nil && bytes.Equal(r.rebindCommit, intent) {
return d.sync(next.Host)
}
diff --git a/internal/provision/plan14_e_write_census_test.go b/internal/provision/plan14_e_write_census_test.go
index 63a671941..a81438fbd 100644
--- a/internal/provision/plan14_e_write_census_test.go
+++ b/internal/provision/plan14_e_write_census_test.go
@@ -2,11 +2,11 @@ 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", "d08773649c51f37ae31f03822741da485d306529177d5bf6450aea1ded9f778b"},
+ {"internal/host/rootgeneration_requirements.go", "generationRequiredInventory", "key-site", "key:RecordKey", "0ceac8b867717d959ccc6a66a44505a177ad56d09bd79d826c89a506eb8d00a8"},
{"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"},
- {"internal/host/rootinventory.go", "ClaimRootHTTP.commitRootInventoryPending", "key-site", "key:RecordKey", "cbc0c1fa8be968c6dd18a42ff200fe5765f25d35c07ed3ece3b18ad4d619d64c"},
+ {"internal/host/rootinventory.go", "ClaimRootHTTP.commitRootInventoryPending", "key-site", "key:RecordKey", "12b08da25b91b0827412cfe0119bdcc9a98bda5a60c56a987e0d44f7016d3bab"},
{"internal/host/rootinventory.go", "ClaimRootHTTP.recoverRootInventory", "key-site", "key:RecordKey", "a828684ad31a409d3e1fc306216d907681f514cb813302eb965a040e5084feda"},
{"internal/host/roothistory_creation.go", "rootRestartRequestSource.creation", "key-site", "key:RecordKey", "af79a66b746b0f42388010b38ea8130dca6a8d29c4ba5623bfc9c3cc40dd9601"},
{"internal/host/rootrestart_requests.go", "rootRestartRequests.consume", "key-site", "key:RecordKey", "0a9fbbe6f201077e276298b7e666e1ee2a345f1c420335290fec38a676b1ad84"},
diff --git a/internal/provision/testdata/plan14-root-reads-e.tsv b/internal/provision/testdata/plan14-root-reads-e.tsv
index 76bf2fa25..69330a916 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 1d5852649dddca1c23b871db7106675702d92e2dfc88a2c53856ececc93a42c0
+@coverage internal/host 95dbbe0e1849ac78eb20c623ea6929a8433970bd238fe16953e52e803643fd5c
@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
@@ -163,11 +163,11 @@ internal/host/claimretire_transport.go type ClaimRootHTTP authority E1 E16: root
internal/host/claimunit.go claimUnitContent authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. 38dcfeb754659895bc28b024bc3186c6b12edeb17d087546507fa1299eb8e331
internal/host/roothistory_creation.go productionCreationHistory authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. file:Open@295,file:Open@374 b4f4f82422cf0b1d874be7a0d9ee4a2032a4704b022bf73b8264097c2a53705c
internal/host/roothistory_creation.go rootHistoryCreation authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. file:Open@190,file:Open@300,whole:Route@535 49c28b2886b2ef03b818b2ad7d9a7f6fec65637f86e4d2f94e54394cac032bcc
-internal/host/roothistory_creation.go rootHistoryInstallation authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. 0b2eb471c2bb877739f75172ef05b4fd2f4f3ea0a11b84c0b72b35461a6d98fb
+internal/host/roothistory_creation.go rootHistoryInstallation authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. 5ca3a1163ea109b6e0ed1a9477023340cf39e6a810761133f1117d311e903b84
internal/host/roothistory_creation.go rootRestartRequestSource.creation authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. registry:Account@219,registry:App@243,registry:Host@133,registry:Host@194 af79a66b746b0f42388010b38ea8130dca6a8d29c4ba5623bfc9c3cc40dd9601
internal/host/roothistory_creation.go type rootHistoryAccess authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. 9c97bcd7878d79022ed942d8e6c00e944a00e0fa27a1bc5c732b4f4c10f4e9dd
internal/host/roothistory_launch.go ClaimLaunchRoot.rootInstalledProgram authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. file:OpenFile@541,file:Read@1043 e8d530dd1b8f613dfbd41d34a1b08b19aa3f74e0522c8f41db768ccd4e9e80d2
-internal/host/rootinventory.go ClaimRootHTTP.commitRootInventoryPending authority C,E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. file:Read@1383,file:Read@3414,registry:Account@4778,registry:Account@4793,registry:App@4814,registry:App@4825,registry:ClaimIncarnation@4284,registry:ClaimOwner@4334,registry:ClaimOwner@5317,registry:ClaimOwnership@4876,registry:ClaimOwnership@4898,registry:ClaimOwnership@5631,registry:ClaimOwnership@7632,registry:ClaimRowID@4498,registry:ClaimRowID@4575,registry:ClaimStamp@4312,registry:DBPath@4842,registry:DBPath@4856,registry:Host@4748,registry:Host@4760,whole:ClaimOwnership@4880,whole:ClaimOwnership@4908,whole:ClaimOwnership@5641,whole:ClaimOwnership@7643,whole:Route@3606,whole:Route@3989,whole:Route@4005,whole:Route@4060,whole:Route@4711,whole:Route@4724,whole:Route@4733,whole:Route@4971,whole:Route@5203,whole:Route@5879,whole:Route@6015,whole:Route@6239,whole:Route@7476,whole:Route@8883,whole:Route@8944 cbc0c1fa8be968c6dd18a42ff200fe5765f25d35c07ed3ece3b18ad4d619d64c
+internal/host/rootinventory.go ClaimRootHTTP.commitRootInventoryPending authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. file:Read@1383,file:Read@3423,registry:Account@4787,registry:Account@4802,registry:App@4823,registry:App@4834,registry:ClaimIncarnation@4293,registry:ClaimOwner@4343,registry:ClaimOwner@5398,registry:ClaimOwnership@4885,registry:ClaimOwnership@4907,registry:ClaimOwnership@5712,registry:ClaimOwnership@7713,registry:ClaimRowID@4507,registry:ClaimRowID@4584,registry:ClaimStamp@4321,registry:DBPath@4851,registry:DBPath@4865,registry:Host@4757,registry:Host@4769,whole:ClaimOwnership@4889,whole:ClaimOwnership@4917,whole:ClaimOwnership@5722,whole:ClaimOwnership@7724,whole:Route@3615,whole:Route@3998,whole:Route@4014,whole:Route@4069,whole:Route@4720,whole:Route@4733,whole:Route@4742,whole:Route@4980,whole:Route@5212,whole:Route@5960,whole:Route@6096,whole:Route@6320,whole:Route@7557,whole:Route@8964,whole:Route@9025,whole:Route@9565 12b08da25b91b0827412cfe0119bdcc9a98bda5a60c56a987e0d44f7016d3bab
internal/release/roothistory_creation.go RootHistory.ConsumeCreation authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. 7ec65199b5afc894ae4aefded6a40f485086d0f8a4c86391eca193f473dca2a1
internal/release/roothistory_creation.go RootHistory.FreshInstallation authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. 71b326a36350139c9c592b0b5b4b91409831fcffdd0c61d68cf5ec2bf54bbd5b
internal/release/roothistory_creation.go RootHistory.RecordCreation authority E1 E16: root first-registration creation evidence under release exclusion; exact installation identity is required for first-program adoption and is consumed before an attempt. Installed-program history comes from the verified claim launch. Fixtures: TestPlan14FreshInstallRegistration, TestPlan14FreshInstallProgramGate, TestPlan14FreshInstallCanonicalIdentity, TestPlan14FreshInstallLaunchObservation, TestPlan14InstalledLaunchProgramEvidence, TestPlan14FreshInstallProcessCrash, TestPlan14FreshInstallVisibleSync, TestClaimControlUnitInstallers. 0b545ab63902eb8f2c8988991786c964a2af53d65eff46d8e53a0747da492b6d
@@ -227,7 +227,7 @@ internal/release/rootgenerationpins.go type RootGenerationPins authority E2 E23:
internal/host/rootinstallation.go rootInstallationHostApp authority E2,J1 E20: read the atomic root installation under the existing host exclusion without recursive lock acquisition; retain fresh claim, canonical record, signed artifact and final reservation checks. Fixtures: TestPlan14GenerationLaunchDoesNotReenterRootLock, TestPlan14GenerationLaunchRefusals, TestPlan14RootDispatchRejectsRegistryMutation. file:Read@488,registry:Account@1062,registry:App@1091 0bbe17aff8c82841ae47fb01e060e1e1a80865e635d4a5c95dc5a638d2754d1c
internal/hostguard/rootview.go RootAuthority.Installation authority E2,J1 E20: read the atomic root installation under the existing host exclusion without recursive lock acquisition; retain fresh claim, canonical record, signed artifact and final reservation checks. Fixtures: TestPlan14GenerationLaunchDoesNotReenterRootLock, TestPlan14GenerationLaunchRefusals, TestPlan14RootDispatchRejectsRegistryMutation. d819256332950ec26c1b541f8be21a6e8cea839708fcbd0f7b8367d118574f92
internal/release/rootgenerations.go RootGenerations.Retire authority E2 E20: exact reservation closure precedes retirement verification; retained tombstones forbid replay and require sync recommit. Retired records release only their own pins; completeness remains a separate mandatory verifier, still unavailable in production. Fixtures: TestPlan14GenerationRetirementReleasesExactPin, TestPlan14GenerationRetirementRefusals, TestPlan14GenerationClosedReplayRefused, TestPlan14GenerationRetirementProcessCrash, TestPlan14GenerationRetirementKeepsIndependentReference, TestPlan14GenerationClaimsOffRestageMatchesMain. file:Read@297 21bb00d11d6fd8474b4d0a556bd1af7e69dbf6bd75755daeb0f14481166f2a90
-@coverage internal/hostguard 512a9f96349d27edba5ff3c0a016911b70b136dde59717ee9bef0a5579cff5a2
+@coverage internal/hostguard 017a3a049b377b2099de8f37c594fa9f5b0e806d0e64fcb1102acff2475241e2
internal/host/rootgeneration_kernel_linux.go generationGroupGone authority E2 E21: bounded kernel reader retains executable, sibling, descriptor, mapping and mount references; checks controller boot/start and cgroup emptiness. Unknown nonroot tasks, foreign namespaces, missing kernel state or renamed trees refuse. Fixtures: TestPlan14GenerationKernelReferences, TestPlan14GenerationKernelMappingIdentity, TestPlan14GenerationKernelControllerIdentity, TestPlan14GenerationKernelRunningExecutable, TestPlan14GenerationKernelCgroup, TestPlan14GenerationKernelRetainedFiles, TestPlan14GenerationKernelSiblingAlias. Real proc executable tests are not full privileged namespace acceptance. file:Lstat@218 a4778a7342e51a58eb736c15463e75c93349690d1f07b0d840d201ae8f57ecc6
internal/host/rootgeneration_kernel_linux.go generationIDRef authority E2 E21: bounded kernel reader retains executable, sibling, descriptor, mapping and mount references; checks controller boot/start and cgroup emptiness. Unknown nonroot tasks, foreign namespaces, missing kernel state or renamed trees refuse. Fixtures: TestPlan14GenerationKernelReferences, TestPlan14GenerationKernelMappingIdentity, TestPlan14GenerationKernelControllerIdentity, TestPlan14GenerationKernelRunningExecutable, TestPlan14GenerationKernelCgroup, TestPlan14GenerationKernelRetainedFiles, TestPlan14GenerationKernelSiblingAlias. Real proc executable tests are not full privileged namespace acceptance. 949b4dddb141d7eafd4edc70b41f8dd337069f699ab30d86c46bc6fce986d40b
internal/host/rootgeneration_kernel_linux.go generationKernel.absent authority E2 E21: bounded kernel reader retains executable, sibling, descriptor, mapping and mount references; checks controller boot/start and cgroup emptiness. Unknown nonroot tasks, foreign namespaces, missing kernel state or renamed trees refuse. Fixtures: TestPlan14GenerationKernelReferences, TestPlan14GenerationKernelMappingIdentity, TestPlan14GenerationKernelControllerIdentity, TestPlan14GenerationKernelRunningExecutable, TestPlan14GenerationKernelCgroup, TestPlan14GenerationKernelRetainedFiles, TestPlan14GenerationKernelSiblingAlias. Real proc executable tests are not full privileged namespace acceptance. file:Readlink@312 18269bf4b64e05138dff05e7894d4925bc24bcc012fa64a336723d981a9c52dd
@@ -306,7 +306,7 @@ internal/host/rootgeneration_inventory.go openGenerationLaunchInventory authorit
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
+internal/hostguard/generationinventory.go RootAuthority.GenerationInstallations authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. 4219e7952cc7c83513f810bae72dbed60384f8bddfd3d121de92eb8b1f82e866
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 passed at E27: TestPlan14GenerationKernelCensusRootRunner and TestPlan14GenerationKernelRetirementRootRunner. file:Open@143,file:Readdirnames@367 06417eff13beac0b675b55f2664183f938afee33a733d51bb571c9a0ad3d50dc
@@ -315,9 +315,9 @@ 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. 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 generationRequiredInventory authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. 0ceac8b867717d959ccc6a66a44505a177ad56d09bd79d826c89a506eb8d00a8
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
+internal/host/rootgeneration_rows.go generationRegistryInventory authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. registry:ClaimOwnership@2058,registry:ClaimOwnership@2437,registry:ClaimRowID@2490,registry:Host@2036,registry:Host@2415,registry:Name@2356,registry:Registration@2468,whole:ClaimOwnership@1334,whole:ClaimOwnership@1398,whole:ClaimOwnership@1412,whole:ClaimOwnership@1426,whole:ClaimOwnership@1935,whole:ClaimOwnership@2064,whole:ClaimOwnership@2082,whole:ClaimOwnership@2137,whole:ClaimOwnership@2386,whole:ClaimOwnership@2443,whole:ClaimOwnership@2461,whole:ClaimOwnership@2556,whole:ClaimOwnership@2584,whole:Route@1953,whole:Timer@2242,whole:Timer@2320 2878cd80e2828dcbfa77db4d9ad589d698cdbf61a3506ac2ecc13dae30970c5f
@coverage internal/registry b8bc7442621fce6ae121cf8557f44289803ec85971ee30581bd4c85ffad480bd
internal/registry/remote.go serveRemote authority E2 Mixed declaration: E2 adds only the ArtifactHosts read operation; all existing dispatch and write behavior stays legacy. Original immutable legacy fingerprints retained. 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. file:Open@332,registry:CallbackError@1712,registry:CallbackFailed@1669,registry:Deadline@1295,registry:Deadline@1369,registry:Method@1022,registry:Method@1043,registry:Method@1070,registry:Method@1604,whole:remoteRequest@1426,whole:remoteRequest@835,whole:remoteRequest@922,whole:remoteResponse@1148,whole:remoteResponse@1194,whole:remoteResponse@1488,whole:remoteResponse@1786,whole:remoteResponse@1822,whole:remoteResponse@392,whole:remoteResponse@431 40254c291f7b1a89b28e39eb1e4ffb3aa5852fd00de076ced1d3f7c48885e6dd
internal/registry/artifacthosts.go Registry.ArtifactHosts 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. file:Query@196,registry:Hosts@164,registry:remote@59,registry:remote@91,registry:sql@196,whole:remoteClient@61,whole:remoteClient@93 7310bda43df947e7b2d237dce83291dfe15596802d9db404f6ec0bb457cd6540
@@ -326,3 +326,13 @@ internal/rootstate/authorityfile_linux.go OpenAuthorityFile authority E2 E33: ce
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
+internal/host/rootinventory_requirements.go ClaimRootHTTP.verifyInventoryTimers authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. registry:Name@370,whole:Timer@176,whole:Timer@254 f7c4e729c18098e73867202fc3a1cde2d521cbb61ff68cf676a32ebee92acc3d
+internal/host/rootinventory_requirements.go generationRetainedTimers authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. 73db5d2174f16d56d9cf2dc2d743621590c6e873da3a86712e0a6fd15790af23
+internal/host/rootinventory_requirements.go inventoryTimerNames authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. 3fc943e0b77b892f855425f6f0f95abdf33c634f9d7edabb55011965adbfc390
+internal/host/rootinventory_requirements.go rootInventoryArtifacts authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. registry:DBPath@573 77d90e674c8c0949b9c31b927fff2518f87ebd33c0efd7d6bc6d965feb3446f5
+internal/hostguard/generationinventory.go RootAuthority.generationInstallation authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. 361a97c539df7c1cee6f6672c095806227b7ca6a9081e3a58d7afab14c56d9cc
+internal/hostguard/root.go Guard.CommitRootInventory authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. 59fe1795db13dbc9a7a3159749b1c52bc306f45da94a9b261fcc2e8cb2ca09c1
+internal/hostguard/root.go Guard.CommitRootInventoryArtifacts authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. 3f17f8e06dcef7f9070f5abec088192650d487fdfdbba180647d191f8c2b1a4f
+internal/hostguard/root.go type InstallationArtifacts authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. fab0473af135dbb33e0f4cf9709a07b5026feedd43709c1a681c650c3ae82dd5
+internal/hostguard/root.go type RootInstallation authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. 33adc00085827994991a0ba71e06cee27157eb04a870506b72c1f16ec3962606
+internal/hostguard/rootrebind.go Guard.RebindRootInventory authority E2 E37: retain independently initialized state-directory and timer requirements in the root installation transaction; preserve them through crash/reopen and rebind. Unknown history cannot certify completeness. Recommit under root-host exclusion before generation use. Canonical record and root retained obligations constrain agent candidate lists; timer enumeration is deny-only. Pre-flight 33, 80, 83. Fixtures: TestPlan14GenerationRequiredArtifacts, TestPlan14GenerationRegistryRows, TestPlan14RootTimerInventory, TestPlan14InventoryRequirementsRegistration, TestPlan14InventoryRequirementsUnknownAndRatchet, TestPlan14InventoryArtifactsCrash, TestPlan14InventoryRequirementsHistoryIdentity. Changed RequiredArtifacts and ExactRetirement root-runner witnesses await acceptance; remaining E2 launch/parked/containment integration is incomplete. 15f4c0b9159f193f9bad5215fe0224ba02276a9b58d036c98b4c4fe190856216