WIP checkpoint (not for merge): claim Plan 14 track E checkpoint 24
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
15 files changed,
+1181
−12
internal/host/plan14_generation_census_kernel_linux_test.go+41 −0internal/host/plan14_generation_census_root_linux_test.go+81 −0internal/host/plan14_generation_inventory_process_test.go+63 −0internal/host/plan14_generation_inventory_root_test.go+122 −0internal/host/plan14_generation_inventory_test.go+278 −0internal/host/rootgeneration_census_linux.go+84 −0internal/host/rootgeneration_census_other.go+12 −0internal/host/rootgeneration_inventory.go+222 −0internal/host/rootgenerationpins.go+7 −7internal/hostbox/generationinventory.go+74 −0internal/hostbox/plan14_generation_inventory_test.go+73 −0internal/hostguard/generationinventory.go+46 −0internal/hostguard/plan14_generation_inventory_test.go+55 −0internal/provision/plan9_b8_census_test.go+1 −0internal/provision/testdata/plan14-root-reads-e.tsv+22 −5
CI passed — run details
CI log
=== gate ===
go: downloading github.com/aws/aws-sdk-go-v2 v1.44.0
go: downloading github.com/aws/aws-sdk-go-v2/config v1.32.6
go: downloading github.com/aws/aws-sdk-go-v2/service/route53 v1.64.1
go: downloading github.com/benbjohnson/litestream v0.5.14
go: downloading github.com/aws/aws-sdk-go-v2/service/ec2 v1.324.0
go: downloading github.com/aws/aws-sdk-go-v2/service/acm v1.45.0
go: downloading github.com/aws/aws-sdk-go-v2/service/sns v1.43.0
go: downloading github.com/aws/aws-sdk-go-v2/service/cloudwatch v1.68.0
go: downloading modernc.org/sqlite v1.55.0
go: downloading github.com/aws/aws-sdk-go-v2/service/iam v1.58.2
go: downloading github.com/aws/aws-sdk-go-v2/service/resourcegroupstaggingapi v1.37.0
go: downloading github.com/superfly/ltx v0.5.1
go: downloading github.com/aws/aws-sdk-go-v2/service/s3 v1.105.0
go: downloading github.com/aws/aws-sdk-go-v2/service/sts v1.45.6
go: downloading github.com/aws/smithy-go v1.28.1
go: downloading golang.org/x/crypto v0.52.0
go: downloading github.com/oschwald/maxminddb-golang v1.13.1
go: downloading github.com/aws/aws-sdk-go-v2/credentials v1.19.6
go: downloading golang.org/x/sys v0.46.0
go: downloading github.com/klauspost/compress v1.18.0
go: downloading github.com/aws/aws-sdk-go-v2/service/sesv2 v1.66.6
go: downloading golang.org/x/net v0.55.0
go: downloading github.com/pion/logging v0.2.4
go: downloading github.com/pion/stun/v3 v3.0.1
go: downloading github.com/pion/turn/v4 v4.1.4
go: downloading github.com/aws/aws-sdk-go-v2/service/costexplorer v1.67.5
go: downloading github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.16
go: downloading github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4
go: downloading github.com/aws/aws-sdk-go-v2/service/signin v1.0.4
go: downloading github.com/aws/aws-sdk-go-v2/service/sso v1.30.8
go: downloading github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.12
go: downloading github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.40
go: downloading github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.38
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.40
go: downloading github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.23
go: downloading github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.31
go: downloading github.com/pion/randutil v0.1.0
go: downloading github.com/pion/transport/v4 v4.0.1
go: downloading github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.40
go: downloading github.com/pierrec/lz4/v4 v4.1.22
go: downloading github.com/wlynxg/anet v0.0.5
go: downloading github.com/prometheus/client_golang v1.17.0
go: downloading golang.org/x/sync v0.21.0
go: downloading github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.20.18
go: downloading github.com/mattn/go-isatty v0.0.20
go: downloading github.com/lmittmann/tint v1.1.3
go: downloading github.com/cespare/xxhash/v2 v2.3.0
go: downloading github.com/prometheus/procfs v0.12.0
go: downloading github.com/prometheus/client_model v0.5.0
go: downloading github.com/beorn7/perks v1.0.1
go: downloading github.com/prometheus/common v0.45.0
go: downloading google.golang.org/protobuf v1.36.10
go: downloading github.com/pion/transport/v3 v3.0.8
go: downloading github.com/pion/dtls/v3 v3.0.7
go: downloading github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0
go: downloading golang.org/x/text v0.37.0
go: downloading modernc.org/libc v1.74.1
go: downloading modernc.org/memory v1.11.0
go: downloading github.com/dustin/go-humanize v1.0.1
go: downloading github.com/google/uuid v1.6.0
go: downloading modernc.org/mathutil v1.7.1
go: downloading github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec
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 355.664s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 3.209s
ok github.com/carlosframework/platform/cmd/moveprobe 93.370s
ok github.com/carlosframework/platform/cmd/zddprobe 0.709s
ok github.com/carlosframework/platform/internal/activator 234.726s
ok github.com/carlosframework/platform/internal/agentsock 0.022s
ok github.com/carlosframework/platform/internal/alerting 0.005s
ok github.com/carlosframework/platform/internal/analytics 0.021s
ok github.com/carlosframework/platform/internal/appconfig 2.370s
ok github.com/carlosframework/platform/internal/apperr 0.082s
ok github.com/carlosframework/platform/internal/applog 0.136s
ok github.com/carlosframework/platform/internal/awsinv 0.005s
ok github.com/carlosframework/platform/internal/boxchoice 0.008s
ok github.com/carlosframework/platform/internal/boxid 0.739s
? github.com/carlosframework/platform/internal/boxid/boxidtest [no test files]
? github.com/carlosframework/platform/internal/boxid/internal/signtest [no test files]
ok github.com/carlosframework/platform/internal/boxvolumes 0.008s
ok github.com/carlosframework/platform/internal/caps 0.012s
ok github.com/carlosframework/platform/internal/certwarm 0.002s
ok github.com/carlosframework/platform/internal/cgroup 0.703s
ok github.com/carlosframework/platform/internal/changelog 0.313s
ok github.com/carlosframework/platform/internal/clamd 0.055s
? github.com/carlosframework/platform/internal/cliflag [no test files]
ok github.com/carlosframework/platform/internal/console 367.716s
? 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.404s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.010s
ok github.com/carlosframework/platform/internal/dbverify 0.005s
ok github.com/carlosframework/platform/internal/dnsrec 0.003s
ok github.com/carlosframework/platform/internal/docsite 0.005s
ok github.com/carlosframework/platform/internal/domains 2.324s
ok github.com/carlosframework/platform/internal/doorkey 0.006s
ok github.com/carlosframework/platform/internal/economics 0.007s
ok github.com/carlosframework/platform/internal/edge 26.704s
? github.com/carlosframework/platform/internal/edgeproof [no test files]
ok github.com/carlosframework/platform/internal/edgesfile 0.008s
ok github.com/carlosframework/platform/internal/fleetapi 0.146s
ok github.com/carlosframework/platform/internal/gate 1.656s
ok github.com/carlosframework/platform/internal/genselect 0.019s
ok github.com/carlosframework/platform/internal/geo 0.018s
ok github.com/carlosframework/platform/internal/geoip 0.033s
ok github.com/carlosframework/platform/internal/historyreadback 0.186s
ok github.com/carlosframework/platform/internal/host 136.501s
ok github.com/carlosframework/platform/internal/hostactors 0.284s
ok github.com/carlosframework/platform/internal/hostbox 2.252s
ok github.com/carlosframework/platform/internal/hostclaim 0.564s
ok github.com/carlosframework/platform/internal/hostevidence 0.006s
ok github.com/carlosframework/platform/internal/hostexec 5.505s
ok github.com/carlosframework/platform/internal/hostfence 0.036s
ok github.com/carlosframework/platform/internal/hostfiles 10.799s
ok github.com/carlosframework/platform/internal/hostfloor 0.016s
ok github.com/carlosframework/platform/internal/hostguard 1.007s
ok github.com/carlosframework/platform/internal/hostops 1.522s
? github.com/carlosframework/platform/internal/hostops/fencerelease [no test files]
ok github.com/carlosframework/platform/internal/hostops/internal/fencerelease 0.002s
ok github.com/carlosframework/platform/internal/hostquarantine 0.002s
ok github.com/carlosframework/platform/internal/hostrelocate 0.480s
ok github.com/carlosframework/platform/internal/hostresolve 0.144s
ok github.com/carlosframework/platform/internal/hostwrite 0.002s
ok github.com/carlosframework/platform/internal/infraview 0.005s
ok github.com/carlosframework/platform/internal/instancectl 1.821s
ok github.com/carlosframework/platform/internal/instanceexec 2.723s
ok github.com/carlosframework/platform/internal/instancefs 0.004s
ok github.com/carlosframework/platform/internal/instancekey 0.002s
ok github.com/carlosframework/platform/internal/instanceupdates 0.011s
ok github.com/carlosframework/platform/internal/layout 1.469s
ok github.com/carlosframework/platform/internal/lease 9.499s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.006s
ok github.com/carlosframework/platform/internal/ledger 0.013s
ok github.com/carlosframework/platform/internal/mail 0.187s
ok github.com/carlosframework/platform/internal/maintenance 0.085s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.543s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.075s
ok github.com/carlosframework/platform/internal/metrics 0.005s
ok github.com/carlosframework/platform/internal/moves 21.583s
ok github.com/carlosframework/platform/internal/moves/archive 0.791s
ok github.com/carlosframework/platform/internal/objstore 0.163s
ok github.com/carlosframework/platform/internal/ops 0.178s
ok github.com/carlosframework/platform/internal/park 0.203s
ok github.com/carlosframework/platform/internal/privatefile 0.003s
ok github.com/carlosframework/platform/internal/provision 33.826s
ok github.com/carlosframework/platform/internal/publicrelease 0.007s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 4.298s
ok github.com/carlosframework/platform/internal/relay 7.289s
ok github.com/carlosframework/platform/internal/release 22.919s
ok github.com/carlosframework/platform/internal/rollout 3.838s
ok github.com/carlosframework/platform/internal/rolloutwords 0.004s
ok github.com/carlosframework/platform/internal/rootaws 0.008s
ok github.com/carlosframework/platform/internal/rootparticipation 0.007s
ok github.com/carlosframework/platform/internal/rootstate 0.025s
ok github.com/carlosframework/platform/internal/s3compat 0.005s
ok github.com/carlosframework/platform/internal/safefile 0.004s
ok github.com/carlosframework/platform/internal/schedule 2.311s
ok github.com/carlosframework/platform/internal/sim/hostlife 11.810s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.997s
ok github.com/carlosframework/platform/internal/status 0.042s
ok github.com/carlosframework/platform/internal/steering 0.006s
ok github.com/carlosframework/platform/internal/stopevidence 0.252s
ok github.com/carlosframework/platform/internal/svcrole 0.002s
ok github.com/carlosframework/platform/internal/telemetry 0.004s
ok github.com/carlosframework/platform/internal/termfence 0.004s
ok github.com/carlosframework/platform/internal/tunnelclient 0.005s
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.005s
ok github.com/carlosframework/platform/internal/version 0.002s
ok github.com/carlosframework/platform/internal/vet 1.633s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
go: downloading github.com/ncruces/go-strftime v1.0.0
--- shard 1 of 5 ---
cross-compiling darwin/amd64
go: downloading github.com/ncruces/go-strftime v1.0.0
--- shard 2 of 5 ---
cross-compiling linux/arm64
--- shard 3 of 5 ---
cross-compiling windows/amd64
go: downloading github.com/ncruces/go-strftime v1.0.0
--- shard 4 of 5 ---
cross-compiling windows/arm64
go: downloading github.com/ncruces/go-strftime v1.0.0
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
minio: cached, digest verified
mc: cached, digest verified
minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
buckets and scoped user ready
=== RUN TestDeliveredContractMinio
=== RUN TestDeliveredContractMinio/put
=== RUN TestDeliveredContractMinio/get_round-trips_the_body
=== RUN TestDeliveredContractMinio/list_shows_the_key
=== RUN TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN TestDeliveredContractMinio/delete_removes_it
=== RUN TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
--- PASS: TestDeliveredContractMinio/put (0.00s)
--- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
--- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
--- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
--- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok github.com/carlosframework/platform/internal/objstore 0.024s
=== RUN TestT4StorageProbeMinio
2026/10/14 13:24:31 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DE68335BF22D64, 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_census_kernel_linux_test.go b/internal/host/plan14_generation_census_kernel_linux_test.go| new file mode 100644 |
| index 000000000..43444945a |
| --- /dev/null |
| +++ b/internal/host/plan14_generation_census_kernel_linux_test.go |
| @@ -0,0 +1,41 @@ |
| +//go:build linux |
| + |
| +package host |
| + |
| +import ( |
| + "os" |
| + "path/filepath" |
| + "testing" |
| +) |
| + |
| +func TestPlan14GenerationKernelCensusUnknownReference(t *testing.T) { |
| + for _, attack := range []string{"none", "unreserved-executable", "unreserved-sibling", "uncontained", "namespace"} { |
| + t.Run(attack, func(t *testing.T) { |
| + f := generationProc(t) |
| + tree := filepath.Dir(filepath.Dir(f.reservation.Binding.Tree)) |
| + other := filepath.Join(tree, "unreserved", "other") |
| + if err := os.MkdirAll(filepath.Dir(other), 0700); err != nil { |
| + t.Fatal(err) |
| + } |
| + generationWrite(t, other, "unknown build") |
| + switch attack { |
| + case "unreserved-executable": |
| + generationRelink(t, other, f.task+"/exe") |
| + case "unreserved-sibling": |
| + generationLink(t, other, f.task+"/fd/7") |
| + case "uncontained": |
| + generationWrite(t, f.task+"/status", "Uid:\t1001\t1001\t1001\t1001\nKthread:\t0\n") |
| + case "namespace": |
| + generationRelink(t, "foreign", f.task+"/ns/mnt") |
| + } |
| + boot, digest, err := (generationKernel{proc: f.proc}).census(t.Context(), tree) |
| + if attack == "none" { |
| + if err != nil || boot == "" || len(digest) != 64 { |
| + t.Fatalf("positive: %s %s %v", boot, digest, err) |
| + } |
| + } else if err == nil { |
| + t.Fatal("unknown kernel obligation certified") |
| + } |
| + }) |
| + } |
| +} |
diff --git a/internal/host/plan14_generation_census_root_linux_test.go b/internal/host/plan14_generation_census_root_linux_test.go| new file mode 100644 |
| index 000000000..31a2932e1 |
| --- /dev/null |
| +++ b/internal/host/plan14_generation_census_root_linux_test.go |
| @@ -0,0 +1,81 @@ |
| +//go:build linux |
| + |
| +package host |
| + |
| +import ( |
| + "os" |
| + "os/exec" |
| + "path/filepath" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/rootstate" |
| +) |
| + |
| +func TestPlan14GenerationKernelCensusRootRunner(t *testing.T) { |
| + if dir := os.Getenv("PLAN14_E24_KERNEL_CHILD"); dir != "" { |
| + t.Setenv("CARLOS_OPT_DIR", dir) |
| + path := filepath.Join(dir, "versions", "v1", "app") |
| + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := os.WriteFile(path, []byte("root census reference"), 0644); err != nil { |
| + t.Fatal(err) |
| + } |
| + if boot, digest, err := productionGenerationKernelCensus(t.Context()); err != nil || boot == "" || len(digest) != 64 { |
| + t.Fatalf("quiescent census: %s %s %v", boot, digest, err) |
| + } |
| + file, err := os.Open(path) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if _, _, err := productionGenerationKernelCensus(t.Context()); err == nil { |
| + file.Close() |
| + t.Fatal("open unreserved file certified") |
| + } |
| + if err := os.Remove(path); err != nil { |
| + file.Close() |
| + t.Fatal(err) |
| + } |
| + if _, _, err := productionGenerationKernelCensus(t.Context()); err == nil { |
| + file.Close() |
| + t.Fatal("deleted open file certified") |
| + } |
| + file.Close() |
| + if _, _, err := productionGenerationKernelCensus(t.Context()); err != nil { |
| + t.Fatal("closed file still retained", err) |
| + } |
| + return |
| + } |
| + parent := os.Getenv("PLAN14_GENERATION_INVENTORY_ROOT_RUNNER") |
| + if parent == "" { |
| + t.Skip("requires a designated disposable Linux root runner with unshare and cgroup support") |
| + } |
| + if os.Geteuid() != 0 { |
| + t.Fatal("designated runner must execute as root") |
| + } |
| + state, err := rootstate.Open(parent, false, true) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + state.Close() |
| + dir, err := os.MkdirTemp(parent, "generation-kernel-") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + t.Cleanup(func() { |
| + if err := os.RemoveAll(dir); err != nil { |
| + t.Error(err) |
| + } |
| + }) |
| + if err := os.Chmod(dir, 0755); err != nil { |
| + t.Fatal(err) |
| + } |
| + // A private PID/mount namespace gives the scan a controlled all-root census. |
| + // No generated tenant unit or running-tenant containment is accepted here. |
| + cmd := exec.CommandContext(t.Context(), "unshare", "--mount", "--pid", "--fork", "--mount-proc", os.Args[0], "-test.run=^TestPlan14GenerationKernelCensusRootRunner$", "-test.count=1") |
| + cmd.Dir = dir |
| + cmd.Env = append(os.Environ(), "PLAN14_E24_KERNEL_CHILD="+dir) |
| + if out, err := cmd.CombinedOutput(); err != nil { |
| + t.Fatalf("isolated root census: %v %s", err, out) |
| + } |
| +} |
diff --git a/internal/host/plan14_generation_inventory_process_test.go b/internal/host/plan14_generation_inventory_process_test.go| new file mode 100644 |
| index 000000000..c9ecdb493 |
| --- /dev/null |
| +++ b/internal/host/plan14_generation_inventory_process_test.go |
| @@ -0,0 +1,63 @@ |
| +package host |
| + |
| +import ( |
| + "context" |
| + "encoding/json" |
| + "errors" |
| + "os" |
| + "os/exec" |
| + "path/filepath" |
| + "strings" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/release" |
| + "github.com/carlosframework/platform/internal/rootparticipation" |
| +) |
| + |
| +func TestPlan14GenerationInventoryProcessCrash(t *testing.T) { |
| + if dir := os.Getenv("PLAN14_E24_INVENTORY_CHILD"); dir != "" { |
| + disk := &rollbackDisk{dir: dir} |
| + ledger := &release.RootGenerations{Store: disk} |
| + v := generationReconciler{read: func(context.Context) (generationInventory, error) { |
| + return generationInventory{Census: rootparticipation.Census{Schema: 1, Box: "box", Enrollment: "en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}}, nil |
| + }, kernel: func(context.Context) (string, string, error) { os.Exit(76); return "", "", nil }} |
| + if _, err := ledger.Certify(t.Context(), []release.GenerationReservation{}, v.reconcile); err != nil { |
| + t.Fatal(err) |
| + } |
| + t.Fatal("crash boundary not reached") |
| + } |
| + dir := t.TempDir() |
| + disk := &rollbackDisk{dir: dir} |
| + ledger := &release.RootGenerations{Store: disk} |
| + evidence := release.GenerationEvidence{Box: "box", Enrollment: "en", Migration: 1, Boot: "boot", Installations: strings.Repeat("a", 64), Launches: strings.Repeat("b", 64), Kernel: strings.Repeat("c", 64)} |
| + if _, err := ledger.Certify(t.Context(), []release.GenerationReservation{}, func(context.Context, []release.GenerationReservation) (release.GenerationEvidence, error) { |
| + return evidence, nil |
| + }); err != nil { |
| + t.Fatal(err) |
| + } |
| + cmd := exec.Command(os.Args[0], "-test.run=^TestPlan14GenerationInventoryProcessCrash$", "-test.count=1") |
| + cmd.Dir = dir |
| + cmd.Env = append(os.Environ(), "PLAN14_E24_INVENTORY_CHILD="+dir) |
| + out, err := cmd.CombinedOutput() |
| + var exit *exec.ExitError |
| + if !errors.As(err, &exit) || exit.ExitCode() != 76 { |
| + t.Fatalf("child: %v %s", err, out) |
| + } |
| + c, err := ledger.Completeness() |
| + if err != nil || c.Complete { |
| + t.Fatalf("stale certificate survived crash: %+v %v", c, err) |
| + } |
| + raw, err := os.ReadFile(filepath.Join(dir, "completeness.json")) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + var saved release.GenerationCompleteness |
| + if json.Unmarshal(raw, &saved) != nil || saved.Complete { |
| + t.Fatal("durable invalidation missing") |
| + } |
| + if _, err := ledger.Certify(t.Context(), []release.GenerationReservation{}, func(context.Context, []release.GenerationReservation) (release.GenerationEvidence, error) { |
| + return evidence, nil |
| + }); err != nil { |
| + t.Fatal(err) |
| + } |
| +} |
diff --git a/internal/host/plan14_generation_inventory_root_test.go b/internal/host/plan14_generation_inventory_root_test.go| new file mode 100644 |
| index 000000000..88bca6946 |
| --- /dev/null |
| +++ b/internal/host/plan14_generation_inventory_root_test.go |
| @@ -0,0 +1,122 @@ |
| +//go:build linux |
| + |
| +package host |
| + |
| +import ( |
| + "encoding/json" |
| + "os" |
| + "path/filepath" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/hostbox" |
| + "github.com/carlosframework/platform/internal/rootparticipation" |
| + "github.com/carlosframework/platform/internal/rootstate" |
| +) |
| + |
| +func TestPlan14GenerationInventoryCheckedRootReader(t *testing.T) { |
| + parent := os.Getenv("PLAN14_GENERATION_INVENTORY_ROOT_RUNNER") |
| + if parent == "" { |
| + t.Skip("requires a designated disposable root runner and its publicly traversable scratch parent") |
| + } |
| + if os.Geteuid() != 0 { |
| + t.Fatal("designated runner must execute as root") |
| + } |
| + check, err := rootstate.Open(parent, false, true) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + check.Close() |
| + // Match the production journal ancestry checked by OpenTrustedFile. |
| + dir, err := os.MkdirTemp(parent, "generation-inventory-") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + t.Cleanup(func() { |
| + if err := os.RemoveAll(dir); err != nil { |
| + t.Error(err) |
| + } |
| + }) |
| + if err := os.Chmod(dir, 0755); err != nil { |
| + t.Fatal(err) |
| + } |
| + censusPath, fencesPath, journalPath := filepath.Join(dir, "census"), filepath.Join(dir, "fences"), filepath.Join(dir, "journals.db") |
| + census, err := rootstate.Open(censusPath, true, false) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer census.Close() |
| + fences, err := rootstate.Open(fencesPath, true, false) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + fences.Close() |
| + c := rootparticipation.Census{Schema: 1, Box: "runner", Enrollment: "runner-en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true} |
| + raw, err := json.Marshal(c) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := census.Write("census.json", raw); err != nil { |
| + t.Fatal(err) |
| + } |
| + journal, err := hostbox.OpenLaunchJournal(journalPath) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + journal.Close() |
| + got, err := generationInventoryAt(t.Context(), censusPath, fencesPath, journalPath) |
| + if err != nil || got.Census != c || len(got.Installations) != 0 || len(got.Launches) != 0 { |
| + t.Fatalf("checked snapshot: %+v %v", got, err) |
| + } |
| + for _, attack := range []string{"writable-db", "service-db", "symlink-db", "writable-wal", "service-wal", "missing-db", "missing-census"} { |
| + t.Run(attack, func(t *testing.T) { |
| + switch attack { |
| + case "writable-db": |
| + if err := os.Chmod(journalPath, 0666); err != nil { |
| + t.Fatal(err) |
| + } |
| + defer os.Chmod(journalPath, 0600) |
| + case "service-db": |
| + if err := os.Chown(journalPath, 65534, 65534); err != nil { |
| + t.Fatal(err) |
| + } |
| + defer os.Chown(journalPath, 0, 0) |
| + case "symlink-db": |
| + if err := os.Rename(journalPath, journalPath+".saved"); err != nil { |
| + t.Fatal(err) |
| + } |
| + defer func() { os.Remove(journalPath); os.Rename(journalPath+".saved", journalPath) }() |
| + if err := os.Symlink(journalPath+".saved", journalPath); err != nil { |
| + t.Fatal(err) |
| + } |
| + case "writable-wal", "service-wal": |
| + wal := journalPath + "-wal" |
| + if err := os.WriteFile(wal, []byte("hostile"), 0600); err != nil { |
| + t.Fatal(err) |
| + } |
| + defer os.Remove(wal) |
| + if attack == "writable-wal" { |
| + if err := os.Chmod(wal, 0666); err != nil { |
| + t.Fatal(err) |
| + } |
| + } else { |
| + if err := os.Chown(wal, 65534, 65534); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + case "missing-db": |
| + if err := os.Rename(journalPath, journalPath+".saved"); err != nil { |
| + t.Fatal(err) |
| + } |
| + defer os.Rename(journalPath+".saved", journalPath) |
| + case "missing-census": |
| + if err := census.Remove("census.json"); err != nil { |
| + t.Fatal(err) |
| + } |
| + defer census.Write("census.json", raw) |
| + } |
| + if _, err := generationInventoryAt(t.Context(), censusPath, fencesPath, journalPath); err == nil { |
| + t.Fatal("hostile source accepted") |
| + } |
| + }) |
| + } |
| +} |
diff --git a/internal/host/plan14_generation_inventory_test.go b/internal/host/plan14_generation_inventory_test.go| new file mode 100644 |
| index 000000000..4082afa96 |
| --- /dev/null |
| +++ b/internal/host/plan14_generation_inventory_test.go |
| @@ -0,0 +1,278 @@ |
| +package host |
| + |
| +import ( |
| + "context" |
| + "os" |
| + "path/filepath" |
| + "strings" |
| + "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/release" |
| + "github.com/carlosframework/platform/internal/rootparticipation" |
| + "github.com/carlosframework/platform/internal/rootstate" |
| +) |
| + |
| +func TestPlan14GenerationInventoryCompleteness(t *testing.T) { |
| + for _, attack := range []string{"none", "empty-reservations", "retired-only", "missing-installation", "missing-launch", "extra-launch", "extra-installation", "wrong-controller", "wrong-inputs", "wrong-installation", "wrong-process", "wrong-census", "unknown-migration", "incomplete-census", "kernel", "changed-snapshot"} { |
| + t.Run(attack, func(t *testing.T) { |
| + f, disk, _ := generationLaunchFixture(t) |
| + if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil { |
| + t.Fatal(err) |
| + } |
| + r := readGenerationLaunch(t, disk) |
| + view, err := f.root.Guard.Root.View(t.Context(), f.target.Host, hostguard.Captured{Incarnation: f.target.Incarnation, Stamp: f.target.Stamp}) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + launch, err := f.journal.Read(t.Context(), f.target) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + snapshot := generationInventory{Census: rootparticipation.Census{Schema: 1, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: []hostguard.FenceView{view}, Launches: []hostbox.LaunchInventory{launch}} |
| + records := []release.GenerationReservation{r} |
| + switch attack { |
| + case "empty-reservations": |
| + records = nil |
| + case "retired-only": |
| + records[0].Closed = true |
| + records[0].Retirement = r.Binding.Digest |
| + case "missing-installation": |
| + snapshot.Installations = nil |
| + case "missing-launch": |
| + snapshot.Launches = nil |
| + case "extra-launch": |
| + extra := launch |
| + extra.Target.Host = "other.example" |
| + snapshot.Launches = append(snapshot.Launches, extra) |
| + case "extra-installation": |
| + extra := view |
| + extra.Host = "other.example" |
| + extra.Installation.Host = extra.Host |
| + extra.Installation.Record = provision.RecordKey(extra.Installation.Account, extra.Installation.App, extra.Host) |
| + snapshot.Installations = append(snapshot.Installations, extra) |
| + case "wrong-controller": |
| + snapshot.Launches[0].ControllerPID++ |
| + case "wrong-inputs": |
| + snapshot.Launches[0].Reservations[0].Inputs = "other" |
| + case "wrong-installation": |
| + records[0].Binding.Installation = "other" |
| + case "wrong-process": |
| + records[0].Process.PID++ |
| + case "wrong-census": |
| + snapshot.Census.Enrollment = "other" |
| + case "unknown-migration": |
| + snapshot.Census.Migration = 0 |
| + case "incomplete-census": |
| + snapshot.Census.Complete = false |
| + } |
| + reads, scans := 0, 0 |
| + verifier := generationReconciler{read: func(context.Context) (generationInventory, error) { |
| + reads++ |
| + out := snapshot |
| + if attack == "changed-snapshot" && reads > 1 { |
| + out.Census.Revision++ |
| + } |
| + return out, nil |
| + }, kernel: func(context.Context) (string, string, error) { |
| + scans++ |
| + if attack == "kernel" { |
| + return "", "", os.ErrPermission |
| + } |
| + return "boot-now", r.Binding.Digest, nil |
| + }} |
| + evidence, err := verifier.reconcile(t.Context(), records) |
| + if attack == "none" { |
| + if err != nil || scans != 1 || reads != 2 || evidence.Box != f.target.Box || evidence.Installations == "" || evidence.Launches == "" { |
| + t.Fatalf("positive: %+v %v reads=%d scans=%d", evidence, err, reads, scans) |
| + } |
| + } else if err == nil { |
| + t.Fatalf("%s certified: %+v", attack, evidence) |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestPlan14GenerationInventoryEmptyNeedsIndependentSources(t *testing.T) { |
| + snapshot := generationInventory{Census: rootparticipation.Census{Schema: 1, Box: "box", Enrollment: "en", Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}} |
| + for _, missing := range []string{"none", "inventory", "kernel", "census"} { |
| + t.Run(missing, func(t *testing.T) { |
| + v := generationReconciler{read: func(context.Context) (generationInventory, error) { |
| + if missing == "census" { |
| + return generationInventory{}, nil |
| + } |
| + return snapshot, nil |
| + }, kernel: func(context.Context) (string, string, error) { return "boot", strings.Repeat("a", 64), nil }} |
| + if missing == "inventory" { |
| + v.read = nil |
| + } |
| + if missing == "kernel" { |
| + v.kernel = nil |
| + } |
| + _, err := v.reconcile(t.Context(), nil) |
| + if (err == nil) != (missing == "none") { |
| + t.Fatalf("empty: %v", err) |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestPlan14GenerationInventoryPinConsumer(t *testing.T) { |
| + for _, attack := range []string{"none", "omitted-reservation", "kernel", "lost-census"} { |
| + t.Run(attack, func(t *testing.T) { |
| + f, disk, _ := generationLaunchFixture(t) |
| + if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil { |
| + t.Fatal(err) |
| + } |
| + r := readGenerationLaunch(t, disk) |
| + view, err := f.root.Guard.Root.View(t.Context(), f.target.Host, hostguard.Captured{Incarnation: f.target.Incarnation, Stamp: f.target.Stamp}) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + launch, err := f.journal.Read(t.Context(), f.target) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + reads := 0 |
| + v := generationReconciler{read: func(context.Context) (generationInventory, error) { |
| + reads++ |
| + c := rootparticipation.Census{Schema: 1, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true} |
| + if attack == "lost-census" { |
| + c.Complete = false |
| + } |
| + return generationInventory{Census: c, Installations: []hostguard.FenceView{view}, Launches: []hostbox.LaunchInventory{launch}}, nil |
| + }, kernel: func(context.Context) (string, string, error) { |
| + if attack == "kernel" { |
| + return "", "", os.ErrPermission |
| + } |
| + return "boot", r.Binding.Digest, nil |
| + }} |
| + if attack == "omitted-reservation" { |
| + names, err := disk.Names() |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + for _, name := range names { |
| + if err := disk.Remove(name); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + } |
| + s := generationPinSource{open: func() (rootstate.Store, error) { return disk, nil }, reconcile: v.reconcile} |
| + pins, err := s.StagedPins(t.Context(), f.root.generations.optDir) |
| + if attack != "none" { |
| + if err == nil { |
| + t.Fatalf("unsafe snapshot: %+v", pins) |
| + } |
| + return |
| + } |
| + if err != nil || reads != 2 { |
| + t.Fatalf("pins: %v reads=%d", err, reads) |
| + } |
| + if pinned, err := pins.Refers(r.Binding.Path); err != nil || !pinned { |
| + t.Fatalf("missing exact pin: %v", err) |
| + } |
| + c, err := (&release.RootGenerations{Store: disk}).Completeness() |
| + if err != nil || !c.Complete { |
| + t.Fatalf("certificate: %+v %v", c, err) |
| + } |
| + }) |
| + } |
| + s, ok := productionRootGenerationPins().(generationPinSource) |
| + if !ok || s.reconcile == nil { |
| + t.Fatal("production reconciliation not connected") |
| + } |
| +} |
| + |
| +func TestPlan14GenerationInventoryLifecycle(t *testing.T) { |
| + for _, mode := range []string{"pending", "pending-wrong-controller", "retired", "retained-rebind", "unrecognized-predecessor"} { |
| + t.Run(mode, func(t *testing.T) { |
| + f, disk, _ := generationLaunchFixture(t) |
| + prior, err := f.journal.Read(t.Context(), f.target) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + journal, err := hostbox.OpenLaunchJournal(filepath.Join(t.TempDir(), "launch.db")) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + defer journal.Close() |
| + if err := journal.Prelaunch(t.Context(), prior); err != nil { |
| + t.Fatal(err) |
| + } |
| + f.root.Journal, f.journal = journal, journal |
| + pending := strings.HasPrefix(mode, "pending") |
| + if pending { |
| + if _, err := f.root.generations.reserve(t.Context(), f.root, f.peer, f.req); err != nil { |
| + t.Fatal(err) |
| + } |
| + } else { |
| + if _, err := f.root.Serve(t.Context(), f.peer, f.req); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := f.root.stop(t.Context(), f.peer, *f.req.Reservation); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := journal.CloseWriters(t.Context(), f.target); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + r := readGenerationLaunch(t, disk) |
| + if mode == "retired" { |
| + v := generationRetirement{guard: f.root.Guard, journal: journal, kernel: func(context.Context, release.GenerationReservation) (string, error) { return r.Binding.Digest, nil }} |
| + if err := v.retire(t.Context(), &release.RootGenerations{Store: disk}, r); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + if mode == "retained-rebind" || mode == "unrecognized-predecessor" { |
| + captured := hostguard.Captured{Incarnation: f.target.Incarnation, Stamp: f.target.Stamp} |
| + in, err := f.root.Guard.Root.Installation(t.Context(), f.target.Host, captured) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + next := in |
| + next.Stamp += 3 |
| + next.InventoryRevision++ |
| + f.claims.set(func(c *hostclaim.Claim) { c.Holder.Stamp = next.Stamp }) |
| + if err := f.root.Guard.RebindRootInventory(t.Context(), captured, in.InventoryRevision, next, []byte("verified-rebind"), func(*hostclaim.Claim) error { return nil }, func() error { return nil }); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + v := generationReconciler{read: func(ctx context.Context) (generationInventory, error) { |
| + views, err := f.root.Guard.Root.GenerationInstallations(ctx) |
| + if err != nil { |
| + return generationInventory{}, err |
| + } |
| + launches, err := journal.GenerationLaunches(ctx) |
| + if err != nil { |
| + return generationInventory{}, err |
| + } |
| + if mode == "unrecognized-predecessor" { |
| + views[0].Retired = nil |
| + } |
| + if mode == "pending-wrong-controller" { |
| + launches[0].ControllerPID++ |
| + } |
| + return generationInventory{Census: rootparticipation.Census{Schema: 1, Box: f.target.Box, Enrollment: f.target.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true}, Installations: views, Launches: launches}, nil |
| + }, kernel: func(context.Context) (string, string, error) { return "boot", r.Binding.Digest, nil }} |
| + source := generationPinSource{open: func() (rootstate.Store, error) { return disk, nil }, reconcile: v.reconcile} |
| + pins, err := source.StagedPins(t.Context(), f.root.generations.optDir) |
| + if mode == "pending-wrong-controller" || mode == "unrecognized-predecessor" { |
| + if err == nil { |
| + t.Fatal("unbound generation certified") |
| + } |
| + return |
| + } |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + pinned, err := pins.Refers(r.Binding.Path) |
| + if err != nil || pinned != (mode != "retired") { |
| + t.Fatalf("exact lifecycle pin=%v: %v", pinned, err) |
| + } |
| + }) |
| + } |
| +} |
diff --git a/internal/host/rootgeneration_census_linux.go b/internal/host/rootgeneration_census_linux.go| new file mode 100644 |
| index 000000000..b7c892ba5 |
| --- /dev/null |
| +++ b/internal/host/rootgeneration_census_linux.go |
| @@ -0,0 +1,84 @@ |
| +//go:build linux |
| + |
| +package host |
| + |
| +import ( |
| + "context" |
| + "io/fs" |
| + "os" |
| + "path/filepath" |
| + |
| + "github.com/carlosframework/platform/internal/release" |
| + "github.com/carlosframework/platform/internal/rootstate" |
| + "golang.org/x/sys/unix" |
| +) |
| + |
| +// census checks the whole staged namespace, including unreserved builds. This |
| +// quiescent verifier refuses uncontained service tasks and foreign namespaces. |
| +// Supporting running tenants requires independent containment evidence first. |
| +func (k generationKernel) census(ctx context.Context, tree string) (string, string, error) { |
| + before, err := k.boot() |
| + if err != nil { |
| + return "", "", err |
| + } |
| + f, err := os.Open(tree) |
| + if err != nil { |
| + return "", "", err |
| + } |
| + id, err := rootstate.Identity(f) |
| + f.Close() |
| + if err != nil { |
| + return "", "", err |
| + } |
| + r := release.GenerationReservation{Binding: release.GenerationBinding{Tree: tree, Path: tree, TreeIdentity: id, Executable: id}} |
| + digest, err := k.absent(ctx, r) |
| + if err != nil { |
| + return "", "", err |
| + } |
| + after, err := k.boot() |
| + if err != nil || after != before { |
| + return "", "", os.ErrPermission |
| + } |
| + return before, digest, nil |
| +} |
| +func checkedGenerationTree(ctx context.Context, tree string) error { |
| + state, err := rootstate.Open(tree, false, true) |
| + if err != nil { |
| + return err |
| + } |
| + defer state.Close() |
| + count := 0 |
| + return filepath.WalkDir(tree, func(path string, entry fs.DirEntry, walkErr error) error { |
| + if walkErr != nil { |
| + return walkErr |
| + } |
| + if err := ctx.Err(); err != nil { |
| + return err |
| + } |
| + count++ |
| + if count > 8192 { |
| + return os.ErrPermission |
| + } |
| + var st unix.Stat_t |
| + if err := unix.Lstat(path, &st); err != nil { |
| + return err |
| + } |
| + if st.Uid != 0 || st.Mode&0022 != 0 || (st.Mode&unix.S_IFMT != unix.S_IFREG && st.Mode&unix.S_IFMT != unix.S_IFDIR) { |
| + return os.ErrPermission |
| + } |
| + return nil |
| + }) |
| +} |
| +func productionGenerationKernelCensus(ctx context.Context) (string, string, error) { |
| + if os.Geteuid() != 0 { |
| + return "", "", os.ErrPermission |
| + } |
| + if err := verifyStopKernelMounts("/sys/fs/cgroup", "/proc"); err != nil { |
| + return "", "", err |
| + } |
| + tree := filepath.Join(release.OptDir(), "versions") |
| + if err := checkedGenerationTree(ctx, tree); err != nil { |
| + return "", "", err |
| + } |
| + return (generationKernel{proc: "/proc"}).census(ctx, tree) |
| +} |
diff --git a/internal/host/rootgeneration_census_other.go b/internal/host/rootgeneration_census_other.go| new file mode 100644 |
| index 000000000..ea0ead216 |
| --- /dev/null |
| +++ b/internal/host/rootgeneration_census_other.go |
| @@ -0,0 +1,12 @@ |
| +//go:build !linux |
| + |
| +package host |
| + |
| +import ( |
| + "context" |
| + "os" |
| +) |
| + |
| +func productionGenerationKernelCensus(context.Context) (string, string, error) { |
| + return "", "", os.ErrPermission |
| +} |
diff --git a/internal/host/rootgeneration_inventory.go b/internal/host/rootgeneration_inventory.go| new file mode 100644 |
| index 000000000..b41cbe314 |
| --- /dev/null |
| +++ b/internal/host/rootgeneration_inventory.go |
| @@ -0,0 +1,222 @@ |
| +package host |
| + |
| +import ( |
| + "context" |
| + "crypto/sha256" |
| + "encoding/hex" |
| + "encoding/json" |
| + "errors" |
| + "os" |
| + |
| + "github.com/carlosframework/platform/internal/hostbox" |
| + "github.com/carlosframework/platform/internal/hostguard" |
| + "github.com/carlosframework/platform/internal/instancectl" |
| + "github.com/carlosframework/platform/internal/provision" |
| + "github.com/carlosframework/platform/internal/release" |
| + "github.com/carlosframework/platform/internal/rootparticipation" |
| + "github.com/carlosframework/platform/internal/rootstate" |
| +) |
| + |
| +type generationInventory struct { |
| + Census rootparticipation.Census |
| + Installations []hostguard.FenceView |
| + Launches []hostbox.LaunchInventory |
| +} |
| +type generationReconciler struct { |
| + read func(context.Context) (generationInventory, error) |
| + kernel func(context.Context) (string, string, error) |
| +} |
| + |
| +func generationInventoryDigest(v any) string { |
| + raw, _ := json.Marshal(v) |
| + sum := sha256.Sum256(raw) |
| + return hex.EncodeToString(sum[:]) |
| +} |
| +func generationOwner(t hostguard.FenceTarget) release.GenerationOwner { |
| + return release.GenerationOwner{Host: t.Host, Incarnation: t.Incarnation, Stamp: t.Stamp, Session: t.Session, Box: t.Box, Enrollment: t.Enrollment, BootID: t.BootID, ControllerID: t.ControllerID} |
| +} |
| +func generationInstallation(v hostguard.FenceView, t release.GenerationOwner) (hostguard.RootInstallation, error) { |
| + in := v.Installation |
| + known := in.Incarnation == t.Incarnation && in.Stamp == t.Stamp |
| + for _, old := range v.Retired { |
| + if old.Incarnation == t.Incarnation && old.Stamp == t.Stamp { |
| + known = true |
| + } |
| + } |
| + if !known || in.Host != t.Host || in.Incarnation != t.Incarnation || in.Stamp < t.Stamp || in.Box != t.Box || in.Enrollment != t.Enrollment { |
| + return in, os.ErrPermission |
| + } |
| + in.Stamp = t.Stamp |
| + return in, nil |
| +} |
| + |
| +// Callers hold release exclusion through use. Recheck independent sources after |
| +// kernel inspection; a certificate never substitutes for the next inspection. |
| +func (v generationReconciler) reconcile(ctx context.Context, records []release.GenerationReservation) (release.GenerationEvidence, error) { |
| + var zero release.GenerationEvidence |
| + if v.read == nil || v.kernel == nil { |
| + return zero, os.ErrPermission |
| + } |
| + before, err := v.read(ctx) |
| + if err != nil { |
| + return zero, err |
| + } |
| + c := before.Census |
| + if c.Schema != 1 || !c.Complete || c.NeverEnrolled || c.Pending != "" || c.Migration == 0 || c.Revision == 0 || c.LaunchRevision == 0 || c.Box == "" || c.Enrollment == "" { |
| + return zero, os.ErrPermission |
| + } |
| + installs := map[string]hostguard.FenceView{} |
| + for _, view := range before.Installations { |
| + in := view.Installation |
| + if _, exists := installs[in.Host]; exists || view.Schema != 1 || view.Host != in.Host || in.Box != c.Box || in.Enrollment != c.Enrollment || in.Migration != c.Migration || in.InventoryRevision == 0 || in.Record != provision.RecordKey(in.Account, in.App, in.Host) { |
| + return zero, os.ErrPermission |
| + } |
| + installs[in.Host] = view |
| + } |
| + matched := make([]bool, len(records)) |
| + hosts := map[string]bool{} |
| + for _, launch := range before.Launches { |
| + t := generationOwner(launch.Target) |
| + if _, err := generationInstallation(installs[t.Host], t); err != nil { |
| + return zero, err |
| + } |
| + hosts[t.Host] = true |
| + seen := map[string]bool{} |
| + for _, unit := range launch.Reservations { |
| + if seen[unit.ID] || unit.ID == "" || !hostbox.SameLaunchOwner(unit.Target, launch.Target) { |
| + return zero, os.ErrPermission |
| + } |
| + seen[unit.ID] = true |
| + if !hostbox.IsUnitReservation(unit) { |
| + if !launch.WritersClosed || unit.State != "settled" { |
| + return zero, os.ErrPermission |
| + } |
| + continue |
| + } |
| + count := 0 |
| + for i, r := range records { |
| + b := r.Binding |
| + if b.Target != t || b.Attempt != unit.ID { |
| + continue |
| + } |
| + in, err := generationInstallation(installs[t.Host], t) |
| + if err != nil { |
| + return zero, err |
| + } |
| + if matched[i] || b.Binding.Host != in.Host || b.Binding.Account != in.Account || b.Binding.App != in.App || b.Record != in.Record || b.Installation != rootHistoryInstallation(in) || b.ControllerPID != launch.ControllerPID || b.ControllerStart != launch.ProcessStart || b.Inputs != unit.Inputs || b.Invocation != unit.InvocationID || b.Executable.Device != unit.Executable.Device || b.Executable.Inode != unit.Executable.Inode || unit.Group.RelativePath != instancectl.ClaimUnitControlGroup(t.Host) { |
| + return zero, os.ErrPermission |
| + } |
| + if unit.State != hostbox.UnitIntended && unit.State != hostbox.UnitStarted && unit.State != hostbox.UnitSettled { |
| + return zero, os.ErrPermission |
| + } |
| + p := r.Process |
| + if p != (release.GenerationProcess{}) && (p.PID != unit.PID || p.Start != unit.ProcessStart || p.Group != unit.Group.RelativePath || p.Invocation != unit.Group.InvocationID || p.GroupIdentity.Device != unit.Group.Device || p.GroupIdentity.Inode != unit.Group.Inode) { |
| + return zero, os.ErrPermission |
| + } |
| + if r.Closed && (!launch.WritersClosed || unit.State != hostbox.UnitSettled) { |
| + return zero, os.ErrPermission |
| + } |
| + matched[i] = true |
| + count++ |
| + } |
| + // A closed legacy launch can predate E2, but kernel inspection must still |
| + // exclude its references. An open launch always needs its exact root pin. |
| + if count != 1 && !(count == 0 && launch.WritersClosed && unit.State == hostbox.UnitSettled) { |
| + return zero, os.ErrPermission |
| + } |
| + } |
| + } |
| + for _, view := range before.Installations { |
| + if !view.Installation.Empty && !hosts[view.Host] { |
| + return zero, os.ErrPermission |
| + } |
| + } |
| + for i, r := range records { |
| + if matched[i] { |
| + continue |
| + } |
| + // Reserve precedes the launch intention. This crash window stays pinned; |
| + // it cannot become a retired or observed generation without its journal. |
| + in, err := generationInstallation(installs[r.Binding.Target.Host], r.Binding.Target) |
| + if err != nil || r.Closed || r.Process != (release.GenerationProcess{}) || r.Binding.Installation != rootHistoryInstallation(in) || r.Binding.Binding.Account != in.Account || r.Binding.Binding.App != in.App || r.Binding.Record != in.Record || !hosts[in.Host] { |
| + return zero, os.ErrPermission |
| + } |
| + pending := 0 |
| + for _, launch := range before.Launches { |
| + if generationOwner(launch.Target) == r.Binding.Target && launch.ControllerPID == r.Binding.ControllerPID && launch.ProcessStart == r.Binding.ControllerStart { |
| + pending++ |
| + } |
| + } |
| + if pending != 1 { |
| + return zero, os.ErrPermission |
| + } |
| + } |
| + snapshotDigest := generationInventoryDigest(before) |
| + boot, kernel, err := v.kernel(ctx) |
| + if err != nil { |
| + return zero, err |
| + } |
| + decoded, err := hex.DecodeString(kernel) |
| + if err != nil || len(decoded) != sha256.Size || hex.EncodeToString(decoded) != kernel || boot == "" { |
| + return zero, os.ErrPermission |
| + } |
| + after, err := v.read(ctx) |
| + if err != nil { |
| + return zero, err |
| + } |
| + if err := ctx.Err(); err != nil { |
| + return zero, err |
| + } |
| + if snapshotDigest != generationInventoryDigest(after) { |
| + return zero, os.ErrPermission |
| + } |
| + return release.GenerationEvidence{Box: c.Box, Enrollment: c.Enrollment, Migration: c.Migration, Boot: boot, Installations: generationInventoryDigest(before.Installations), Launches: generationInventoryDigest([]any{c.LaunchRevision, before.Launches}), Kernel: kernel}, nil |
| +} |
| + |
| +func productionGenerationInventory(ctx context.Context) (generationInventory, error) { |
| + return generationInventoryAt(ctx, rootparticipation.Namespace, "/var/lib/carlos-claims/authority/fences", "/var/lib/carlos-claims/journals.db") |
| +} |
| + |
| +func generationInventoryAt(ctx context.Context, censusPath, fencesPath, journalPath string) (generationInventory, error) { |
| + var out generationInventory |
| + censusState, err := rootstate.Open(censusPath, false, false) |
| + census := rootparticipation.Ledger{State: censusState} |
| + if err != nil { |
| + return out, err |
| + } |
| + defer census.State.Close() |
| + out.Census, err = census.Census() |
| + if err != nil { |
| + return out, err |
| + } |
| + state, err := rootstate.Open(fencesPath, false, false) |
| + if err != nil { |
| + return out, err |
| + } |
| + defer state.Close() |
| + out.Installations, err = hostguard.NewRootAuthority(state).GenerationInstallations(ctx) |
| + if err != nil { |
| + return out, err |
| + } |
| + journal, err := openGenerationLaunchInventory(journalPath) |
| + if err != nil { |
| + return out, err |
| + } |
| + defer journal.Close() |
| + out.Launches, err = journal.GenerationLaunches(ctx) |
| + return out, err |
| +} |
| + |
| +func openGenerationLaunchInventory(path string) (*hostbox.LaunchJournal, error) { |
| + for _, suffix := range []string{"", "-wal", "-shm"} { |
| + file, err := rootstate.OpenTrustedFile(path + suffix) |
| + if suffix != "" && errors.Is(err, os.ErrNotExist) { |
| + continue |
| + } |
| + if err != nil { |
| + return nil, err |
| + } |
| + file.Close() |
| + } |
| + return hostbox.OpenGenerationLaunchReader(path) |
| +} |
diff --git a/internal/host/rootgenerationpins.go b/internal/host/rootgenerationpins.go| index 196dc3f29..31ba8526a 100644 |
| --- a/internal/host/rootgenerationpins.go |
| +++ b/internal/host/rootgenerationpins.go |
| @@ -9,18 +9,18 @@ import ( |
| ) |
| type generationPinSource struct { |
| - legacy func() (bool, error) |
| - open func() (rootstate.Store, error) |
| - verify func(context.Context, []release.GenerationReservation) error |
| - retire func(context.Context, *release.RootGenerations) error |
| + legacy func() (bool, error) |
| + open func() (rootstate.Store, error) |
| + verify func(context.Context, []release.GenerationReservation) error |
| + reconcile func(context.Context, []release.GenerationReservation) (release.GenerationEvidence, error) |
| + retire func(context.Context, *release.RootGenerations) error |
| } |
| func productionRootGenerationPins() release.PinSource { |
| s := generationPinSource{legacy: legacyRootRestart, retire: productionRetireGenerations, open: func() (rootstate.Store, error) { |
| return rootstate.Open(release.RootGenerationsDir, false, false) |
| }} |
| - // Until root reconciles installation, launch and kernel inventory, Verify |
| - // stays absent and destructive writes on census boxes remain unavailable. |
| + s.reconcile = (generationReconciler{read: productionGenerationInventory, kernel: productionGenerationKernelCensus}).reconcile |
| return s |
| } |
| @@ -51,5 +51,5 @@ func (s generationPinSource) StagedPins(ctx context.Context, optDir string) (rel |
| return nil, err |
| } |
| } |
| - return (release.RootGenerationPins{Ledger: ledger, Verify: s.verify}).StagedPins(ctx, optDir) |
| + return (release.RootGenerationPins{Ledger: ledger, Verify: s.verify, Reconcile: s.reconcile}).StagedPins(ctx, optDir) |
| } |
diff --git a/internal/hostbox/generationinventory.go b/internal/hostbox/generationinventory.go| new file mode 100644 |
| index 000000000..2b86e62d2 |
| --- /dev/null |
| +++ b/internal/hostbox/generationinventory.go |
| @@ -0,0 +1,74 @@ |
| +package hostbox |
| + |
| +import ( |
| + "bytes" |
| + "context" |
| + "database/sql" |
| + "encoding/json" |
| + "io" |
| + "net/url" |
| + "os" |
| + "path/filepath" |
| +) |
| + |
| +// GenerationLaunches includes hosts absent from the reservation directory. |
| +// Only the checked local journal is eligible; an agent list is not a census. |
| +func (j *LaunchJournal) GenerationLaunches(ctx context.Context) ([]LaunchInventory, error) { |
| + if j == nil || j.db == nil || j.remote != nil { |
| + return nil, ErrUnsupported |
| + } |
| + if err := journalIdentity(j.path, j.identity); err != nil { |
| + return nil, err |
| + } |
| + rows, err := journalExecutorFor(ctx, j.db).QueryContext(ctx, "SELECT identity, body FROM launches ORDER BY identity") |
| + if err != nil { |
| + return nil, err |
| + } |
| + defer rows.Close() |
| + var out []LaunchInventory |
| + for rows.Next() { |
| + var key string |
| + var body []byte |
| + if err := rows.Scan(&key, &body); err != nil { |
| + return nil, err |
| + } |
| + if len(body) > 1<<20 || len(out) >= 8192 { |
| + return nil, ErrTypedInventory |
| + } |
| + var in LaunchInventory |
| + dec := json.NewDecoder(bytes.NewReader(body)) |
| + dec.DisallowUnknownFields() |
| + if dec.Decode(&in) != nil || dec.Decode(new(any)) != io.EOF || !validLaunchOwner(in.Target) || launchKey(in.Target) != key || !in.Completed.Valid() || in.ControllerPID <= 0 || in.ProcessStart == "" { |
| + return nil, ErrTypedInventory |
| + } |
| + out = append(out, in) |
| + } |
| + if err := rows.Err(); err != nil { |
| + return nil, err |
| + } |
| + if err := journalIdentity(j.path, j.identity); err != nil { |
| + return nil, err |
| + } |
| + return out, nil |
| +} |
| + |
| +// OpenGenerationLaunchReader never creates a database or initializes its schema. |
| +// Production callers must check root ownership of the database and sidecars. |
| +func OpenGenerationLaunchReader(path string) (*LaunchJournal, error) { |
| + if !filepath.IsAbs(path) { |
| + return nil, ErrUnsupported |
| + } |
| + if err := checkJournalPath(path); err != nil { |
| + return nil, err |
| + } |
| + identity, err := os.Lstat(path) |
| + if err != nil { |
| + return nil, err |
| + } |
| + db, err := sql.Open("sqlite", "file:"+url.PathEscape(path)+"?mode=ro&_pragma=busy_timeout(5000)") |
| + if err != nil { |
| + return nil, err |
| + } |
| + db.SetMaxOpenConns(1) |
| + return &LaunchJournal{db: db, path: path, identity: identity}, nil |
| +} |
diff --git a/internal/hostbox/plan14_generation_inventory_test.go b/internal/hostbox/plan14_generation_inventory_test.go| new file mode 100644 |
| index 000000000..4ccd5c490 |
| --- /dev/null |
| +++ b/internal/hostbox/plan14_generation_inventory_test.go |
| @@ -0,0 +1,73 @@ |
| +package hostbox |
| + |
| +import ( |
| + "os" |
| + "path/filepath" |
| + "testing" |
| +) |
| + |
| +func TestPlan14GenerationLaunchInventory(t *testing.T) { |
| + for _, attack := range []string{"none", "unknown-field", "wrong-key", "oversize", "replaced-path", "missing-table"} { |
| + t.Run(attack, func(t *testing.T) { |
| + j, r := roleLaunchFixture(t) |
| + if err := j.IntendUnit(t.Context(), r); err != nil { |
| + t.Fatal(err) |
| + } |
| + switch attack { |
| + case "unknown-field": |
| + _, err := j.db.Exec("UPDATE launches SET body=json_set(body,'$.unknown',true)") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + case "wrong-key": |
| + _, err := j.db.Exec("UPDATE launches SET identity='other'") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + case "oversize": |
| + _, err := j.db.Exec("UPDATE launches SET body=zeroblob(1048577)") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + case "replaced-path": |
| + if err := os.Rename(j.path, j.path+".old"); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := os.WriteFile(j.path, []byte("replacement"), 0600); err != nil { |
| + t.Fatal(err) |
| + } |
| + case "missing-table": |
| + _, err := j.db.Exec("DROP TABLE launches") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + got, err := j.GenerationLaunches(t.Context()) |
| + if attack == "none" { |
| + if err != nil || len(got) != 1 || len(got[0].Reservations) != 1 { |
| + t.Fatalf("inventory: %+v %v", got, err) |
| + } |
| + } else if err == nil { |
| + t.Fatal("incomplete inventory accepted") |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestPlan14GenerationReaderDoesNotInitialize(t *testing.T) { |
| + path := filepath.Join(t.TempDir(), "empty.db") |
| + if err := os.WriteFile(path, nil, 0600); err != nil { |
| + t.Fatal(err) |
| + } |
| + j, err := OpenGenerationLaunchReader(path) |
| + if err != nil { |
| + return |
| + } |
| + defer j.Close() |
| + if _, err := j.GenerationLaunches(t.Context()); err == nil { |
| + t.Fatal("missing schema was initialized as complete") |
| + } |
| + if info, err := os.Stat(path); err != nil || info.Size() != 0 { |
| + t.Fatalf("reader changed empty database: %v %v", info, err) |
| + } |
| +} |
diff --git a/internal/hostguard/generationinventory.go b/internal/hostguard/generationinventory.go| new file mode 100644 |
| index 000000000..0851eb378 |
| --- /dev/null |
| +++ b/internal/hostguard/generationinventory.go |
| @@ -0,0 +1,46 @@ |
| +package hostguard |
| + |
| +import ( |
| + "context" |
| + "sort" |
| + "strings" |
| +) |
| + |
| +// GenerationInstallations enumerates independently of generation reservations. |
| +// Pending or quarantined installations cannot certify a destructive snapshot. |
| +func (a *RootAuthority) GenerationInstallations(ctx context.Context) ([]FenceView, error) { |
| + if a == nil || a.store == nil { |
| + return nil, ErrInvalidInput |
| + } |
| + names, err := a.store.Names() |
| + if err != nil { |
| + return nil, err |
| + } |
| + sort.Strings(names) |
| + var out []FenceView |
| + for _, name := range names { |
| + if err := ctx.Err(); err != nil { |
| + return nil, err |
| + } |
| + if strings.HasSuffix(name, ".lock") { |
| + continue |
| + } |
| + if !strings.HasSuffix(name, ".json") { |
| + 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}) |
| + if err != nil { |
| + return nil, err |
| + } |
| + if view.Installation.InventoryRevision == 0 { |
| + return nil, ErrNoRecord |
| + } |
| + out = append(out, view) |
| + } |
| + return out, nil |
| +} |
diff --git a/internal/hostguard/plan14_generation_inventory_test.go b/internal/hostguard/plan14_generation_inventory_test.go| new file mode 100644 |
| index 000000000..75c919776 |
| --- /dev/null |
| +++ b/internal/hostguard/plan14_generation_inventory_test.go |
| @@ -0,0 +1,55 @@ |
| +//go:build unix |
| + |
| +package hostguard |
| + |
| +import "testing" |
| + |
| +func TestPlan14GenerationInstallationInventory(t *testing.T) { |
| + for _, attack := range []string{"none", "unknown-file", "malformed", "uninitialized", "quarantined", "pending"} { |
| + t.Run(attack, func(t *testing.T) { |
| + f, target, captured := fenceFixture(t) |
| + store := &rootTestStore{dir: t.TempDir()} |
| + f.g.Root = NewRootAuthority(store) |
| + f.obs.Claim.Holder.Account, f.obs.Claim.Holder.App = "bab", "console" |
| + in := RootInstallation{Host: target.Host, Incarnation: captured.Incarnation, Stamp: captured.Stamp, Account: "bab", App: "console", Record: "record", Box: target.Box, Enrollment: target.Enrollment, Migration: 1, InventoryRevision: 1} |
| + if err := f.g.EstablishRoot(t.Context(), in, allow); err != nil { |
| + t.Fatal(err) |
| + } |
| + switch attack { |
| + case "unknown-file": |
| + if err := store.Write("unknown", nil); err != nil { |
| + t.Fatal(err) |
| + } |
| + case "malformed": |
| + if err := store.Write(target.Host+".json", []byte("{}")); err != nil { |
| + t.Fatal(err) |
| + } |
| + case "uninitialized", "quarantined", "pending": |
| + r, err := f.g.Root.read(target.Host) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + switch attack { |
| + case "uninitialized": |
| + r.installation.InventoryRevision = 0 |
| + case "quarantined": |
| + r.quarantined = true |
| + case "pending": |
| + r.pendingInstallation = &in |
| + r.pendingInventory = []byte("pending") |
| + } |
| + if err := f.g.Root.write(r); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| + got, err := f.g.Root.GenerationInstallations(t.Context()) |
| + if attack == "none" { |
| + if err != nil || len(got) != 1 || got[0].Installation != in { |
| + t.Fatalf("installations: %+v %v", got, err) |
| + } |
| + } else if err == nil { |
| + t.Fatal("incomplete installation certified") |
| + } |
| + }) |
| + } |
| +} |
diff --git a/internal/provision/plan9_b8_census_test.go b/internal/provision/plan9_b8_census_test.go| index 5d55256e0..82a379ccb 100644 |
| --- a/internal/provision/plan9_b8_census_test.go |
| +++ b/internal/provision/plan9_b8_census_test.go |
| @@ -473,6 +473,7 @@ func Reviewed(ctx context.Context, s Store, k string) error { |
| // BEGIN REVIEWED INVENTORY |
| var plan9B8CensusInventory = []censusEntry{ |
| + {"internal/host/rootgeneration_inventory.go", "generationReconciler.reconcile", "key-site", "key:RecordKey", "35fba4c4b286b530288f83d150f93a49980cb5a587bdea1b69c4f71a90442fbf"}, |
| {"internal/host/rootgenerations_launch.go", "rootGenerationSource.resolve", "key-site", "key:RecordKey", "1d66e9ffa6aca3ea4a2d370c04f782b01c8a15d9ae6ca93a9356632d2b032977"}, |
| {"internal/host/rootinventory.go", "ClaimRootHTTP.recoverRootInventory", "key-site", "key:RecordKey", "10d09b5ab6d07d316437233c9a65bf0bcdfe1083db4d0769fa8303b255dfd0ac"}, |
| {"internal/host/proofnamed.go", "proofBindingSource.record", "key-site", "key:RecordKey", "10a99ff4b355907831a904010b7019d480ce93f2f595e99e076d3c702d10ca2b"}, |
diff --git a/internal/provision/testdata/plan14-root-reads-e.tsv b/internal/provision/testdata/plan14-root-reads-e.tsv| index 59ad98868..acb6645b3 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 8e78fc36197a3e57ec73d46b824b93a87c4a5031a6f0fd11d6e86f86d188260d |
| +@coverage internal/host 7a0f83f08620422a955ded1e353b5dd4e77aeaf0f07580c7bff85d7393923de2 |
| @coverage internal/release 2bb546a86c1ab49b02e424eb0b366b46776857a1249baa2d6fb61ead4209d6bd |
| @coverage internal/rootstate 6338f74a6b2059cf77cf478bd6111b37b441eb9d65a309464c378359d4bdac05 |
| internal/release/roothistory_status.go RootHistory.MigrationStatus authority E1 Exact completion binds the accepted authorization digest from durable private history to enrolled-root signed readback. Pending originals, changed evidence and canonical sources cannot supply retirement completion. Fixtures: TestPlan14HistoryReconstructionCompletion, TestPlan14HistoryRetirementCompletion, TestPlan14ReadReconstructionCompletion, TestPlan14ReadRetirementCompletion, TestPlan14RecoveryRetirementProductionCallback, TestPlan14RootHistoryReconstructionCrash, TestPlan14HistoryRetirementProcessCrash. file:Read@1062,file:Read@3891 f0ab7ad2d2d70f8c22ebfd575f5a1c6931f7eba30ccdba80ad43d0efffc88154 |
| @@ -211,9 +211,9 @@ internal/release/rootgenerations.go type GenerationProcess authority E2 E18: cla |
| internal/release/rootgenerations.go type GenerationReservation 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. 1917db4836b3b18b45ce6bca1a55f6d5a8082e5de26af1710d0da25db0809551 |
| internal/release/rootgenerations.go type RootGenerations authority E2 E18: claims-on launch records the canonical installation, deployment revision, signed build, executable and containing tree before start under release exclusion; both final spawn checks reread the same reservation and identities. Root process evidence never retires the reservation. This is the launch prerequisite only; complete pins and retirement remain pending. Fixtures: TestPlan14GenerationReservedBeforeClaimSpawn, TestPlan14GenerationLaunchRefusals, TestPlan14GenerationStartedSyncFailureRetainsPin, TestPlan14GenerationReservationProcessCrash, TestPlan14GenerationProductionWiring, TestPlan14GenerationReservationDurability, TestPlan14GenerationReservationConflicts, TestPlan14GenerationObservationKeepsReservation, TestPlan14GenerationRecordRefusals. 240860014efc91f51ca7e3e920ffb5cfa98076340dc0c9e9972c35051e5eca84 |
| internal/release/rootgenerations.go validGeneration 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. 25b4b51c18290f5e169e4c8225710cf3d27078b7a89101ab069bc7a838e75335 |
| -internal/host/rootgenerationpins.go generationPinSource.StagedPins authority E2 E21: production census-only pin reader calls exact retirement before the unchanged mandatory completeness verifier. No census performs no generation open or retirement. Fixtures: TestPlan14GenerationRetirementCensusGate, TestPlan14GenerationProductionModeGate, TestPlan14GenerationClaimsOffRestageMatchesMain. 25c90cc73e198e2a05c6e8069d35ddfbc72a5ac43ed6f5b90673bf015dce1800 |
| -internal/host/rootgenerationpins.go productionRootGenerationPins authority E2 E21: production census-only pin reader calls exact retirement before the unchanged mandatory completeness verifier. No census performs no generation open or retirement. Fixtures: TestPlan14GenerationRetirementCensusGate, TestPlan14GenerationProductionModeGate, TestPlan14GenerationClaimsOffRestageMatchesMain. file:Open@195 8d0a7b9fc2bfd14808ec6ab8f52751fc563b4b1364f58ccf59f48ed1348b25f2 |
| -internal/host/rootgenerationpins.go type generationPinSource authority E2 E21: production census-only pin reader calls exact retirement before the unchanged mandatory completeness verifier. No census performs no generation open or retirement. Fixtures: TestPlan14GenerationRetirementCensusGate, TestPlan14GenerationProductionModeGate, TestPlan14GenerationClaimsOffRestageMatchesMain. edbc03de4156cf4292a92928e190749d50b317041827b9b83123798882ee4481 |
| +internal/host/rootgenerationpins.go generationPinSource.StagedPins authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. ba1ee92576995f2592b2837d535163e9c54f7bbcc63d729d69e0cc09a50e3244 |
| +internal/host/rootgenerationpins.go productionRootGenerationPins authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. file:Open@195 68f1c801080780dd36f864abf6c2cb59fe55265aabea02566008eeaecb0d291a |
| +internal/host/rootgenerationpins.go type generationPinSource authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. 48c0762d696c9693ed90a8003ea3eea5c72e64f72603e014eaed1cac4043b6bc |
| internal/host/versionsweep.go sweepStagedVersions authority E2,J3,J4 root-route E19: both destructive readers use the root reservation snapshot under existing release exclusion. Exact artifact and containing-tree identities are retained; missing completeness refuses destruction. Production installation/launch/kernel completeness and verified retirement remain unfinished. Fixtures: TestPlan14GenerationDeletionCannotUnpin, TestPlan14GenerationSnapshotCompleteness, TestPlan14GenerationSnapshotPermitsUnreservedRestage, TestPlan14GenerationSnapshotPinsContainingTree, TestPlan14GenerationSnapshotEmptyIsUnknown, TestPlan14GenerationConsumerMode. registry:GoodVersion@898,registry:GoodVersion@930,registry:PrevVersion@711,registry:PrevVersion@743,registry:Version@656,registry:Version@684,whole:Route@1231,whole:Route@435 e7fc68ed8c5ff2dc8a98daeb16c08f4d497b8016a3c459b6e4158721ba109843 |
| internal/host/versionsweep.go sweepStagedVersionsPass authority E2 E19: both destructive readers use the root reservation snapshot under existing release exclusion. Exact artifact and containing-tree identities are retained; missing completeness refuses destruction. Production installation/launch/kernel completeness and verified retirement remain unfinished. Fixtures: TestPlan14GenerationDeletionCannotUnpin, TestPlan14GenerationSnapshotCompleteness, TestPlan14GenerationSnapshotPermitsUnreservedRestage, TestPlan14GenerationSnapshotPinsContainingTree, TestPlan14GenerationSnapshotEmptyIsUnknown, TestPlan14GenerationConsumerMode, TestPlan14GenerationProductionModeGate, TestPlan14GenerationSnapshotPermitsFreshTree. efeae77306262f47556aeccb382409933995f80c52f989e469e088e714e7d888 |
| internal/release/adopt.go stageFrom authority E2 E19: both destructive readers use the root reservation snapshot under existing release exclusion. Exact artifact and containing-tree identities are retained; missing completeness refuses destruction. Production installation/launch/kernel completeness and verified retirement remain unfinished. Fixtures: TestPlan14GenerationDeletionCannotUnpin, TestPlan14GenerationSnapshotCompleteness, TestPlan14GenerationSnapshotPermitsUnreservedRestage, TestPlan14GenerationSnapshotPinsContainingTree, TestPlan14GenerationSnapshotEmptyIsUnknown, TestPlan14GenerationConsumerMode, TestPlan14GenerationProductionModeGate, TestPlan14GenerationSnapshotPermitsFreshTree. E22: root generation pins guard existing-tree artifact and manifest writes in adoption and moves-held staging/publication. Held snapshot is read under release exclusion before the leaf held-stage lock; failures remain restrictive for writes. Claims-off source returns no root pins. Fresh wholly absent version trees retain signed staging. Fixtures: TestPlan14GenerationSiblingWrites, TestPlan14GenerationVerifiedSiblingWrites, TestPlan14GenerationHeldStage (production source assembly, both flag values without census, lock order, pin/error/fresh cases). Production completeness remains unavailable; no new completeness grant. 30efb69f9a7ee5aca5a1909d94d3c6669065c7880207430e7e0d2a23ae90a8b6 |
| @@ -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 c69ab7fe435f0f512d92538df6b505517a0b444f776c158d51fe16250edcde64 |
| +@coverage internal/hostguard 512a9f96349d27edba5ff3c0a016911b70b136dde59717ee9bef0a5579cff5a2 |
| 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 |
| @@ -290,3 +290,20 @@ internal/release/materializeheld.go ServeHeldStageRequests authority E2 E23: old |
| internal/release/materializeheld.go ServeHeldStageRequestsWithPins authority E2 E23: old held-stage relay forwards the root generation source before artifact writes and outside publication exclusion. Request fields retain their prior hint/authorization checks; only root pins authorize destructive staging on census boxes. No-census source yields empty pins. Fixtures: TestPlan14GenerationLegacyRelay, TestHeldStagePassIsANoOpWithoutRequests, TestHeldStagePassDropsAnUnauthorizedBuild, TestServeHeldStageRequestsRefusesStaleAndTraversal. Production source wiring is inspected; privileged completeness is not claimed. file:ReadDir@429 edb9927c53af09311b4dd3e277a34d5eba7ab048d343b9b05e25f0b24a48e79d |
| internal/release/materializeheld.go serveHeldStageRequest authority E2 E23: old held-stage relay forwards the root generation source before artifact writes and outside publication exclusion. Request fields retain their prior hint/authorization checks; only root pins authorize destructive staging on census boxes. No-census source yields empty pins. Fixtures: TestPlan14GenerationLegacyRelay, TestHeldStagePassIsANoOpWithoutRequests, TestHeldStagePassDropsAnUnauthorizedBuild, TestServeHeldStageRequestsRefusesStaleAndTraversal. Production source wiring is inspected; privileged completeness is not claimed. file:OpenFile@615,file:ReadAll@812,whole:Route@1676 bbda896335311f5e09a75542d82c57fb993da72cbc27875b74bda79bf0239280 |
| internal/host/heldstage.go heldStagePass authority E2 E23: old held-stage relay forwards the root generation source before artifact writes and outside publication exclusion. Request fields retain their prior hint/authorization checks; only root pins authorize destructive staging on census boxes. No-census source yields empty pins. Fixtures: TestPlan14GenerationLegacyRelay, TestHeldStagePassIsANoOpWithoutRequests, TestHeldStagePassDropsAnUnauthorizedBuild, TestServeHeldStageRequestsRefusesStaleAndTraversal. Production source wiring is inspected; privileged completeness is not claimed. 759f041ad504375f3000acccc10b5fba80aaa44ab574c3491d1df2cb49a35087 |
| +internal/host/rootgeneration_census_linux.go checkedGenerationTree authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. file:Lstat@467,file:Open@84,file:WalkDir@192 2a9f530f41143ce64723797ef4c5628b536caa6fefb11bcbe57adbec38f9a012 |
| +internal/host/rootgeneration_census_linux.go generationKernel.census authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. file:Open@170 5c43de2497824076701fb6275b86d629f0a1035be212d41754d90ba39c431140 |
| +internal/host/rootgeneration_inventory.go generationInventoryAt authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. file:Open@173,file:Open@424 0fdfb9575baf9e9f5099e3ff463223d7b5100060fe3f6cf2908f20639e0e486e |
| +internal/hostbox/generationinventory.go LaunchJournal.GenerationLaunches authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. file:QueryContext@270 6694bd3a35b8d91f53e67134145c44854086f08593110ed1b905bca9587be290 |
| +internal/hostbox/generationinventory.go OpenGenerationLaunchReader authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. file:Lstat@218,file:Open@283 552d8629a7932c4ae5dca9d2beef4559f7e8a2864259d11e2ff98c699fdcd999 |
| +@coverage internal/hostbox 9e63382b4bf1376b75aef3040de0ed079cff0de80ba77d81598c0c06b62041e5 |
| +internal/host/rootgeneration_census_linux.go productionGenerationKernelCensus authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. 85f085029e0cd557c8ff8eb2d522fdf131c388229c14515fda0f45453d40eebb |
| +internal/host/rootgeneration_census_other.go productionGenerationKernelCensus authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. a1217cf881e9d05336fc16d6050f7f7a1a2184a0c20ff2988d0cf0f9012eaa45 |
| +internal/host/rootgeneration_inventory.go generationInstallation authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. 3e5e15d50752510acb44b10eb29621757a87bff54ceae015f704dd7f3a848ecb |
| +internal/host/rootgeneration_inventory.go generationInventoryDigest authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. 36ff5a31c6a013ac907d85e59b754cf4088df4600646ba7df177cf14e990177a |
| +internal/host/rootgeneration_inventory.go generationOwner authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. 69f89153837761f13116937b330ffbf406891299d5580296ea4aade905aa02ce |
| +internal/host/rootgeneration_inventory.go generationReconciler.reconcile authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. 35fba4c4b286b530288f83d150f93a49980cb5a587bdea1b69c4f71a90442fbf |
| +internal/host/rootgeneration_inventory.go openGenerationLaunchInventory authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. 522445e6d8acbe083d9d8f001719c4dbd3af57c56429e6b73b05c55a3826cb8b |
| +internal/host/rootgeneration_inventory.go productionGenerationInventory authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. 8c888114e1d40fa8d5496775a7dd136dc1c7a732c997369af61449b2a0f55a41 |
| +internal/host/rootgeneration_inventory.go type generationInventory authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. 398337e54b33efb37b21b181520f1bf9bcce48c34a0d66430f2e58505ae7bf00 |
| +internal/host/rootgeneration_inventory.go type generationReconciler authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. a979cdff1cd6311cf57d4a93e4501fb8dc2f65772d0013e622175b2a1f610d91 |
| +internal/hostguard/generationinventory.go RootAuthority.GenerationInstallations authority E2 E24: independent root installation and launch enumeration, exact reservation reconciliation and fresh quiescent whole-version-tree kernel census; missing sources, open unreserved launches, foreign namespaces and uncontained tasks refuse. Production pin source certifies this evidence before returning pins. Fixtures: TestPlan14GenerationInventoryCompleteness, TestPlan14GenerationInventoryPinConsumer, TestPlan14GenerationInventoryLifecycle, TestPlan14GenerationInventoryProcessCrash, TestPlan14GenerationKernelCensusUnknownReference, TestPlan14GenerationLaunchInventory, TestPlan14GenerationReaderDoesNotInitialize, TestPlan14GenerationInstallationInventory. Checked production source/kernel tests are runner-pending. 593768e6ba937cf2cfa2635c8f796f767d6c2354737a72073cc351f514faed20 |