Plan 14 integrated: guarded edge passes activation (not for merge)
The verifier recognises the exact guard-exec invocation, verifies the guard and its selected executable against signed authority, and matches the running edge; the lifecycle E2E uses the real guard-arm drop-in. Census text corrections. Retirement-crash cleanup under the real guard under investigation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
9 files changed,
+219
−44
internal/host/claimcensus.go+3 −0internal/host/claimcensus_test.go+11 −0internal/host/plan14_generation_registry_root_linux_test.go+26 −0internal/host/plan14_lifecycle_guard_root_linux_test.go+71 −0internal/host/plan14_lifecycle_root_linux_test.go+24 −19internal/host/rootgeneration_services_linux.go+27 −1internal/host/rootgeneration_services_linux_test.go+30 −0internal/provision/testdata/plan14-root-reads-activation-tools.tsv+15 −15internal/provision/testdata/plan14-root-reads-p3.tsv+12 −9
CI passed — run details
CI log
=== gate ===
cd launchctx && go vet ./... && go test ./... -count=1
ok amadan.net/carlos/platform.git/launchctx 0.011s
? amadan.net/carlos/platform.git/launchctx/controlname [no test files]
? amadan.net/carlos/platform.git/launchctx/wireid [no test files]
go vet ./...
gofmt -l .: clean
go test ./... -count=1 -timeout 30m
ok github.com/carlosframework/platform 309.002s
? github.com/carlosframework/platform/cmd/changelog [no test files]
ok github.com/carlosframework/platform/cmd/console 2.789s
ok github.com/carlosframework/platform/cmd/moveprobe 93.191s
ok github.com/carlosframework/platform/cmd/zddprobe 0.705s
ok github.com/carlosframework/platform/internal/activator 251.659s
ok github.com/carlosframework/platform/internal/agentsock 0.014s
ok github.com/carlosframework/platform/internal/alerting 0.003s
ok github.com/carlosframework/platform/internal/analytics 0.020s
ok github.com/carlosframework/platform/internal/appconfig 2.451s
ok github.com/carlosframework/platform/internal/apperr 0.070s
ok github.com/carlosframework/platform/internal/applog 0.130s
ok github.com/carlosframework/platform/internal/awsinv 0.003s
ok github.com/carlosframework/platform/internal/boxchoice 0.004s
ok github.com/carlosframework/platform/internal/boxid 0.669s
? github.com/carlosframework/platform/internal/boxid/boxidtest [no test files]
? github.com/carlosframework/platform/internal/boxid/internal/signtest [no test files]
ok github.com/carlosframework/platform/internal/boxvolumes 0.008s
ok github.com/carlosframework/platform/internal/caps 0.012s
ok github.com/carlosframework/platform/internal/certwarm 0.002s
ok github.com/carlosframework/platform/internal/cgroup 0.691s
ok github.com/carlosframework/platform/internal/changelog 0.302s
ok github.com/carlosframework/platform/internal/claimactivation 0.023s
ok github.com/carlosframework/platform/internal/clamd 0.054s
? github.com/carlosframework/platform/internal/cliflag [no test files]
ok github.com/carlosframework/platform/internal/console 405.673s
? 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.066s
ok github.com/carlosframework/platform/internal/dbdoor/dbdoortest 0.008s
ok github.com/carlosframework/platform/internal/dbverify 0.004s
ok github.com/carlosframework/platform/internal/dnsrec 0.004s
ok github.com/carlosframework/platform/internal/docsite 0.005s
ok github.com/carlosframework/platform/internal/domains 2.342s
ok github.com/carlosframework/platform/internal/doorkey 0.006s
ok github.com/carlosframework/platform/internal/economics 0.003s
ok github.com/carlosframework/platform/internal/edge 44.855s
? 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.131s
ok github.com/carlosframework/platform/internal/gate 1.655s
ok github.com/carlosframework/platform/internal/genselect 0.014s
ok github.com/carlosframework/platform/internal/geo 0.016s
ok github.com/carlosframework/platform/internal/geoip 0.029s
ok github.com/carlosframework/platform/internal/historyreadback 0.151s
ok github.com/carlosframework/platform/internal/host 173.772s
ok github.com/carlosframework/platform/internal/hostactors 0.454s
ok github.com/carlosframework/platform/internal/hostbox 2.571s
ok github.com/carlosframework/platform/internal/hostclaim 0.522s
ok github.com/carlosframework/platform/internal/hostevidence 0.005s
ok github.com/carlosframework/platform/internal/hostexec 4.728s
ok github.com/carlosframework/platform/internal/hostfence 0.036s
ok github.com/carlosframework/platform/internal/hostfiles 10.478s
ok github.com/carlosframework/platform/internal/hostfloor 0.012s
ok github.com/carlosframework/platform/internal/hostguard 1.029s
ok github.com/carlosframework/platform/internal/hostops 1.563s
? 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.427s
ok github.com/carlosframework/platform/internal/hostresolve 0.143s
ok github.com/carlosframework/platform/internal/hostwrite 0.001s
ok github.com/carlosframework/platform/internal/infraview 0.002s
ok github.com/carlosframework/platform/internal/instancectl 5.215s
ok github.com/carlosframework/platform/internal/instanceexec 2.724s
ok github.com/carlosframework/platform/internal/instancefs 0.019s
ok github.com/carlosframework/platform/internal/instancekey 0.013s
ok github.com/carlosframework/platform/internal/instanceupdates 0.018s
ok github.com/carlosframework/platform/internal/layout 1.525s
ok github.com/carlosframework/platform/internal/lbhealth 0.013s
ok github.com/carlosframework/platform/internal/lease 9.807s
ok github.com/carlosframework/platform/internal/lease/leasetest 0.010s
ok github.com/carlosframework/platform/internal/ledger 0.010s
ok github.com/carlosframework/platform/internal/mail 0.155s
ok github.com/carlosframework/platform/internal/maintenance 0.087s
ok github.com/carlosframework/platform/internal/maintenanceexec 5.503s
ok github.com/carlosframework/platform/internal/maintenanceguard 0.079s
ok github.com/carlosframework/platform/internal/metrics 0.007s
ok github.com/carlosframework/platform/internal/moves 21.031s
ok github.com/carlosframework/platform/internal/moves/archive 0.818s
ok github.com/carlosframework/platform/internal/objstore 0.186s
ok github.com/carlosframework/platform/internal/ops 0.175s
ok github.com/carlosframework/platform/internal/park 0.201s
ok github.com/carlosframework/platform/internal/privatefile 0.006s
ok github.com/carlosframework/platform/internal/provision 32.529s
ok github.com/carlosframework/platform/internal/publicrelease 0.006s
? github.com/carlosframework/platform/internal/regionname [no test files]
ok github.com/carlosframework/platform/internal/registry 5.422s
ok github.com/carlosframework/platform/internal/relay 7.219s
ok github.com/carlosframework/platform/internal/release 21.703s
ok github.com/carlosframework/platform/internal/rollout 3.641s
ok github.com/carlosframework/platform/internal/rolloutwords 0.003s
ok github.com/carlosframework/platform/internal/rootaws 0.006s
ok github.com/carlosframework/platform/internal/rootparticipation 0.057s
ok github.com/carlosframework/platform/internal/rootstate 0.026s
ok github.com/carlosframework/platform/internal/runs 0.573s
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.467s
ok github.com/carlosframework/platform/internal/seeds 0.010s
ok github.com/carlosframework/platform/internal/sim/hostlife 9.047s
ok github.com/carlosframework/platform/internal/sim/litestreamfacts 3.788s
ok github.com/carlosframework/platform/internal/status 0.037s
ok github.com/carlosframework/platform/internal/steering 0.006s
ok github.com/carlosframework/platform/internal/stopevidence 0.187s
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.003s
ok github.com/carlosframework/platform/internal/unixdial 0.002s
ok github.com/carlosframework/platform/internal/updatewords 0.004s
ok github.com/carlosframework/platform/internal/usage 0.004s
ok github.com/carlosframework/platform/internal/version 0.001s
ok github.com/carlosframework/platform/internal/vet 1.539s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
linux/arm64 carlos binary: 48824482 bytes (ceiling 52000000)
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
minio: cached, digest verified
mc: cached, digest verified
minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
buckets and scoped user ready
=== RUN TestDeliveredContractMinio
=== RUN TestDeliveredContractMinio/put
=== RUN TestDeliveredContractMinio/get_round-trips_the_body
=== RUN TestDeliveredContractMinio/list_shows_the_key
=== RUN TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN TestDeliveredContractMinio/delete_removes_it
=== RUN TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
--- PASS: TestDeliveredContractMinio/put (0.00s)
--- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
--- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
--- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
--- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
--- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
--- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok github.com/carlosframework/platform/internal/objstore 0.021s
=== RUN TestT4StorageProbeMinio
2026/10/17 16:55:45 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DF5F77E6326C04, 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.00s)
PASS
ok github.com/carlosframework/platform/internal/host 0.020s
minio contract: PASS
diff --git a/internal/host/claimcensus.go b/internal/host/claimcensus.go| index 83898c893..0b79ddd6b 100644 |
| --- a/internal/host/claimcensus.go |
| +++ b/internal/host/claimcensus.go |
| @@ -119,6 +119,9 @@ func claimUnitCensus(unitsDir string) []string { |
| // file bootstrap and the applier install for the whole box. |
| entries = append(entries, hostbox.CensusFile(name)) |
| } |
| + for _, unit := range []string{"carlos-edge.service", "carlos-system-update.service", "carlos-system-recover.service"} { |
| + entries = append(entries, hostbox.CensusFile(unit+".d/90-host-floor.conf")) |
| + } |
| for _, unit := range claimCensusEnabledUnits() { |
| if !slices.Contains(names, unit) || strings.Contains(unit, "@") { |
| continue |
diff --git a/internal/host/claimcensus_test.go b/internal/host/claimcensus_test.go| index d276e0943..20c2688d0 100644 |
| --- a/internal/host/claimcensus_test.go |
| +++ b/internal/host/claimcensus_test.go |
| @@ -216,6 +216,15 @@ func claimCensusBox(t *testing.T) ClaimRuntimeResources { |
| t.Fatal(err) |
| } |
| } |
| + for unit, mode := range map[string]string{"carlos-edge.service": "edge", "carlos-system-update.service": "apply", "carlos-system-recover.service": "recover"} { |
| + path := filepath.Join(r.UnitsDir, unit+".d", "90-host-floor.conf") |
| + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := os.WriteFile(path, []byte("[Service]\nExecStart=\nExecStart=/opt/carlos/system/guard/carlos system guard-exec "+mode+"\n"), 0644); err != nil { |
| + t.Fatal(err) |
| + } |
| + } |
| return r |
| } |
| @@ -312,6 +321,8 @@ func claimCensusMutations(r ClaimRuntimeResources, host string) []claimCensusMut |
| } |
| } |
| mutations := []claimCensusMutation{ |
| + {"unknown guarded-edge drop-in", newFile(unit("carlos-edge.service.d/91-unknown.conf"), "[Service]\nExecStart=/bin/true\n")}, |
| + {"guard drop-in symlink", relink(unit("carlos-edge.service.d/90-host-floor.conf"), "/missing/guard")}, |
| {"unknown data file", newFile(filepath.Join(r.DataDir, "unclassified.db"), "old")}, |
| {"unknown opt file", newFile(filepath.Join(r.OptDir, "unexplained"), "old")}, |
| {"unknown vendor link", newLink(unit("multi-user.target.wants/ssh.service"), "/usr/lib/systemd/system/ssh.service")}, |
diff --git a/internal/host/plan14_generation_registry_root_linux_test.go b/internal/host/plan14_generation_registry_root_linux_test.go| index bf839a3f8..1c692679d 100644 |
| --- a/internal/host/plan14_generation_registry_root_linux_test.go |
| +++ b/internal/host/plan14_generation_registry_root_linux_test.go |
| @@ -3,8 +3,10 @@ |
| package host |
| import ( |
| + "flag" |
| "fmt" |
| "github.com/carlosframework/platform/internal/instanceexec" |
| + "github.com/carlosframework/platform/internal/version" |
| "os" |
| "os/exec" |
| "os/user" |
| @@ -14,6 +16,30 @@ import ( |
| // The test binary must take the same private registry entrypoint as carlos. |
| func TestMain(m *testing.M) { |
| + if os.Getenv("PLAN14_LIFECYCLE_GUARD") == "1" && len(os.Args) > 1 { |
| + if os.Args[1] == "system" && len(os.Args) > 2 { |
| + switch os.Args[2] { |
| + case "guard-arm": |
| + version.Build = "v2.0.0" |
| + RunSystemGuardArm(os.Args[3:]) |
| + return |
| + case "guard-exec": |
| + RunSystemGuardExec(os.Args[3:]) |
| + return |
| + case "apply": |
| + RunSystemApply(os.Args[3:]) |
| + return |
| + } |
| + } |
| + if len(os.Args) == 2 && os.Args[1] == "agent" && os.Getenv("PLAN14_LIFECYCLE_AGENT") == "1" { |
| + os.Setenv("CARLOS_S3_ENDPOINT", os.Getenv("PLAN14_LIFECYCLE_DATA_ENDPOINT")) |
| + flag.Set("test.run", "^TestPlan14LifecycleEndToEndRootRunnerPayload$") |
| + flag.Set("test.v", "true") |
| + flag.Set("test.timeout", "4m") |
| + os.Exit(m.Run()) |
| + } |
| + } |
| + |
| if os.Getenv("PLAN14_CONSOLE") == "1" { |
| plan14ConsolePayload() |
| return |
diff --git a/internal/host/plan14_lifecycle_guard_root_linux_test.go b/internal/host/plan14_lifecycle_guard_root_linux_test.go| new file mode 100644 |
| index 000000000..f667e0c6c |
| --- /dev/null |
| +++ b/internal/host/plan14_lifecycle_guard_root_linux_test.go |
| @@ -0,0 +1,71 @@ |
| +//go:build linux && rootrunner |
| + |
| +package host |
| + |
| +import ( |
| + "crypto/ed25519" |
| + "encoding/json" |
| + "os" |
| + "os/exec" |
| + "strings" |
| + "testing" |
| + |
| + "github.com/carlosframework/platform/internal/hostbox" |
| + "github.com/carlosframework/platform/internal/hostfloor" |
| + "github.com/carlosframework/platform/internal/release" |
| +) |
| + |
| +func lifecycleArmGuard(t *testing.T, store release.Store, key ed25519.PrivateKey, actor hostbox.Actor, pointer release.SystemPointer, resume bool) { |
| + t.Helper() |
| + endpoint := activationS3Fixture(t, store) |
| + env := []string{"PLAN14_LIFECYCLE_GUARD=1", "CARLOS_DEPLOYMENT_BUCKET=" + actor.ControlBucket, "CARLOS_S3_ENDPOINT=" + endpoint, "AWS_REGION=eu-west-1", "AWS_DEFAULT_REGION=eu-west-1"} |
| + for _, name := range []string{"CARLOS_RELEASE_PUBKEY", "CARLOS_DATA_DIR", "CARLOS_OPT_DIR", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_EC2_METADATA_DISABLED", "AWS_CONFIG_FILE", "AWS_SHARED_CREDENTIALS_FILE"} { |
| + env = append(env, name+"="+os.Getenv(name)) |
| + } |
| + lifecycleCheck(t, "guard environment", os.WriteFile("/etc/carlos/host.env", []byte(strings.Join(env, "\n")+"\n"), 0600)) |
| + for _, unit := range []string{"carlos-edge.service", "carlos-system-update.service", "carlos-system-recover.service"} { |
| + path := "/etc/systemd/system/" + unit + ".d" |
| + if !resume { |
| + if _, err := os.Lstat(path); !os.IsNotExist(err) { |
| + t.Fatal("pre-existing guard drop-in", path, err) |
| + } |
| + } |
| + t.Cleanup(func() { |
| + if os.Getenv("PLAN14_LIFECYCLE_PHASE") != "1" || t.Failed() { |
| + os.RemoveAll(path) |
| + } |
| + exec.Command("systemctl", "daemon-reload").Run() |
| + }) |
| + } |
| + if !resume { |
| + floor, err := json.Marshal(hostfloor.Floor{Min: 1, MinRelease: "v2.0.0"}) |
| + lifecycleCheck(t, "guard floor", err) |
| + auth, err := release.SignRootEnvelope(release.RootSigner(key), hostfloor.Authorization{Schema: 1, Purpose: "moves-protocol", DeploymentBucket: actor.ControlBucket, Key: hostfloor.Key, SHA256: hostfloor.Digest(floor)}) |
| + lifecycleCheck(t, "guard authorization", err) |
| + policy, err := json.Marshal(hostfloor.Policy{Schema: 1, DeploymentBucket: actor.ControlBucket, Mode: "armed", Floor: floor, Authorization: auth}) |
| + lifecycleCheck(t, "guard policy", err) |
| + identity, err := release.SignRootEnvelope(release.RootSigner(key), pointer) |
| + lifecycleCheck(t, "guard signed identity", err) |
| + enrollment, err := json.Marshal(hostfloor.Enrollment{Box: actor.Box, Enrollment: actor.Enrollment, InstanceID: actor.Cloud.InstanceID}) |
| + lifecycleCheck(t, "guard enrollment", err) |
| + for name, raw := range map[string][]byte{"policy": policy, "identity": identity, "enrollment": enrollment} { |
| + lifecycleCheck(t, "guard input", os.WriteFile("/etc/carlos/"+name+".json", raw, 0600)) |
| + } |
| + cmd := exec.Command("/opt/carlos/carlos", "system", "guard-arm", "--policy", "/etc/carlos/policy.json", "--identity", "/etc/carlos/identity.json", "--enrollment", "/etc/carlos/enrollment.json") |
| + cmd.Env = append(env, "PATH=/usr/sbin:/usr/bin:/sbin:/bin") |
| + out, err := cmd.CombinedOutput() |
| + lifecycleCheck(t, "real guard-arm: "+string(out), err) |
| + } |
| + raw, err := os.ReadFile("/etc/systemd/system/carlos-edge.service.d/90-host-floor.conf") |
| + lifecycleCheck(t, "guard drop-in", err) |
| + if string(raw) != "[Unit]\nRequires=carlos-system-recover.service\nAfter=carlos-system-recover.service\n[Service]\nExecStart=\nExecStart=/opt/carlos/system/guard/carlos system guard-exec edge\n" { |
| + t.Fatal("edge does not retain guard-arm drop-in", string(raw)) |
| + } |
| + guard, err := os.Stat("/opt/carlos/system/guard/carlos") |
| + lifecycleCheck(t, "guard inode", err) |
| + current, err := os.Stat("/opt/carlos/carlos") |
| + lifecycleCheck(t, "current inode", err) |
| + if os.SameFile(guard, current) { |
| + t.Fatal("guard must be an independent copy") |
| + } |
| +} |
diff --git a/internal/host/plan14_lifecycle_root_linux_test.go b/internal/host/plan14_lifecycle_root_linux_test.go| index c528c56b1..59b764815 100644 |
| --- a/internal/host/plan14_lifecycle_root_linux_test.go |
| +++ b/internal/host/plan14_lifecycle_root_linux_test.go |
| @@ -241,21 +241,7 @@ func lifecycleRun(t *testing.T) { |
| _, err = store.Put(t.Context(), hostactors.Key, raw, "") |
| lifecycleCheck(t, "roster", err) |
| filesSeedConstructionMigration(t, "/opt/carlos", actor.ControlBucket, actor.Box, actor.Enrollment) |
| - state, err := rootstate.Open(rootparticipation.Namespace, true, false) |
| - lifecycleCheck(t, "census state", err) |
| - census := rootparticipation.Census{Schema: 1, Box: actor.Box, Enrollment: actor.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true} |
| - raw, err = json.Marshal(census) |
| - lifecycleCheck(t, "census encode", err) |
| - lifecycleCheck(t, "census", state.Write("census.json", raw)) |
| - state.Close() |
| - failures, err := rootstate.Open(rollout.RootFailuresDir, true, false) |
| - lifecycleCheck(t, "failure state", err) |
| - _, err = rollout.InitializeRootFailures(failures, rootUpdatesMigration(census), nil) |
| - lifecycleCheck(t, "initialize migration failures", err) |
| - failures.Close() |
| - pins, err := rootstate.Open(release.RootGenerationsDir, true, false) |
| - lifecycleCheck(t, "generation store", err) |
| - pins.Close() |
| + |
| } |
| binary, err := os.Executable() |
| lifecycleCheck(t, "test binary", err) |
| @@ -264,7 +250,7 @@ func lifecycleRun(t *testing.T) { |
| if !resume { |
| lifecycleCheck(t, "initial version namespace", os.MkdirAll("/opt/carlos/versions", 0755)) |
| lifecycleCheck(t, "executor", os.WriteFile("/opt/carlos/carlos", image, 0755)) |
| - lifecycleCheck(t, "guard executor", os.WriteFile("/opt/carlos/system/guard/carlos", image, 0755)) |
| + lifecycleCheck(t, "previous executor", os.WriteFile("/opt/carlos/carlos.prev", image, 0755)) |
| } |
| sum := sha256.Sum256(image) |
| platform := runtime.GOOS + "-" + runtime.GOARCH |
| @@ -278,6 +264,25 @@ func lifecycleRun(t *testing.T) { |
| identities.Close() |
| } |
| + lifecycleArmGuard(t, store, key, actor, pointer, resume) |
| + if !resume { |
| + state, err := rootstate.Open(rootparticipation.Namespace, true, false) |
| + lifecycleCheck(t, "census state", err) |
| + census := rootparticipation.Census{Schema: 1, Box: actor.Box, Enrollment: actor.Enrollment, Migration: 1, Revision: 1, LaunchRevision: 1, Complete: true} |
| + raw, err := json.Marshal(census) |
| + lifecycleCheck(t, "census encode", err) |
| + lifecycleCheck(t, "census", state.Write("census.json", raw)) |
| + state.Close() |
| + failures, err := rootstate.Open(rollout.RootFailuresDir, true, false) |
| + lifecycleCheck(t, "failure state", err) |
| + _, err = rollout.InitializeRootFailures(failures, rootUpdatesMigration(census), nil) |
| + lifecycleCheck(t, "initialize migration failures", err) |
| + failures.Close() |
| + pins, err := rootstate.Open(release.RootGenerationsDir, true, false) |
| + lifecycleCheck(t, "generation store", err) |
| + pins.Close() |
| + } |
| + |
| now := time.Now().UTC() |
| rec := provision.Record{Schema: 1, Host: lifecycleHost, Account: "bab", App: "lifecycle", Channel: "stable", Region: "", CreatedAt: now} |
| completeCreate := func() error { return nil } |
| @@ -381,12 +386,12 @@ func lifecycleStartAgent(t *testing.T, endpoint string, completeCreate func() er |
| for _, line := range strings.Split(string(unit), "\n") { |
| key, _, _ := strings.Cut(line, "=") |
| switch key { |
| - case "ExecStart", "ExecReload", "EnvironmentFile", "Restart": |
| + case "ExecReload", "Restart": |
| continue |
| } |
| lines = append(lines, line) |
| } |
| - lines = append(lines, "[Service]", "Restart=no", "ExecStart=/opt/carlos/carlos -test.run=^TestPlan14LifecycleEndToEndRootRunnerPayload$ -test.v -test.timeout=4m", "Environment=PLAN14_LIFECYCLE_AGENT=1", "Environment=PLAN14_LIFECYCLE_PHASE="+os.Getenv("PLAN14_LIFECYCLE_PHASE"), "Environment=CARLOS_S3_ENDPOINT="+endpoint, "Environment=TMPDIR=/run/carlos", "ReadWritePaths="+lifecycleHome) |
| + lines = append(lines, "[Service]", "Restart=no", "Environment=PLAN14_LIFECYCLE_AGENT=1", "Environment=PLAN14_LIFECYCLE_PHASE="+os.Getenv("PLAN14_LIFECYCLE_PHASE"), "Environment=PLAN14_LIFECYCLE_DATA_ENDPOINT="+endpoint, "Environment=TMPDIR=/run/carlos", "ReadWritePaths="+lifecycleHome) |
| path := "/run/systemd/system/carlos-edge.service" |
| f, err := os.OpenFile(path, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) |
| lifecycleCheck(t, "edge exclusive", err) |
| @@ -784,7 +789,7 @@ func lifecycleLaunch(t *testing.T, ctx context.Context, runtime *activator.Claim |
| func lifecycleInstallUnits(t *testing.T) { |
| t.Helper() |
| - for _, name := range []string{"carlos.slice", "carlos-instances.slice", "data-instances.mount", "run-carlos-instances.mount", "run-carlos-logs.mount", "carlos-instance@.service"} { |
| + for _, name := range []string{"carlos.slice", "carlos-instances.slice", "data-instances.mount", "run-carlos-instances.mount", "run-carlos-logs.mount", "carlos-instance@.service", "carlos-system-recover.service"} { |
| raw, err := unitsFS.ReadFile("units/" + name) |
| lifecycleCheck(t, "unit template", err) |
| path := filepath.Join("/etc/systemd/system", name) |
diff --git a/internal/host/rootgeneration_services_linux.go b/internal/host/rootgeneration_services_linux.go| index bbe9f81ea..c29da9545 100644 |
| --- a/internal/host/rootgeneration_services_linux.go |
| +++ b/internal/host/rootgeneration_services_linux.go |
| @@ -18,6 +18,7 @@ import ( |
| type generationService struct { |
| group, executable string |
| main int |
| + selected rootstate.FileIdentity |
| } |
| func generationServiceReader(ctx context.Context) func(string, []byte) (bool, error) { |
| @@ -77,6 +78,19 @@ func generationServiceReader(ctx context.Context) func(string, []byte) (bool, er |
| return false, os.ErrPermission |
| } |
| s.executable, _, _ = strings.Cut(tail, " ;") |
| + if unit == "carlos-edge.service" && s.executable == "/opt/carlos/system/guard/carlos" { |
| + if !generationGuardEdgeStart(unit, props["ExecStart"]) { |
| + return false, os.ErrPermission |
| + } |
| + if _, err := readRootFloorBinary("/opt/carlos", s.executable); err != nil { |
| + return false, err |
| + } |
| + selected, err := readRootFloorBinary("/opt/carlos", "/opt/carlos/carlos") |
| + if err != nil { |
| + return false, err |
| + } |
| + s.selected = selected.File |
| + } |
| } |
| units[unit] = s |
| } |
| @@ -95,8 +109,11 @@ func generationServiceReader(ctx context.Context) func(string, []byte) (bool, er |
| // Non-root services may execute only the program selected by their |
| // root-owned unit. UID and a service-looking cgroup are insufficient. |
| - if _, err := generationRootTask(status); err != nil { |
| + if _, err := generationRootTask(status); err != nil || s.selected != (rootstate.FileIdentity{}) { |
| want, e := generationIdentity(s.executable) |
| + if s.selected != (rootstate.FileIdentity{}) { |
| + want, e = s.selected, nil |
| + } |
| if e != nil || want != actual { |
| return false, os.ErrPermission |
| } |
| @@ -110,6 +127,15 @@ func generationServiceReader(ctx context.Context) func(string, []byte) (bool, er |
| } |
| } |
| +func generationGuardEdgeStart(unit, start string) bool { |
| + if unit != "carlos-edge.service" || strings.Count(start, "{") != 1 || strings.Count(start, "}") != 1 { |
| + return false |
| + } |
| + fields := strings.Split(start, " ; ") |
| + return len(fields) >= 4 && fields[0] == "{ path=/opt/carlos/system/guard/carlos" && |
| + fields[1] == "argv[]=/opt/carlos/system/guard/carlos system guard-exec edge" && fields[2] == "ignore_errors=no" |
| +} |
| + |
| func generationServiceGroup(group, base string) bool { |
| if group == base && base != "" { |
| return true |
diff --git a/internal/host/rootgeneration_services_linux_test.go b/internal/host/rootgeneration_services_linux_test.go| new file mode 100644 |
| index 000000000..9c4a5f23c |
| --- /dev/null |
| +++ b/internal/host/rootgeneration_services_linux_test.go |
| @@ -0,0 +1,30 @@ |
| +//go:build linux |
| + |
| +package host |
| + |
| +import ( |
| + "strings" |
| + "testing" |
| +) |
| + |
| +func TestGenerationGuardEdgeStart(t *testing.T) { |
| + const start = "{ path=/opt/carlos/system/guard/carlos ; argv[]=/opt/carlos/system/guard/carlos system guard-exec edge ; ignore_errors=no ; start_time=[n/a] ; stop_time=[n/a] ; pid=10 ; code=(null) ; status=0/0 }" |
| + if !generationGuardEdgeStart("carlos-edge.service", start) { |
| + t.Fatal("supported guard invocation refused") |
| + } |
| + for _, other := range []string{ |
| + strings.Replace(start, "guard-exec edge", "guard-exec recover", 1), |
| + strings.Replace(start, "guard-exec edge", "guard-exec edge extra", 1), |
| + strings.Replace(start, "argv[]=/opt/carlos/system/guard/carlos", "argv[]=other", 1), |
| + strings.Replace(start, "path=/opt/carlos/system/guard/carlos", "path=/opt/carlos/carlos", 1), |
| + strings.Replace(start, "ignore_errors=no", "ignore_errors=yes", 1), |
| + start + " " + start, |
| + } { |
| + if generationGuardEdgeStart("carlos-edge.service", other) { |
| + t.Fatal("unsupported invocation accepted", other) |
| + } |
| + } |
| + if generationGuardEdgeStart("another.service", start) { |
| + t.Fatal("another unit accepted") |
| + } |
| +} |
diff --git a/internal/provision/testdata/plan14-root-reads-activation-tools.tsv b/internal/provision/testdata/plan14-root-reads-activation-tools.tsv| index 1f0c2effb..f626de486 100644 |
| --- a/internal/provision/testdata/plan14-root-reads-activation-tools.tsv |
| +++ b/internal/provision/testdata/plan14-root-reads-activation-tools.tsv |
| @@ -1,27 +1,27 @@ |
| # Activation-tools runs 1–10: proposals only; loaded after all existing overlays. |
| @coverage . 2145f21807743eb683387a61738d6bb16d9c41d7d69a9071dbac765ccd9ec2d6 |
| @coverage internal/claimactivation 625d82dce14bae92f619077ca92a8c9066a9446545dfcc96a6f818e89e20d4c2 |
| -@coverage internal/host f9e020f793b41ae3bc207970c12c4245ce29f956c429a240e8b09b48912e00a5 |
| +@coverage internal/host 6673bed5fb74d6225f52a97683e7efd7cb13f97f032490c940c0cc258b1565d9 |
| @coverage internal/hostfence 552570510fb62d2a8822a3f24fa76aeb1437c0a9516966dc5ce15ef58e3fb4c6 |
| internal/claimactivation/runner.go Runner.Run authority F,G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=trusted operator phases bind expected signed roster revision, exact deployment and actors; verify each write; dry-run overlay never calls backing Put file:Read@310,file:Read@5986,file:Read@6318 683059aedf27656d4b49c88102089b2bb2f2ce7fcc108ff8545e52a86a0f8385 |
| internal/claimactivation/runner.go checkedStore.Delete authority F,G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=trusted operator phases bind expected signed roster revision, exact deployment and actors; verify each write; dry-run overlay never calls backing Put 6bc9103a30cfd53cea59d611ccacb7dcc0ca0ea620b403497ef543f5bdaa0217 |
| internal/claimactivation/runner.go checkedStore.Get authority F,G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=trusted operator phases bind expected signed roster revision, exact deployment and actors; verify each write; dry-run overlay never calls backing Put 366fec104118e7bce2b71ede13e7c8c72314d99fd32f773967e31018b0f0fbd6 |
| internal/claimactivation/runner.go checkedStore.Put authority F,G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=trusted operator phases bind expected signed roster revision, exact deployment and actors; verify each write; dry-run overlay never calls backing Put file:Read@1357 26c6e1f7cb2a057ef8b19deb30bc18fd62fe08f81ff48fd4022bdfc3dd22d6b0 |
| -internal/host/activation_command.go RunSystemClaims authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call cc8289fbcb3f34d8d43cd912a6a43ace727ed7289c6c32c5d35c8d00e6194cf3 |
| +internal/host/activation_command.go RunSystemClaims authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=dispatches explicit operator arguments to the platform command implementation and exits on error; wrapper adds no authority checks cc8289fbcb3f34d8d43cd912a6a43ace727ed7289c6c32c5d35c8d00e6194cf3 |
| @removed internal/host/activation_command_linux.go activationProcessesDrained G,J4 census-ruling-activation-tools-removal |
| -internal/host/activation_command_linux.go runSystemClaims authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call file:Read@1518 b71131cace86703746375d4525b91e69c35a4702021e8a003545cb2d78eefd0b |
| -internal/host/activation_command_other.go runSystemClaims authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call fcf45d665275c2ac99ff9bfc37302217bb6134b34ff74c3b17f50d1348760151 |
| -internal/host/activation_console.go activationConsoleEnvironment authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call 9d1144ac45e5849f43872d12ac6389c620ad07f0b2e69575a1353bfaf218b9fd |
| -internal/host/activation_install.go activationEnvironment authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call 289ff3c1267e613afd102f83f68592bfd99dafc3d6b7eaba05e630e6b2e7be3e |
| -internal/host/activation_install.go activationUnitSettings authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call data-literal@253,data-literal@427 5e540dae93fb146cc116d647677b7ca4d6fc6bc1ae0efb609b05fa907eba1fce |
| -internal/host/activation_install_linux.go activationDir authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call file:Fstat@1016,file:Fstat@398,file:Open@189,file:Openat@566,file:Openat@814 78d11f56b448ba6e5ba8dfb021cae1c286dcbaceb379dbfdd4bf6a0236efdd46 |
| -internal/host/activation_install_linux.go activationLock authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call file:Fstat@224 f51423c16e9f9ca1226a02178155cedfae7e38cfb450524899841295e01d219f |
| -internal/host/activation_install_linux.go activationRead authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call fee48092f7a26b0de5409052d63e6aa48ff830b66edf01a035be514a0470f22f |
| -internal/host/activation_install_linux.go activationReadAt authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call file:Fstat@234,file:ReadAll@440 5caf00466afd942308bdcb33b469b212f97bfcdcde3e1fc4847824b1859d29af |
| -internal/host/activation_install_linux.go installActivationConsole authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call 3a6bdc41ae970453395ae516ad5183b70af7ec191279ba0431c2a12b913dcad9 |
| -internal/host/activation_install_linux.go installActivationControl authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call file:ReadFile@616 16e3c76c1d1d06fc429725cfeaee21b17d6a37620da287b40482b12e2f6b20a5 |
| -internal/host/activation_install_linux.go installActivationEvidenceKey authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call 74f311fcb31aec95f5760ae9781186cc44a6f650858349c379e82f3a1fc6fd10 |
| -internal/host/activation_install_linux.go installActivationFenceCredentials authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit root operator command only; protected ancestry, exact inputs, retained mask ownership and durable readback; no automatic claims-off call 9e18de6df5fb9d3e6898967c9ed91e72d19faf5fd6bff7331ee654d76dfe7075 |
| +internal/host/activation_command_linux.go runSystemClaims authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=explicit command validates per-command arguments, protected input files and signed roster revisions or matching signing key before invoking phased writes, reporting or installers; dry-run propagates without writes; install-control requires offline migration preflight file:Read@1518 b71131cace86703746375d4525b91e69c35a4702021e8a003545cb2d78eefd0b |
| +internal/host/activation_command_other.go runSystemClaims authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=non-Linux command returns the activation refusal without executing any installer or signed phase fcf45d665275c2ac99ff9bfc37302217bb6134b34ff74c3b17f50d1348760151 |
| +internal/host/activation_console.go activationConsoleEnvironment authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=checks roster, local enrollment, compute binding and existing environment before deriving the exact console runtime environment; rejects conflicting keys and scope 9d1144ac45e5849f43872d12ac6389c620ad07f0b2e69575a1353bfaf218b9fd |
| +internal/host/activation_install.go activationEnvironment authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=parses environment assignments, rejecting invalid or duplicate keys, unsupported quoting, escapes and embedded controls 289ff3c1267e613afd102f83f68592bfd99dafc3d6b7eaba05e630e6b2e7be3e |
| +internal/host/activation_install.go activationUnitSettings authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=requires claims enabled and local region, defaults data and opt roots, and rejects nonabsolute, unclean or root directory paths data-literal@253,data-literal@427 5e540dae93fb146cc116d647677b7ca4d6fc6bc1ae0efb609b05fa907eba1fce |
| +internal/host/activation_install_linux.go activationDir authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=requires root caller and absolute clean path; traverses root-owned non-writable directories by no-follow descriptors and fsyncs permitted directory creation file:Fstat@1016,file:Fstat@398,file:Open@189,file:Openat@566,file:Openat@814 78d11f56b448ba6e5ba8dfb021cae1c286dcbaceb379dbfdd4bf6a0236efdd46 |
| +internal/host/activation_install_linux.go activationLock authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=opens protected activation lock at checked directory, requires root-owned mode 0600 and takes exclusive nonblocking flock file:Fstat@224 f51423c16e9f9ca1226a02178155cedfae7e38cfb450524899841295e01d219f |
| +internal/host/activation_install_linux.go activationRead authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=opens checked parent directory and delegates bounded protected file reading by basename fee48092f7a26b0de5409052d63e6aa48ff830b66edf01a035be514a0470f22f |
| +internal/host/activation_install_linux.go activationReadAt authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=opens file relative to checked directory, verifies root ownership, non-writable mode and requested private permissions, and limits input to 64 KiB file:Fstat@234,file:ReadAll@440 5caf00466afd942308bdcb33b469b212f97bfcdcde3e1fc4847824b1859d29af |
| +internal/host/activation_install_linux.go installActivationConsole authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=checks protected environment and drop-in parents, permits only exact existing content or claims-off predecessor, locks writes, durably writes and reads back both files, then reloads systemd; dry-run writes nothing 3a6bdc41ae970453395ae516ad5183b70af7ec191279ba0431c2a12b913dcad9 |
| +internal/host/activation_install_linux.go installActivationControl authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=checks protected environment and local claim settings, refuses masked or symlinked controller unit, locks installation, prepares root retry directory, writes rendered shipped unit and verifies readback before reload; dry-run writes nothing file:ReadFile@616 16e3c76c1d1d06fc429725cfeaee21b17d6a37620da287b40482b12e2f6b20a5 |
| +internal/host/activation_install_linux.go installActivationEvidenceKey authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=validates box/key binding, reads or exclusively installs private evidence key under protected directory lock, checks durable readback and expected public key, and signs the exact binding; dry-run does not generate a key 74f311fcb31aec95f5760ae9781186cc44a6f650858349c379e82f3a1fc6fd10 |
| +internal/host/activation_install_linux.go installActivationFenceCredentials authority G,J4 rev3=PROPOSED-activation-tools;identity=explicit operator CLI uid 0;basis=validates private credential source against exact bucket, region and role; locks protected target, refuses different existing bytes or receipt, writes missing credentials and installation receipt with readback; dry-run writes nothing 9e18de6df5fb9d3e6898967c9ed91e72d19faf5fd6bff7331ee654d76dfe7075 |
| @removed internal/host/activation_offline_linux.go activationOffline.begin G,J4 census-ruling-activation-tools-removal |
| @removed internal/host/activation_offline_linux.go activationOffline.checkItem G,J4 census-ruling-activation-tools-removal |
| @removed internal/host/activation_offline_linux.go activationOffline.dirs G,J4 census-ruling-activation-tools-removal |
diff --git a/internal/provision/testdata/plan14-root-reads-p3.tsv b/internal/provision/testdata/plan14-root-reads-p3.tsv| index 09a6a99b3..039408f30 100644 |
| --- a/internal/provision/testdata/plan14-root-reads-p3.tsv |
| +++ b/internal/provision/testdata/plan14-root-reads-p3.tsv |
| @@ -333,7 +333,7 @@ internal/hostguard/generationinventory.go RootAuthority.generationInstallation a |
| internal/host/rootgeneration_inventory.go generationReconciler.reconcile authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=checks matched root installations, payloads and launch reservations before and after kernel observation; a route-only installation without a launch requires the exact unlaunched identity derived by the independent reader from root creation evidence da4df99dcbcec0cd62025c8c50e8f6be19e64ac990034002bcad1a6b8332bcb6 |
| internal/host/rootgeneration_requirements.go generationRequiredInventory authority E2 rev3=PROPOSED-p3;run=16;identity=root adopt/update/control uid 0;basis=active installations require canonical deployment records; retired exact root tuples use retained artifacts and verified completed retirement journals without requiring deleted remote records; local obligations and pins remain 3c0eb88abc5680c67191d1e848e93ba528c66e81bcd521577098ce761fb827e0 |
| internal/host/rootgeneration_retirement.go generationRetirement.closure authority E2 rev3=PROPOSED-p3;run=16;identity=root adopt/update/control uid 0;basis=root installation and exact controller identity bind retirement; pre-intention cancellation requires no process and irreversible writer closure, followed by the existing independent kernel verifier and durable close/retirement writes 1035a55fba521c7c81631d14084684e2dd4cb94703a7604d982be880bcea0cdf |
| -internal/host/rootgeneration_inventory.go type generationInventory authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=root kernel generation verifier carries service classification and exact retained containment; all observations are independently checked before certification 979381265f27bb0dad39d184aa1f1e1aa22fd72268a688c99cadbaca6c9938f6 |
| +internal/host/rootgeneration_inventory.go type generationInventory authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=carries retained handovers, retirement journals, census, installations, launches, payloads, canonical records and independently derived unlaunched identities for generation reconciliation 979381265f27bb0dad39d184aa1f1e1aa22fd72268a688c99cadbaca6c9938f6 |
| internal/host/rootgeneration_parked.go generationPhysicalPayloads authority E2 rev3=PROPOSED-p3;run=16;identity=root claim/adoption controller uid 0;basis=verifies retained completed serving-handover journals against both root payloads, retired predecessor, exact stop receipt, ownership and persistent path identity; follows transfers through terminal retirement without removing launch reservations; TestPlan14ServingHandoverPins and TestPlan14ServingHandoverPinsRootRunner;rev3=PROPOSED-p3;run=21;basis=shared recorded-mount location for typed retirement and generation pin verification; root inventory and completed receipt required;basis=recorded mount relocation only for CapturePersistent root-protected artifacts; legacy destinations unchanged 67aca6be358b8f87a98c72e03a65d2dcd240cface604b17a058d7360a883e382 |
| internal/host/rootgeneration_parked.go generationRetirementRecords authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=enumerates and decodes retained retirement journals while excluding the serving-handover directory; generationPhysicalPayloads and its consumers perform subsequent receipt and artifact verification file:Read@728 65cc967484b693f71e4f78c418b85dcef801e82d0eed1ffd2cdda21c8ba163ef |
| internal/host/rootgeneration_rows.go generationRegistryInventory authority E2 rev3=PROPOSED-p3;run=16;identity=root claim/adoption controller uid 0;basis=verifies retained completed serving-handover journals against both root payloads, retired predecessor, exact stop receipt, ownership and persistent path identity; follows transfers through terminal retirement without removing launch reservations; TestPlan14ServingHandoverPins and TestPlan14ServingHandoverPinsRootRunner registry:ClaimOwnership@2394,registry:ClaimOwnership@2773,registry:ClaimRowID@2826,registry:Host@2372,registry:Host@2751,registry:Name@2692,registry:Registration@2804,whole:ClaimOwnership@1625,whole:ClaimOwnership@1734,whole:ClaimOwnership@1748,whole:ClaimOwnership@1762,whole:ClaimOwnership@2271,whole:ClaimOwnership@2400,whole:ClaimOwnership@2418,whole:ClaimOwnership@2473,whole:ClaimOwnership@2722,whole:ClaimOwnership@2779,whole:ClaimOwnership@2797,whole:ClaimOwnership@2892,whole:ClaimOwnership@2920,whole:Route@2289,whole:Timer@2578,whole:Timer@2656 4d4e526c596c7a7fe36a071ca2452acc63f4941b225d389971fb718f4c39dd33 |
| @@ -365,7 +365,7 @@ internal/hostguard/recoveryfence.go Guard.PendingStopFences authority C,J4 rev3= |
| internal/hostguard/recoveryfence.go Guard.pendingFences authority C,J2,J4,J1 rev3=PROPOSED-p3;run=19;identity=root;basis=reads root-owned records under host lock and keeps exact barrier classification separate from ordinary stop recovery; legacy behavior retained 20be9535fd54587a54e3027f5388581ae5a64e5fcfced5a6031ffc9a4fc76a3b |
| internal/host/claimroleassembly_linux.go verifiedClaimExecutable authority C,G,J4 rev3=PROPOSED-p3;run=19;identity=agent uid carlos;basis=pins root-owned executable bytes and keeps the verified descriptor while supplying its absolute path to the root launcher file:Fstat@1122,file:Fstat@579,file:OpenFile@376,file:Read@949 7a7e9af520e4157e60ae93d12c3268a629a9b0bd1e83b263cdd27103cf2e227c |
| internal/host/claimroleassembly_linux.go claimExecutableName authority C,G,J4 rev3=PROPOSED-p3;run=19;identity=agent uid carlos;basis=duplicates the existing descriptor with CLOEXEC and its validated absolute name; never reopens a potentially replaced path e887880731a16c9e7934aef33a08169b34e7236f6d3b85a195602d35f8b69107 |
| -@coverage internal/host f9e020f793b41ae3bc207970c12c4245ce29f956c429a240e8b09b48912e00a5 |
| +@coverage internal/host 6673bed5fb74d6225f52a97683e7efd7cb13f97f032490c940c0cc258b1565d9 |
| @coverage internal/hostbox 0b6ddb07bc7ef62f23e402757155825e8d098eb47ec9288c09b8754bb1632e56 |
| @coverage internal/hostguard ca664497969680d692172abda403b6a5d6664f8d7c8bc16a6440f02dd8426be6 |
| internal/host/claimstop.go ClaimStopCoordinator.FenceAndStop authority C,J4 rev3=PROPOSED-p3;run=20;identity=root instance-control uid 0;basis=authenticated session stop retains root fence before independent unjournaled absence check; proves no journaled installation, containment or open files before bypassing nonexistent writer closure; normal drain remains for recorded launches; no-census unchanged file:Read@1610 58147f3abbf278037fc660392aeec0b6e76b667e3fe8435845ebaedc62d2ead1 |
| @@ -440,16 +440,16 @@ internal/host/rootgeneration_retired_linux.go productionRetiredGenerationAbsence |
| internal/host/rootgeneration_retired_linux.go retiredGenerationSnapshot authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=proves exact retired installations through receipts and closed matched journals; active isolated siblings remain separately retained and pinned rather than making retirement a whole-box proof a00bec36603a167bbbe9d3d724da30a7db0025f7769801868c8514466644ef21 |
| internal/host/rootgeneration_retired_linux.go emptyGenerationGroups authority E2 rev3=PROPOSED-p3;run=26;identity=root retirement and pin readers uid 0 under release exclusion;basis=production wrapper supplies procfs, cgroupfs and the root reservation snapshot to the bounded managed-group inspection 24b4a91713227557ce9d66fff32aaf5ae80c3f532ea8335e1bc132661f4d9bb5 |
| internal/host/rootgeneration_retired_linux.go productionRetiredGenerationKernelCensus authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=checks retired installations own recorded groups and controller references; all populated managed groups must match a live root reservation, so active isolated siblings pass but unknown processes refuse 543f196321218063c074314fb9d05e95bc205db553890b127e11f7a8f40815aa |
| -internal/host/rootgeneration_retired_other.go productionRetiredGenerationAbsence authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=requires isolated reservation and exact root retirement receipt before checking recorded target absence and scoped shared-controller references; generic or named paths retain the stricter verifier a281b05dae1d2b65b0fbcffe6b7cb65002da5dabfdc4f2596bd05511594b3546 |
| -internal/host/rootgeneration_retired_other.go productionRetiredGenerationKernelCensus authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=checks retired installations own recorded groups and controller references; all populated managed groups must match a live root reservation, so active isolated siblings pass but unknown processes refuse f61b35d6064ff8a5a4fa757e229167d1b8e71b0109f8d116e0f288c533552a5c |
| +internal/host/rootgeneration_retired_other.go productionRetiredGenerationAbsence authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=non-Linux wrapper delegates to the unsupported generation-absence verifier and grants no retirement authority a281b05dae1d2b65b0fbcffe6b7cb65002da5dabfdc4f2596bd05511594b3546 |
| +internal/host/rootgeneration_retired_other.go productionRetiredGenerationKernelCensus authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=non-Linux wrapper delegates to the refusing kernel census and grants no generation authority f61b35d6064ff8a5a4fa757e229167d1b8e71b0109f8d116e0f288c533552a5c |
| internal/host/rootgeneration_retirement.go generationRetirement.retire authority E2 rev3=PROPOSED-p3;run=22;identity=root uid 0 holding release exclusion;basis=read immutable root retirement receipt snapshot before host exclusion; existing exact installation and journal closure checks run under exclusion before and after kernel verification; prevents recursive host locking aad12e0c897c3abab5dd04fc0f9377f56076a6341e42e92a15c4382e3bf22831 |
| internal/host/rootgeneration_retirement.go type generationRetirement authority E2 rev3=PROPOSED-p3;run=22;identity=root uid 0 under release and host exclusion;basis=retirement verifier carries a separate root-retired proof callback; caller reads canonical receipt before host locking while closure and kernel checks remain inside 132edefce1c8427220dcfee4d8bdcfc8ffa2bd21012cb97b3d330a73af13a93b |
| internal/host/claimstop_run.go activateClaimRoot authority C,D,E2,G rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=shared production startup recovers pending work, runs claimActivationReady under launch exclusion, then starts the lifecycle; the shipped-unit lifecycle witness uses this same function 32d687b7273cdc1d04ad7542e1a8f9a6bed9a566e581c4c08a12a709135a0f98 |
| internal/host/rootadoption_options.go rootAdoptionOptions authority B3,E1,E3 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=checks participation before constructing history or recovery options; missing census returns the original main option slice; census read errors refuse a076d56dfa12b4080a4592aaca3f21be89abb33896718f335ddaf08d8b8f4ccc |
| internal/host/rootexecution_transport.go ClaimRootHTTP.serveExecution authority D rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=authenticated broker refusal for a retired hostname has a distinct Gone response; other failures retain Conflict; no success capability is published c43b871b9cb5c41035c9d1dbc82e76e8b8cfb7d8fe0a4b8b7dbe864195073a3d |
| -internal/host/rootexecution_transport.go rootExecutionClient authority D rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=preserves the root retired-host refusal instead of translating it into ErrLost; success still validates the exact request tuple and serving/token shape 2a3981bb5549db09accdb28bcbb2da93460fb677c8f8ed7652bcdb2dec964a8b |
| +internal/host/rootexecution_transport.go rootExecutionClient authority D rev3=PROPOSED-p3;run=24;identity=service-UID agent and root claim/adopt/update controller uid 0;basis=preserves the root retired-host refusal instead of translating it into ErrLost; success still validates the exact request tuple and serving/token shape 2a3981bb5549db09accdb28bcbb2da93460fb677c8f8ed7652bcdb2dec964a8b |
| internal/host/rootgeneration_census_linux.go productionGenerationKernelCensus authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=checks protected staged tree and hardlink policy; actual systemd services pass only through unit, cgroup and executable verification while recorded live tenants use independent containment cc2500efe71568d535f5c2699115f7dc5922836c27977d072a1784fe807666f6 |
| -internal/host/rootgeneration_census_other.go productionGenerationKernelCensus authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=checks protected staged tree and hardlink policy; actual systemd services pass only through unit, cgroup and executable verification while recorded live tenants use independent containment a1217cf881e9d05336fc16d6050f7f7a1a2184a0c20ff2988d0cf0f9012eaa45 |
| +internal/host/rootgeneration_census_other.go productionGenerationKernelCensus authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=non-Linux implementation returns permission denied; performs no kernel census a1217cf881e9d05336fc16d6050f7f7a1a2184a0c20ff2988d0cf0f9012eaa45 |
| internal/host/rootgeneration_containment_linux.go generationKernel.containedTask authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=matches only live unclosed containment reservations to current kernel group and process identity; completed historical reservations cannot shadow an active sibling wake 25b3655284fb97a02d0f2e090ec105a18f178669979c147645b5a0f1966c893a |
| internal/host/rootgeneration_kernel_linux.go generationKernel.task authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=retains the original root-only scan with matching namespaces and strict reference checks when service lookup is unavailable; only independently verified unit, cgroup and executable identity permits nonroot or private-namespace services; every accepted service still has all references and process start rechecked file:Readlink@1197 d75cd84dda21cba70763794bfd3a996f212fc75f6d01e4ce0ffae93959b61bb7 |
| internal/host/rootgeneration_kernel_linux.go generationMounts authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=rejects target-tree bind mounts and nsfs references; independently verified system services may retain ancestor mounts used by shipped private mount namespaces; unknown tasks retain the strict namespace policy 8bfb9496d7c24fd4bca0f7b84cd0b9cf01a1c116a9112f66738be5dde93b1f75 |
| @@ -459,15 +459,15 @@ internal/host/rootgeneration_kernel_linux.go type generationKernel authority E2 |
| internal/host/rootgeneration_kernel_other.go productionGenerationAbsence authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=checks recorded launch targets and controller absence then inspects all tasks with independently verified service classification de47d997019075326e1d5513d7576390d085566fd69edc35efd00f067fb1db24 |
| internal/host/rootgeneration_retired_linux.go generationRetiredController authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=for an exact retired isolated installation, an old controller may still serve another host only after its boot/start identity and all thread references to this target generation are independently checked 793dfb40f339824ca3aeb9ceecfde69d8b63452eadf1a8abda734722401f1b79 |
| internal/host/rootgeneration_services_linux.go generationServiceGroup authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=accepts exact systemd cgroup or root-owned non-writable descendants only; delegated service-owned subgroups cannot impersonate platform or system services file:Lstat@281 b82ebaadda3ee8a36a66d69c36dda2551329197b903c09875b41fc0bbff44b38 |
| -internal/host/rootgeneration_services_linux.go generationServiceReader authority E2 rev3=PROPOSED-p3;run=25;identity=root generation kernel reader uid 0;basis=systemd main PID and trusted unit/executable identify only the edge main process threads in its exact supervise subgroup; unknown child processes and other services remain refused; does not exempt descriptor or generation-reference checks file:CommandContext@919,file:EvalSymlinks@1633,file:Readlink@2228 a25956a39883aa0e392068f715a99d7d86e993c9ec6c2fa7df6e3d5bed07964f |
| +internal/host/rootgeneration_services_linux.go generationServiceReader authority E2 rev3=PROPOSED-p3;run=25;identity=root generation kernel reader uid 0;basis=classifies independently verified platform and system services by trusted unit files, systemd main PID, cgroup and protected executable; only edge main threads may use its delegated supervise subgroup; exact guard-exec edge verifies guard and selected current against signed protected identities and matches current to the process; descriptor and generation-reference checks remain file:CommandContext@919,file:EvalSymlinks@1633,file:Readlink@2707 4e631521124d1055e7f2714d476dcc2fb3007a6838be595996af6dc38db1acbb |
| internal/hostguard/rootretired_host.go RootAuthority.HostRetired authority C rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=refusal-only host-locked read of the exact retained installation, inactive stamp and retired tuple; quarantine, pending installation and barrier refuse; no evidence is rewritten e9e93474a629358ab5dda2d845b2571e913d5a7ec14beb544851f55690b17b1b |
| internal/host/rootgeneration_services_linux.go generationServiceExecutable authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=opens the kernel-selected process root and checks each executable-path component with descriptor-relative no-symlink traversal, root ownership and no group/world writes; final single-link regular inode must equal the running executable; private controller mounts cannot hide a legitimate host executable file:Fstat@576,file:Open@243,file:Openat@1123 b0380d86d61198c94f826dc48df3d46f1352dd983279b75387dbbe2b51cca59f |
| -internal/host/rootgeneration_payload_other.go generationPayloadFiles authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=verifies exact retained persistent file identity; on a cross-boot ancestry refusal permits only the configured carlos owner at the managed root itself, retaining root-owned protected ancestors and refusing writable roots candidate:Kind@183,candidate:Kind@204 182189d8409113d671cdc76fc5d2d73e7056057bca230d546e65300c428b2c18 |
| +internal/host/rootgeneration_payload_other.go generationPayloadFiles authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=non-Linux implementation permits route and timer-only inventories and refuses physical payload entries; performs no persistent-file verification candidate:Kind@183,candidate:Kind@204 182189d8409113d671cdc76fc5d2d73e7056057bca230d546e65300c428b2c18 |
| internal/hostbox/inventory_identity_linux.go VerifyInventoryIdentity authority E2,J2,J4 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=default verifier retains root-owned ancestry and delegates exact persistent identity checks to the owner-aware verifier 5fc8216d53453a86c90a59e49a31b4e6027772488bae4b7c3068f79409891b18 |
| internal/hostbox/inventory_identity_linux.go VerifyInventoryIdentityForOwner authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=reopens the exact managed object without symlinks, verifies persistent filesystem, handle, inode, ownership and mount shape; cross-boot ancestry admits only the explicitly selected owner at the final managed root 1d0985ac5f306797f311c60ca582a8323c0194f6c3bfc1785dfe3da9cfa36786 |
| internal/hostbox/inventory_identity_linux.go checkedInventoryAncestry authority E2,J2,J4 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=default ancestry remains root-owned and protected through the final directory 80004516dfdd0c3dff425853c7f4010813a98068c6a0918e5367c40a575fe569 |
| internal/hostbox/inventory_identity_linux.go checkedInventoryAncestryOwner authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=descriptor-relative no-symlink walk requires root ownership and no group/world writes for every ancestor; only the final managed directory can have the explicitly selected service owner file:Fstat@649,file:Open@172,file:Openat@456 d4e93ccc1794dfc0854f723a637597ad5d8cc7a7117ada380b6289a2d1c470b1 |
| -internal/hostbox/inventory_identity_other.go VerifyInventoryIdentity authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=default verifier retains root-owned ancestry and delegates exact persistent identity checks to the owner-aware verifier 2c1db6b94ad3c11b95e6905ff935efe3e13396b6d4febb6d757c1eda38039921 |
| +internal/hostbox/inventory_identity_other.go VerifyInventoryIdentity authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=non-Linux implementation returns ErrUnsupported; performs no inventory identity verification 2c1db6b94ad3c11b95e6905ff935efe3e13396b6d4febb6d757c1eda38039921 |
| internal/host/rootgeneration_census_linux.go generationCensusRetry authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=restarts the entire independent kernel census at most three times only when a process disappears; unknown tasks and permission failures refuse immediately, and no partial census is accepted 3cfd54f395ed7e77b15c01b76567de0188573603aafc4e7ec72f0c3cfb9d512f |
| internal/host/rootgeneration_requirements.go generationLocalObligations authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=requires retained route and state artifacts; a current route-only installation without predecessors or any launch is unlaunched only after exact root FreshInstallation proof and physical state-directory absence, recomputed on every read file:Lstat@3018 1a5c493c0a6383df880d4d6e730f6d01822559cab804b57ac445e36bb4535f3c |
| internal/host/rootgeneration_requirements.go generationUnlaunched authority E2 rev3=PROPOSED-p3;run=24;identity=root claim/adopt/update controller uid 0;basis=requires absence of every root launch for this hostname, selects the earliest retained stamp of this same incarnation, and performs one durable unconsumed-creation read; any read error refuses; empty installations keep their existing path; consumers independently validate transfers and physical state 8041de414e9b90f044066c4cad51e7e8b43dea858a4e2ae6eb6f22418466a720 |
| @@ -496,3 +496,6 @@ internal/rootstate/persistent_linux.go persistentMountBoundary authority E2 rev3 |
| internal/host/rootinventory_state_linux.go verifyPreparedStateIdentity authority C,J1 rev3=PROPOSED-p3;run=28;identity=carlos agent uid carlos;basis=read-only existing-state check; root verifier remains strict; only a same-boot different namespace may add self-bind ancestors around unchanged child mount layers and persistent object identity; changed metadata child mounts filesystem or boot refuse;test=TestPlan14PreparedInventoryPrivateView 58c378288e10f40c5e50595fe54fd992a96b906aea9863491d37947e9450bb8e |
| internal/rootstate/persistent_linux.go uniqueMountIdentity authority E2 rev3=PROPOSED-p3;run=28;identity=root claim generation and inventory readers uid 0;basis=captures non-reused kernel mount and namespace sequence IDs through STATX_MNT_ID_UNIQUE and NS_GET_MNTNS_ID; missing or unsupported evidence refuses rather than falling back to recyclable numbers file:Open@344,file:Statx@95,file:Syscall@461 54d8759db39bb4e4255c81a56fdbf0ab52883e911a5d189acba7be4d26a7c490 |
| internal/rootstate/state.go FileIdentity.SameMountNamespace authority E2 rev3=PROPOSED-p3;run=28;identity=root claim generation and inventory readers uid 0;basis=same-boot namespace relation requires nonzero kernel namespace sequence IDs; recyclable proc namespace names never establish this relation 4e4a48f09b5e09d65f6e402eb17426f774e2e46ab6f7ee47406ba7d81334b80e |
| +internal/host/rootgeneration_services_linux.go generationGuardEdgeStart authority E2 rev3=PROPOSED-p3;run=30;identity=root generation verifier uid 0;basis=admits only the exact single systemd guard-exec edge invocation on carlos-edge.service without ignored errors; signed guard and selected executable verification remain in the caller 97a2702e03779010a6f5fc70a572e3da52e61775588d9ea2f14c840e021199e3 |
| +internal/host/claimcensus.go claimUnitCensus authority C,G,J4 rev3=PROPOSED-p3;run=30;identity=root installation admission controller uid 0;basis=classifies the three exact guard-arm platform drop-in files as shared regular files alongside shipped units; unknown drop-ins, per-host artifacts and symlinks remain refused file:ReadFile@771 2c2a0259fab748ce2a5045ca3da814d4d3abbac9e64ad04c3e266e72ed2a91ce |
| +@coverage internal/lease 511d4fb6f91c1b758758a02095047f11e0357ecb50875d7b5af0c390a7aba141 |