carlos / platform Public

WIP checkpoint (not for merge): files seed checkpoint 7

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Paul Campbell pushed by claude agent, operated by paul@keymail.dev 58716b1e799758ecbf7b3f1d0f872f4612977851 parent 0f33d46
5 files changed, +557 −17
  • internal/host/claimfilesseed_restore_linux_test.go +522 −0
  • internal/host/claimservinglegacyroot_linux_test.go +28 −11
  • internal/host/claimservingpark_linux_test.go +1 −1
  • internal/host/claimwakestop.go +3 −3
  • internal/provision/testdata/plan14-root-reads-seed.tsv +3 −2

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	356.734s
?   	github.com/carlosframework/platform/cmd/changelog	[no test files]
ok  	github.com/carlosframework/platform/cmd/console	3.057s
ok  	github.com/carlosframework/platform/cmd/moveprobe	92.230s
ok  	github.com/carlosframework/platform/cmd/zddprobe	0.705s
ok  	github.com/carlosframework/platform/internal/activator	234.953s
ok  	github.com/carlosframework/platform/internal/agentsock	0.018s
ok  	github.com/carlosframework/platform/internal/alerting	0.006s
ok  	github.com/carlosframework/platform/internal/analytics	0.020s
ok  	github.com/carlosframework/platform/internal/appconfig	2.333s
ok  	github.com/carlosframework/platform/internal/apperr	0.078s
ok  	github.com/carlosframework/platform/internal/applog	0.130s
ok  	github.com/carlosframework/platform/internal/awsinv	0.004s
ok  	github.com/carlosframework/platform/internal/boxchoice	0.004s
ok  	github.com/carlosframework/platform/internal/boxid	0.848s
?   	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.009s
ok  	github.com/carlosframework/platform/internal/caps	0.012s
ok  	github.com/carlosframework/platform/internal/certwarm	0.003s
ok  	github.com/carlosframework/platform/internal/cgroup	0.704s
ok  	github.com/carlosframework/platform/internal/changelog	0.306s
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	366.593s
?   	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.030s
ok  	github.com/carlosframework/platform/internal/dbdoor/dbdoortest	0.015s
ok  	github.com/carlosframework/platform/internal/dbverify	0.006s
ok  	github.com/carlosframework/platform/internal/dnsrec	0.003s
ok  	github.com/carlosframework/platform/internal/docsite	0.006s
ok  	github.com/carlosframework/platform/internal/domains	2.142s
ok  	github.com/carlosframework/platform/internal/doorkey	0.007s
ok  	github.com/carlosframework/platform/internal/economics	0.009s
ok  	github.com/carlosframework/platform/internal/edge	26.302s
?   	github.com/carlosframework/platform/internal/edgeproof	[no test files]
ok  	github.com/carlosframework/platform/internal/edgesfile	0.010s
ok  	github.com/carlosframework/platform/internal/fleetapi	0.127s
ok  	github.com/carlosframework/platform/internal/gate	1.671s
ok  	github.com/carlosframework/platform/internal/genselect	0.020s
ok  	github.com/carlosframework/platform/internal/geo	0.016s
ok  	github.com/carlosframework/platform/internal/geoip	0.037s
ok  	github.com/carlosframework/platform/internal/host	137.863s
ok  	github.com/carlosframework/platform/internal/hostactors	0.391s
ok  	github.com/carlosframework/platform/internal/hostbox	2.193s
ok  	github.com/carlosframework/platform/internal/hostclaim	0.527s
ok  	github.com/carlosframework/platform/internal/hostevidence	0.015s
ok  	github.com/carlosframework/platform/internal/hostexec	4.862s
ok  	github.com/carlosframework/platform/internal/hostfence	0.033s
ok  	github.com/carlosframework/platform/internal/hostfiles	10.497s
ok  	github.com/carlosframework/platform/internal/hostfloor	0.015s
ok  	github.com/carlosframework/platform/internal/hostguard	0.978s
ok  	github.com/carlosframework/platform/internal/hostops	1.460s
?   	github.com/carlosframework/platform/internal/hostops/fencerelease	[no test files]
ok  	github.com/carlosframework/platform/internal/hostops/internal/fencerelease	0.015s
ok  	github.com/carlosframework/platform/internal/hostquarantine	0.015s
ok  	github.com/carlosframework/platform/internal/hostrelocate	0.450s
ok  	github.com/carlosframework/platform/internal/hostresolve	0.139s
ok  	github.com/carlosframework/platform/internal/hostwrite	0.015s
ok  	github.com/carlosframework/platform/internal/infraview	0.015s
ok  	github.com/carlosframework/platform/internal/instancectl	1.817s
ok  	github.com/carlosframework/platform/internal/instanceexec	2.710s
ok  	github.com/carlosframework/platform/internal/instancefs	0.019s
ok  	github.com/carlosframework/platform/internal/instancekey	0.016s
ok  	github.com/carlosframework/platform/internal/instanceupdates	0.021s
ok  	github.com/carlosframework/platform/internal/layout	1.445s
ok  	github.com/carlosframework/platform/internal/lease	9.439s
ok  	github.com/carlosframework/platform/internal/lease/leasetest	0.006s
ok  	github.com/carlosframework/platform/internal/ledger	0.015s
ok  	github.com/carlosframework/platform/internal/mail	0.181s
ok  	github.com/carlosframework/platform/internal/maintenance	0.080s
ok  	github.com/carlosframework/platform/internal/maintenanceexec	5.540s
ok  	github.com/carlosframework/platform/internal/maintenanceguard	0.085s
ok  	github.com/carlosframework/platform/internal/metrics	0.006s
ok  	github.com/carlosframework/platform/internal/moves	21.029s
ok  	github.com/carlosframework/platform/internal/moves/archive	0.783s
ok  	github.com/carlosframework/platform/internal/objstore	0.171s
ok  	github.com/carlosframework/platform/internal/ops	0.175s
ok  	github.com/carlosframework/platform/internal/park	0.209s
ok  	github.com/carlosframework/platform/internal/privatefile	0.004s
ok  	github.com/carlosframework/platform/internal/provision	11.786s
ok  	github.com/carlosframework/platform/internal/publicrelease	0.006s
?   	github.com/carlosframework/platform/internal/regionname	[no test files]
ok  	github.com/carlosframework/platform/internal/registry	4.031s
ok  	github.com/carlosframework/platform/internal/relay	7.231s
ok  	github.com/carlosframework/platform/internal/release	20.116s
ok  	github.com/carlosframework/platform/internal/rollout	3.652s
ok  	github.com/carlosframework/platform/internal/rolloutwords	0.004s
ok  	github.com/carlosframework/platform/internal/rootaws	0.007s
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.289s
ok  	github.com/carlosframework/platform/internal/sim/hostlife	9.262s
ok  	github.com/carlosframework/platform/internal/sim/litestreamfacts	4.154s
ok  	github.com/carlosframework/platform/internal/status	0.041s
ok  	github.com/carlosframework/platform/internal/steering	0.006s
ok  	github.com/carlosframework/platform/internal/stopevidence	0.349s
ok  	github.com/carlosframework/platform/internal/svcrole	0.002s
ok  	github.com/carlosframework/platform/internal/telemetry	0.004s
ok  	github.com/carlosframework/platform/internal/termfence	0.004s
ok  	github.com/carlosframework/platform/internal/tunnelclient	0.004s
ok  	github.com/carlosframework/platform/internal/unixdial	0.003s
ok  	github.com/carlosframework/platform/internal/updatewords	0.004s
ok  	github.com/carlosframework/platform/internal/usage	0.004s
ok  	github.com/carlosframework/platform/internal/version	0.002s
ok  	github.com/carlosframework/platform/internal/vet	1.588s
=== cross ===
--- shard 0 of 5 ---
cross-compiling darwin/arm64
--- shard 1 of 5 ---
cross-compiling darwin/amd64
--- shard 2 of 5 ---
cross-compiling linux/arm64
--- shard 3 of 5 ---
cross-compiling windows/amd64
--- shard 4 of 5 ---
cross-compiling windows/arm64
=== minio-contract ===
minio contract: arm64, minio RELEASE.2025-09-07T16-13-09Z, mc RELEASE.2025-08-13T08-35-41Z
  minio: cached, digest verified
  mc: cached, digest verified
  minio healthy on 127.0.0.1:20999
Added `local` successfully.
Bucket created successfully `local/contract-bucket`.
Bucket created successfully `local/denied-bucket`.
Created policy `contract-only` successfully.
Added user `scopeduser` successfully.
Attached Policies: [contract-only]
To User: scopeduser
  buckets and scoped user ready
=== RUN   TestDeliveredContractMinio
=== RUN   TestDeliveredContractMinio/put
=== RUN   TestDeliveredContractMinio/get_round-trips_the_body
=== RUN   TestDeliveredContractMinio/list_shows_the_key
=== RUN   TestDeliveredContractMinio/head_and_list_agree_on_the_version
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict
=== RUN   TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed
=== RUN   TestDeliveredContractMinio/delete_removes_it
=== RUN   TestDeliveredContractMinio/cross-bucket_denial
--- PASS: TestDeliveredContractMinio (0.02s)
    --- PASS: TestDeliveredContractMinio/put (0.00s)
    --- PASS: TestDeliveredContractMinio/get_round-trips_the_body (0.00s)
    --- PASS: TestDeliveredContractMinio/list_shows_the_key (0.00s)
    --- PASS: TestDeliveredContractMinio/head_and_list_agree_on_the_version (0.00s)
    --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/not_by_PutObjectTagging (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_its_own_object/and_not_by_x-amz-tagging_on_upload (0.00s)
    --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route (0.01s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CreateMultipartUpload (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/not_by_CopyObject_with_a_REPLACE_tagging_directive (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/but_multipart_itself_is_allowed (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_copy_itself_is_allowed (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/a_POST-policy_forged_tag_with_a_matching_version_is_still_not_a_verdict (0.00s)
        --- PASS: TestDeliveredContractMinio/the_app_cannot_tag_by_any_other_route/and_an_ordinary_tagged-with-something-else_upload_is_allowed (0.00s)
    --- PASS: TestDeliveredContractMinio/delete_removes_it (0.00s)
    --- PASS: TestDeliveredContractMinio/cross-bucket_denial (0.00s)
PASS
ok  	github.com/carlosframework/platform/internal/objstore	0.024s
=== RUN   TestT4StorageProbeMinio
2026/10/14 13:23:35 moves: T4: STORE: list: operation error S3: ListObjectsV2, https response error StatusCode: 403, RequestID: 18DE68263A9D5D4F, 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.024s
minio contract: PASS
diff --git a/internal/host/claimfilesseed_restore_linux_test.go b/internal/host/claimfilesseed_restore_linux_test.go
new file mode 100644
index 000000000..b28b7d688
--- /dev/null
+++ b/internal/host/claimfilesseed_restore_linux_test.go
@@ -0,0 +1,522 @@
+//go:build linux
+
+package host
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "net/http/httptest"
+ "os"
+ "path/filepath"
+ "reflect"
+ "strings"
+ "syscall"
+ "testing"
+ "time"
+
+ "github.com/carlosframework/platform/internal/activator"
+ "github.com/carlosframework/platform/internal/cgroup"
+ "github.com/carlosframework/platform/internal/genselect"
+ "github.com/carlosframework/platform/internal/hostbox"
+ "github.com/carlosframework/platform/internal/hostclaim"
+ "github.com/carlosframework/platform/internal/hostwrite"
+ "github.com/carlosframework/platform/internal/lease"
+ "github.com/carlosframework/platform/internal/lease/leasetest"
+ "github.com/carlosframework/platform/internal/provision"
+ "github.com/carlosframework/platform/internal/registry"
+ "github.com/superfly/ltx"
+ "golang.org/x/sys/unix"
+)
+
+// This fixture ends at Restore: a legacy holder has no C8 evidence yet.
+// It uses the real selector, lease against local S3, retry wrapper, admission,
+// activation, prelaunch journal, selected Work, stop coordinator and retirement
+// transport. Kernel checks, installation enrollment and launch preflight are
+// explicit seams; it does not claim an installed-unit or privileged witness.
+type servingPreparation struct {
+ f *servingRootFixture
+ owners *claimServingOwners
+ wake *ClaimWakeAssembly
+ runtime *activator.ClaimRuntime
+ local claimStopLocal
+ events []string
+ baseline *genselect.Plan
+ enrollFailure error
+ afterEnroll func()
+}
+
+func newServingPreparation(t *testing.T, f *servingRootFixture, boot, controller string) *servingPreparation {
+ t.Helper()
+ p := &servingPreparation{f: f, local: claimStopLocal{Actor: f.actor, BootID: boot, ControllerID: controller, PID: 77, ProcessStart: controller + "-start"}}
+ f.f.peer = StopPeer{UID: 1001, PID: p.local.PID, ProcessStart: p.local.ProcessStart, BootID: boot, ControllerID: controller}
+ launchFixture := plan9D5Fixture(t)
+ launchFixture.launch.Args = []string{"--socket", provision.NestedSocket(recoveryHost), "--db", f.db}
+ launchFixture.launch.Limit = cgroup.TierDefault
+ launchFixture.launch.Env = []string{"CARLOS_ADMIN_TOKEN=test"}
+ sources := launchFixture.owners.Sources
+ p.owners = &claimServingOwners{Data: f.f.data, Bucket: f.f.bucket, Region: f.actor.Region, Box: f.actor.Box, Sources: sources, Now: func() time.Time { return f.f.now }, NewID: f.f.newID}
+ producers := &ClaimProducerSet{}
+ w := &ClaimWakeAssembly{LeaseRegion: "eu-west-1", Manager: new(cgroup.Manager), Inputs: p.owners, RequireServingEnrollment: true}
+ p.wake = w
+ w.AfterAdmission = func(ctx context.Context, _, admitted hostclaim.Observed) error {
+ p.events = append(p.events, "admit")
+ // The production root installation door requires privileged host inventory.
+ // Retain that explicit seam, but do not advance it before admission.
+ f.install(admitted)
+ return nil
+ }
+ w.Prelaunch = func(ctx context.Context, o hostclaim.Observed, work hostwrite.Work, in ClaimLaunchInputs) (*activator.RoleLaunch, error) {
+ p.events = append(p.events, "prelaunch")
+ target := recoveryTuple(o, p.local)
+ err := work.With(ctx, in.Identity, func() error {
+ return f.f.journal.Prelaunch(ctx, hostbox.LaunchInventory{Target: target, ControllerPID: p.local.PID, ProcessStart: p.local.ProcessStart, NoState: true, Groups: []hostbox.LaunchGroup{{RelativePath: "carlos/" + claimContainmentName(target), Device: 1, Inode: target.Session}}})
+ })
+ return &activator.RoleLaunch{Target: target, Journal: f.f.journal}, err
+ }
+ roles := &ClaimRoleAssembly{Inventory: f.p.root.Journals.Inventory, ManagedRoots: map[string]string{"data": f.f.guard.DataDir}, Identity: handoverRecoveryIdentity(t, f.actor)}
+ w.Roles = roles
+ roles.Root = fakeLaunchRoot{protocol: hostbox.RoleLaunchProtocol}
+ w.PrepareState = func(ctx context.Context, o hostclaim.Observed, work hostwrite.Work, in ClaimLaunchInputs) error {
+ p.events = append(p.events, "state")
+ return roles.PrepareState(ctx, o, work, in)
+ }
+ w.Launch = func(context.Context, hostclaim.Observed, hostwrite.Work, ClaimLaunchInputs, *activator.RoleLaunch) (*activator.RoleLaunch, error) {
+ t.Fatal("tenant launch in restore witness")
+ return nil, hostbox.ErrUnsupported
+ }
+ w.Producers = func(_ context.Context, o hostclaim.Observed) (*ClaimProducers, error) {
+ return producers.Ensure(recoveryTuple(o, p.local), p.local.ProcessStart, f.f.journal.RecordProducer)
+ }
+ w.Retain = func(hostclaim.Observed, activator.Proc, activator.Rep, error) {}
+ w.ObserveLoss = func(hostclaim.Observed, error) {}
+ w.Retries = activator.ServingRetries{Claims: f.f.claims, Box: f.actor.Box, Enrollment: f.actor.Enrollment, BootID: boot, ControllerID: controller, Now: func() time.Time { return f.f.now }, NewID: f.f.newID, Inputs: p.owners.launchDigest}
+ a := ClaimAssembly{Claims: f.f.claims, Launches: f.f.journal, Wake: w, Producers: producers, stopLocal: func(context.Context) (claimStopLocal, error) { return p.local, nil }}
+ a.collectStop = func(ctx context.Context, req StopRequest, drain StopDrainer) (StopResult, error) {
+ f.f.requests = append(f.f.requests, req)
+ coordinator := f.p.root.Stop.Coordinator
+ coordinator.Drainer = drain
+ coordinator.RetainNoProcess = f.p.retainLegacyNoProcess
+ if req.WriterSession != 0 {
+ coordinator.Kernel = f.f.kernel
+ }
+ result, err := coordinator.FenceAndStop(ctx, req)
+ if err != nil {
+ return result, err
+ }
+ return validateTransportStop(req, result)
+ }
+ a.bindClaimWakeStop()
+ retirement := &claimServingArtifactRetirement{Claims: f.f.claims, Guard: f.f.guard, DataDir: f.f.guard.DataDir, Box: f.actor.Box, Region: f.actor.Region, Root: hostbox.ServingParkClient{Call: servingParkSocket(t, servingTransportRoot(t, f))}}
+ w.RetireArtifacts = retirement.Retire
+ w.RecoverArtifacts = func(ctx context.Context, o hostclaim.Observed, in ClaimLaunchInputs) error {
+ p.events = append(p.events, "recover")
+ return retirement.Recover(ctx, o, in)
+ }
+ w.BeforeLegacyInstall = retirement.BeforeLegacyInstall
+ w.BeforeInstallLocked = func(ctx context.Context, o hostclaim.Observed, in ClaimLaunchInputs) error {
+ if err := retirement.EnrollLocked(ctx, o, in); err != nil {
+ return err
+ }
+ p.events = append(p.events, "enrolled")
+ if p.afterEnroll != nil {
+ p.afterEnroll()
+ }
+ return p.enrollFailure
+ }
+ source := hostbox.WorkSource{Guard: f.f.guard, Box: f.actor.Box, Region: f.actor.Region, Now: func() time.Time { return f.f.now }, Policy: func(context.Context, *hostclaim.Claim, hostbox.Selection, hostwrite.Identity) error { return nil }, Local: func(context.Context, *hostclaim.Claim, hostbox.Selection) error { return nil }}
+ writer := hostbox.Writer{Data: f.f.data, Bucket: f.f.bucket, Claims: f.f.claims, Box: f.actor.Box, Region: f.actor.Region, AdmissionEvidence: p.owners.admissionEvidence}
+ var err error
+ p.runtime, err = w.Build(t.Context(), source, writer)
+ if err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() {
+ ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+ defer cancel()
+ if err := w.Close(ctx); err != nil {
+ t.Error(err)
+ }
+ })
+ return p
+}
+
+func (p *servingPreparation) prepare(t *testing.T) (hostclaim.Observed, activator.ClaimStart) {
+ t.Helper()
+ f := p.f
+ rt := registry.Route{Host: recoveryHost, DBPath: f.db}
+ o := f.f.read()
+ // Prerequisites' isolated launch preflight requires an installed /data
+ // namespace. Drive its selection, stop and real lease in the same order;
+ // Admit and Prepare below are the unmodified production runtime closures.
+ var err error
+ if f.f.guard.DataDir == "/data" {
+ if err = p.runtime.Prerequisites(t.Context(), rt, o, recoveryPolicy); err != nil {
+ t.Fatal("prerequisites", err)
+ }
+ p.events = append(p.events, "prerequisites")
+ pending := p.wake.get(rt.Host)
+ if pending == nil || pending.held == nil {
+ t.Fatal("prerequisites returned without a lease")
+ }
+ _, selected := pending.snapshot()
+ p.baseline = selected.Baseline
+ } else {
+ selected, err := p.owners.Select(t.Context(), rt, o, recoveryPolicy)
+ if err != nil {
+ t.Fatal(err)
+ }
+ p.events = append(p.events, "select")
+ if selected.Empty || selected.Baseline == nil {
+ t.Fatal("baseline restore replaced by empty install")
+ }
+ p.baseline = selected.Baseline
+ if err = p.wake.stopPredecessor(t.Context(), o); err != nil {
+ t.Fatal("stop predecessor", err)
+ }
+ p.events = append(p.events, "stop")
+ held, err := lease.Acquire(t.Context(), f.f.bucket, o.Claim.Holder.Location, p.wake.LeaseRegion)
+ if err != nil {
+ t.Fatal(err)
+ }
+ p.events = append(p.events, "lease")
+ live, cancel := context.WithCancel(t.Context())
+ done := make(chan struct{})
+ go func() { <-live.Done(); close(done) }()
+ p.wake.pending[rt.Host] = &claimWakeAttempt{owner: p.wake, observed: o, inputs: selected, held: held, ctx: live, cancel: cancel, done: done}
+ }
+ admitted, err := p.runtime.Admit(t.Context(), rt, f.f.read(), recoveryPolicy)
+ if err != nil {
+ t.Fatal("admit", err)
+ }
+ r := admitted.Claim.Holder.WakeRetry
+ if r == nil || r.Attempt == nil || r.Attempt.Session != admitted.Claim.Holder.Session || r.Attempt.ControllerID != p.local.ControllerID {
+ t.Fatalf("retry not bound: %+v", r)
+ }
+ start, err := p.runtime.Prepare(t.Context(), rt, admitted, recoveryPolicy)
+ if err != nil {
+ t.Fatal("prepare", err)
+ }
+ return admitted, start
+}
+
+func TestFilesSeedOrdinaryPreparationRestore(t *testing.T) {
+ for _, failure := range []string{"", "enrolled-refusal", "missing-marker", "cancel-after-enrollment", "disk-after-enrollment", "invalid-sqlite"} {
+ for _, restart := range []string{"same-controller", "controller", "reboot"} {
+ t.Run(failure+"/"+restart, func(t *testing.T) { runFilesSeedOrdinaryPreparation(t, failure, restart) })
+ }
+ }
+}
+
+func runFilesSeedOrdinaryPreparation(t *testing.T, failure, restart string, mounted ...bool) {
+ t.Setenv("CARLOS_HOST_CLAIM", "1")
+ t.Setenv("CARLOS_HOST_CLAIM_ORIGINS", "1")
+ t.Setenv("CARLOS_MOVES_PHASE1", "")
+ var f *servingRootFixture
+ if len(mounted) > 0 && mounted[0] {
+
+ t.Cleanup(func() {
+ if err := os.RemoveAll("/data"); err != nil {
+ t.Error(err)
+ }
+ })
+ t.Cleanup(func() {
+ if err := os.RemoveAll("/var/lib/carlos-claims"); err != nil {
+ t.Error(err)
+ }
+ })
+ if failure == "restore-enospc" {
+ if err := os.Mkdir("/data", 0700); err != nil {
+ t.Fatal(err)
+ }
+ if err := unix.Mount("tmpfs", "/data", "tmpfs", unix.MS_NOSUID|unix.MS_NODEV, "size=1m"); err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() {
+ if err := unix.Unmount("/data", 0); err != nil {
+ t.Error(err)
+ }
+ })
+ }
+ state, err := openClaimServingParkAuthority()
+ if err != nil {
+ t.Fatal("production private store", err)
+ }
+ t.Cleanup(func() {
+ if err := state.Close(); err != nil {
+ t.Error(err)
+ }
+ })
+ f, _, _ = newServingLegacyRootSetup(t, false, "/data")
+ instances := filepath.Join(f.f.guard.DataDir, "instances")
+ if err := unix.Mount(instances, instances, "", unix.MS_BIND, ""); err != nil {
+ t.Fatal(err)
+ }
+ t.Cleanup(func() {
+ if err := unix.Unmount(instances, 0); err != nil {
+ t.Error(err)
+ }
+ })
+ if err := unix.Mount("", instances, "", unix.MS_BIND|unix.MS_REMOUNT|unix.MS_NOSYMFOLLOW|unix.MS_NOSUID|unix.MS_NODEV|unix.MS_NOEXEC, ""); err != nil {
+ t.Fatal(err)
+ }
+ // Use the production UID and mount checks in this isolated namespace.
+ f.p.ledger.state = state
+ f.p.ledger.fs = newServingParkFS(f.f.guard.DataDir, state)
+ source, err := os.Open(filepath.Dir(f.db))
+ if err != nil {
+ t.Fatal(err)
+ }
+ sourceID, err := servingParkDirID(source)
+ source.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ outside, err := os.Open(f.f.guard.DataDir)
+ if err != nil {
+ t.Fatal(err)
+ }
+ outsideID, err := servingParkDirID(outside)
+ outside.Close()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if sourceID.Device != outsideID.Device || sourceID.Mount == outsideID.Mount {
+ t.Fatal("fixture did not create same-device cross-mount boundary")
+ }
+ if err := os.Rename(f.db, filepath.Join(f.f.guard.DataDir, "old-flat-park")); !errors.Is(err, syscall.EXDEV) {
+ t.Fatalf("old destination must fail with EXDEV: %v", err)
+ }
+ } else {
+ f, _, _ = newServingLegacyRootSetup(t, false)
+ }
+ selected := plan12RestoreBaseline(t, f.f)
+ raw, err := f.f.data.GetVersion(t.Context(), f.f.bucket, selected.Files[0].Key, selected.Files[0].VersionID)
+ if err != nil {
+ t.Fatal(err)
+ }
+ bucket := f.f.data.(*leasetest.VersionedBucket)
+ key := strings.Replace(selected.Files[0].Key, "/g-src/", "/", 1)
+ bucket.Put(f.f.bucket, key, []byte("older invalid snapshot"))
+ version := bucket.Put(f.f.bucket, key, raw)
+ // Keep a valid LTX envelope so failures occur inside the production restore,
+ // after legacy retirement, rather than at selected-object decoding.
+ if failure == "invalid-sqlite" {
+ var spec ltx.FileSpec
+ if _, err := spec.ReadFrom(bytes.NewReader(raw)); err != nil {
+ t.Fatal(err)
+ }
+ copy(spec.Pages[0].Data, []byte("not a sqlite db!"))
+ var encoded bytes.Buffer
+ if _, err := spec.WriteTo(&encoded); err != nil {
+ t.Fatal(err)
+ }
+ version = bucket.Put(f.f.bucket, key, encoded.Bytes())
+ }
+ handler, _ := leasetest.NewS3Server(bucket)
+ server := httptest.NewServer(handler)
+ t.Cleanup(server.Close)
+ for key, value := range map[string]string{"AWS_ACCESS_KEY_ID": "test-key", "AWS_SECRET_ACCESS_KEY": "test-secret", "AWS_SESSION_TOKEN": "", "AWS_EC2_METADATA_DISABLED": "true", "AWS_CONFIG_FILE": filepath.Join(t.TempDir(), "absent"), "AWS_SHARED_CREDENTIALS_FILE": filepath.Join(t.TempDir(), "absent"), "CARLOS_S3_ENDPOINT": server.URL} {
+ t.Setenv(key, value)
+ }
+ sentinels := map[string]string{filepath.Join(filepath.Dir(f.db), "files", "payload"): "live files", filepath.Join(filepath.Dir(f.db), ".env"): "environment"}
+ for path, body := range sentinels {
+ if err = os.MkdirAll(filepath.Dir(path), 0700); err != nil {
+ t.Fatal(err)
+ }
+ if err = os.WriteFile(path, []byte(body), 0600); err != nil {
+ t.Fatal(err)
+ }
+ }
+ p := newServingPreparation(t, f, "boot", "controller")
+ var legacy servingParkRecord
+ var baseline *genselect.Plan
+ for i := 0; i < 2; i++ {
+ if i == 1 {
+ if failure == "invalid-sqlite" {
+ version = bucket.Put(f.f.bucket, key, raw)
+ }
+ f.f.now = f.f.now.Add(time.Minute)
+ }
+ p.events = nil
+ admitted, start := p.prepare(t)
+ if len(p.baseline.Files) != 1 || p.baseline.Files[0].Key != key || p.baseline.Files[0].VersionID != version {
+ t.Fatalf("wrong exact baseline: %+v", p.baseline)
+ }
+ if baseline != nil && failure != "invalid-sqlite" && !reflect.DeepEqual(baseline, p.baseline) {
+ t.Fatal("baseline changed")
+ }
+ baseline = p.baseline
+ p.enrollFailure = nil
+ restoreCtx, cancel := context.WithCancel(t.Context())
+ p.afterEnroll = nil
+ if i == 0 {
+ switch failure {
+ case "enrolled-refusal":
+ p.enrollFailure = os.ErrPermission
+ case "disk-after-enrollment":
+ p.enrollFailure = syscall.ENOSPC
+ case "cancel-after-enrollment":
+ p.afterEnroll = cancel
+ case "restore-enospc":
+ p.afterEnroll = func() {
+ err := os.WriteFile(filepath.Join(filepath.Dir(f.db), ".disk-full-witness"), make([]byte, 1<<20), 0600)
+ if !errors.Is(err, syscall.ENOSPC) {
+ t.Fatalf("small private tmpfs did not fill: %v", err)
+ }
+ }
+ }
+ }
+ restoreErr := start.Restore(restoreCtx)
+ if i == 0 && failure == "restore-enospc" {
+ if !errors.Is(restoreErr, syscall.ENOSPC) || !strings.Contains(restoreErr.Error(), "restore selected generation") {
+ t.Fatalf("not a production restore ENOSPC: %v", restoreErr)
+ }
+ if err := os.Remove(filepath.Join(filepath.Dir(f.db), ".disk-full-witness")); err != nil {
+ t.Fatal(err)
+ }
+ }
+ cancel()
+ failed := i == 0 && failure != "" && failure != "missing-marker"
+ if failed {
+ if restoreErr == nil {
+ t.Fatal("missed restore failure")
+ }
+ if p.enrollFailure != nil && !errors.Is(restoreErr, p.enrollFailure) {
+ t.Fatalf("wrong failure: %v", restoreErr)
+ }
+ if failure == "cancel-after-enrollment" && !errors.Is(restoreErr, context.Canceled) {
+ t.Fatalf("wrong cancellation: %v", restoreErr)
+ }
+ if failure == "invalid-sqlite" && !strings.Contains(restoreErr.Error(), "check restored database") {
+ t.Fatalf("failure before database check: %v", restoreErr)
+ }
+ if _, err = os.Stat(f.db); !os.IsNotExist(err) {
+ t.Fatalf("failed restore left database: %v", err)
+ }
+ temps, err := filepath.Glob(filepath.Join(filepath.Dir(f.db), ".restore-*"))
+ if err != nil || len(temps) != 0 {
+ t.Fatalf("restore scratch survived: %v %v", temps, err)
+ }
+ } else {
+ if restoreErr != nil {
+ t.Fatal("restore", restoreErr)
+ }
+ if got := mwReadDB(t, f.db); got != "selected recovery baseline" {
+ t.Fatal(got)
+ }
+ marker, err := hostbox.ReadGenerationMarker(f.db + ".gen")
+ if err != nil || marker != admitted.Claim.Holder.Generation {
+ t.Fatalf("marker %q %v", marker, err)
+ }
+ if i == 0 && failure == "missing-marker" {
+ if err = os.Remove(f.db + ".gen"); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+ want := []string{"select", "stop", "lease", "admit", "prelaunch", "recover", "state", "enrolled"}
+ if f.f.guard.DataDir == "/data" {
+ want = append([]string{"prerequisites"}, want[3:]...)
+ }
+ if !reflect.DeepEqual(p.events, want) {
+ t.Fatalf("preparation order: %v", p.events)
+ }
+ current, err := servingWriterIdentity(f.f.read(), f.actor.Box, f.actor.Region)
+ if err != nil {
+ t.Fatal(err)
+ }
+ slot, err := f.p.ledger.slot(servingParkAuthorization{Successor: current})
+ if err != nil {
+ t.Fatal(err)
+ }
+ record, err := f.p.ledger.read(slot.ID)
+ if err != nil || !record.Complete || !record.Authorization.Legacy {
+ t.Fatalf("legacy slot: %+v %v", record, err)
+ }
+ if i == 0 {
+ legacy = record
+ } else if !reflect.DeepEqual(legacy, record) {
+ t.Fatal("never-launched restore displaced legacy park or changed deadline")
+ }
+ if i == 0 && restart != "same-controller" {
+ // Lose only the controller's lease and memory. Leave its retry and
+ // prelaunch evidence unsettled for the next controller's stop path.
+ attempt := p.wake.get(recoveryHost)
+ if err = p.wake.release(t.Context(), recoveryHost, attempt); err != nil {
+ t.Fatal(err)
+ }
+ // This is ordinary retry recovery, not Task 1R's future seed rerun.
+ nextBoot := "boot"
+ if restart == "reboot" {
+ nextBoot = "boot-2"
+ }
+ next := newServingPreparation(t, f, nextBoot, "controller-2")
+ err = next.runtime.Prerequisites(t.Context(), registry.Route{Host: recoveryHost, DBPath: f.db}, f.f.read(), recoveryPolicy)
+ if !errors.Is(err, hostbox.ErrChanged) {
+ t.Fatalf("retry settlement: %v", err)
+ }
+ if r := f.f.retry(); r.State != "open" || r.Attempt != nil || r.AttemptsReserved != 1 {
+ t.Fatalf("settlement refunded/stranded: %+v", r)
+ }
+ p = next
+ } else {
+ if err = p.runtime.FailedAdmission(t.Context(), admitted, errors.New("restore-only witness")); err != nil {
+ t.Fatal("settlement", err)
+ }
+ }
+ }
+ for suffix, want := range map[string]string{"": "legacy bytes", "-wal": "legacy WAL", "-shm": "legacy SHM"} {
+ body, err := os.ReadFile(filepath.Join(f.f.guard.DataDir, "instances", ".serving-parked", legacy.Authorization.ID, filepath.Base(f.db)+suffix))
+ if err != nil || string(body) != want {
+ t.Fatalf("park bytes %q %v", body, err)
+ }
+ }
+ for path, want := range sentinels {
+ body, err := os.ReadFile(path)
+ if err != nil || string(body) != want {
+ t.Fatalf("unrelated bytes: %s %v", path, err)
+ }
+ }
+ after, err := f.f.data.GetVersion(t.Context(), f.f.bucket, key, version)
+ if err != nil || string(after) != string(raw) {
+ t.Fatal("remote baseline changed", err)
+ }
+ if r := f.f.retry(); r.State != "open" || r.Attempt != nil || r.AttemptsReserved != 2 {
+ t.Fatalf("final retry: %+v", r)
+ }
+ if restart != "same-controller" && len(f.f.kernel.recovered) == 0 {
+ t.Fatal("restart did not recover original launch tuple")
+ }
+}
+
+func TestFilesSeedLegacyStopRefusesForeignWriterBeforeRoot(t *testing.T) {
+ f, _, _ := newServingLegacyRootSetup(t, false)
+ o, err := hostclaim.Update(t.Context(), f.f.claims, f.f.read(), func(c *hostclaim.Claim) error { c.Holder.WriterBox = "foreign-box"; return nil })
+ if err != nil {
+ t.Fatal(err)
+ }
+ calls := 0
+ a := ClaimAssembly{Claims: f.f.claims, collectStop: func(context.Context, StopRequest, StopDrainer) (StopResult, error) { calls++; return StopResult{}, nil }}
+ local := claimStopLocal{Actor: f.actor, BootID: "boot", ControllerID: "controller", PID: 77, ProcessStart: "start"}
+ before, _ := f.disk.Names()
+ _, err = a.collectCapturedStop(t.Context(), o, local, claimLaunchOwner{BootID: local.BootID, ControllerID: local.ControllerID}, false)
+ if !errors.Is(err, hostbox.ErrChanged) || calls != 0 {
+ t.Fatalf("foreign legacy writer: calls %d error %v", calls, err)
+ }
+ after, _ := f.disk.Names()
+ if !reflect.DeepEqual(before, after) {
+ t.Fatal("foreign legacy stop wrote private evidence")
+ }
+}
+
+func TestServingParkNestedBindMount(t *testing.T) {
+ if !plan14RootFixture(t) {
+ return
+ }
+ for _, failure := range []string{"", "enrolled-refusal", "missing-marker", "cancel-after-enrollment", "disk-after-enrollment", "invalid-sqlite", "restore-enospc"} {
+ for _, restart := range []string{"same-controller", "controller", "reboot"} {
+ t.Run(failure+"/"+restart, func(t *testing.T) { runFilesSeedOrdinaryPreparation(t, failure, restart, true) })
+ }
+ }
+}
diff --git a/internal/host/claimservinglegacyroot_linux_test.go b/internal/host/claimservinglegacyroot_linux_test.go
index 12953f965..ccecb2bd8 100644
--- a/internal/host/claimservinglegacyroot_linux_test.go
+++ b/internal/host/claimservinglegacyroot_linux_test.go
@@ -20,11 +20,21 @@ import (
"github.com/carlosframework/platform/internal/hostguard"
"github.com/carlosframework/platform/internal/lease"
"github.com/carlosframework/platform/internal/lease/leasetest"
+ "github.com/carlosframework/platform/internal/provision"
)
-func newServingLegacyRootFixture(t *testing.T, empty bool, beforeAdmission ...func(*servingRootFixture, *ClaimStopCoordinator, StopRequest)) (*servingRootFixture, ClaimLaunchInputs) {
+func newServingLegacyRootSetup(t *testing.T, empty bool, dataRoot ...string) (*servingRootFixture, ClaimLaunchInputs, ClaimStopCoordinator) {
t.Helper()
f, _, _, in := servingRetirementFixture(t)
+ if len(dataRoot) != 0 {
+ f.guard.DataDir = dataRoot[0]
+ in.DBPath = provision.NestedDBPath(f.guard.DataDir, recoveryHost)
+ for _, dir := range []string{filepath.Join(f.guard.DataDir, "hostguard"), filepath.Dir(in.DBPath)} {
+ if err := os.MkdirAll(dir, 0700); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
if !empty {
_, err := hostclaim.Update(t.Context(), f.claims, f.read(), func(c *hostclaim.Claim) error {
c.Holder.WriterBox = "box-1"
@@ -59,6 +69,23 @@ func newServingLegacyRootFixture(t *testing.T, empty bool, beforeAdmission ...fu
fx.p.root = ClaimRootHTTP{Roots: map[string]string{"data": f.guard.DataDir}, Journals: journals, Stop: ClaimStopHTTP{Coordinator: coordinator, Authenticate: func(context.Context, net.Conn) (StopPeer, error) { return f.peer, nil }}, InventoryActor: func(context.Context) (hostbox.Actor, error) { return fx.actor, nil }, RequireNewWork: func(context.Context) error { return nil }}
fx.install(f.read())
coordinator.RetainNoProcess = fx.p.retainLegacyNoProcess
+ if !empty {
+ for suffix, body := range map[string]string{"": "legacy bytes", "-wal": "legacy WAL", "-shm": "legacy SHM"} {
+ if err := os.WriteFile(fx.db+suffix, []byte(body), 0600); err != nil {
+ t.Fatal(err)
+ }
+ }
+ }
+ left, right := net.Pipe()
+ fx.conn = left
+ t.Cleanup(func() { left.Close(); right.Close() })
+ return fx, in, coordinator
+}
+
+func newServingLegacyRootFixture(t *testing.T, empty bool, beforeAdmission ...func(*servingRootFixture, *ClaimStopCoordinator, StopRequest)) (*servingRootFixture, ClaimLaunchInputs) {
+ t.Helper()
+ fx, in, coordinator := newServingLegacyRootSetup(t, empty)
+ f := fx.f
o := f.read()
h := o.Claim.Holder
req := StopRequest{Region: "ie", Host: o.Host, Incarnation: h.Incarnation, InstallationStamp: h.Stamp, Box: "box-1", Enrollment: "enrollment", BootID: "boot", ControllerID: "controller", ExpectedClaimETag: o.ETag, ExpectedDirectoryRevision: "7"}
@@ -83,16 +110,6 @@ func newServingLegacyRootFixture(t *testing.T, empty bool, beforeAdmission ...fu
}
fx.install(admitted)
in.Admission, in.Empty = admission, empty
- if !empty {
- for suffix, body := range map[string]string{"": "legacy bytes", "-wal": "legacy WAL", "-shm": "legacy SHM"} {
- if err := os.WriteFile(fx.db+suffix, []byte(body), 0600); err != nil {
- t.Fatal(err)
- }
- }
- }
- left, right := net.Pipe()
- fx.conn = left
- t.Cleanup(func() { left.Close(); right.Close() })
return fx, in
}
diff --git a/internal/host/claimservingpark_linux_test.go b/internal/host/claimservingpark_linux_test.go
index 3af4ca23d..976cb82e9 100644
--- a/internal/host/claimservingpark_linux_test.go
+++ b/internal/host/claimservingpark_linux_test.go
@@ -249,7 +249,7 @@ func TestServingParkLedgerExpiryAndIdentity(t *testing.T) {
if err := os.Rename(path, path+".saved"); err != nil {
t.Fatal(err)
}
- if err := os.WriteFile(path, []byte("replacement"), 0600); err != nil {
+ if err := os.WriteFile(path, []byte("victim"), 0600); err != nil {
t.Fatal(err)
}
if err := os.Rename(path+".saved", filepath.Join(t.TempDir(), "original")); err != nil {
diff --git a/internal/host/claimwakestop.go b/internal/host/claimwakestop.go
index a66b71cd0..529a149db 100644
--- a/internal/host/claimwakestop.go
+++ b/internal/host/claimwakestop.go
@@ -313,9 +313,9 @@ func (a ClaimAssembly) collectCapturedStop(ctx context.Context, captured hostcla
req := StopRequest{Region: actor.Region, Recover: recovering, Host: observed.Host, Incarnation: holder.Incarnation, InstallationStamp: holder.Stamp, WriterSession: holder.Session, OperationID: claim.Op.ID, Phase: claim.Op.Phase, ObligationID: obligation, Box: actor.Box, Enrollment: actor.Enrollment, BootID: owner.BootID, ControllerID: owner.ControllerID, ExpectedClaimETag: observed.ETag, ExpectedDirectoryRevision: directoryRevision}
target := hostguard.FenceTarget{Region: req.Region, Host: req.Host, Incarnation: req.Incarnation, Stamp: req.InstallationStamp, Session: req.WriterSession, Op: req.OperationID, Phase: req.Phase, ObligationID: req.ObligationID, Box: req.Box}
if zero && claim.Op.Kind == hostclaim.KindServing {
- // A created incarnation names no writer box or admission; its
- // authority is the executor, and root checks the never-admitted holder.
- if claim.Op.Exec != actor.Box || holder.WriterBox != "" || len(holder.Admissions) != 0 {
+ // A never-admitted holder may be a new creator or an adopted legacy
+ // writer on this box. Root independently proves that no process remains.
+ if claim.Op.Exec != actor.Box || (holder.WriterBox != "" && holder.WriterBox != actor.Box) || len(holder.Admissions) != 0 {
return StopResult{}, hostbox.ErrChanged
}
} else if !hostguard.StopAuthorityMatches(claim, target) {
diff --git a/internal/provision/testdata/plan14-root-reads-seed.tsv b/internal/provision/testdata/plan14-root-reads-seed.tsv
index 235f17ca5..28581a042 100644
--- a/internal/provision/testdata/plan14-root-reads-seed.tsv
+++ b/internal/provision/testdata/plan14-root-reads-seed.tsv
@@ -1,4 +1,4 @@
-# Files seed checkpoint 6 overlay; pending Paul’s census ruling.
+# Files seed checkpoint 7 overlay; pending Paul’s census ruling.
# Generation, legacy and empty-bootstrap root authority have test callers only; production binding remains refused.
# Load after zddr4 and its explicit seal check. No legacy fingerprints are added or removed.
# Every classification below is rev3=PROPOSED-seed, not an approved disposition.
@@ -75,7 +75,7 @@ internal/host/claimservingparkroot_linux.go validateServingRootHistory authority
internal/host/claimservingpark_records.go servingParkRecord.activeAuthorization authority C,J4 rev3=PROPOSED-seed; last persisted continuation authorization or immutable original authorization; no source or deadline changes fe168fe52317e45ce70c3e97a9bc2c52d2409328ab4844a8d2a34fc4babba36d
internal/host/claimservingpark_records.go servingParkLedger.continueLocked authority C,J4 rev3=PROPOSED-seed; namespace/host-protected slot and record locks; pending original retirement, absent source and retained parked identities before durable bounded continuation 87f297a924f7f153624304233218d8b5548a016fc7dbf0f7cb8dedb789324009
internal/host/claimservingpark_records.go servingParkLedger.replaceLocked authority C,J4 rev3=PROPOSED-seed; completed incumbent record under its record lock; resumes its recorded deletion cause before slot replacement 3e67316e959c9a901c32a2a61fda4e6ea5e92fe51bdb7fdb5f5c53d9d5960aad
-@coverage internal/host d9e2304b55fd9f7653139cc17d2254ef7ec730b2e8c812a9c8f343603f2f6aa6
+@coverage internal/host 2d65820f0366a1ee92490ef9af8888cfda33292f5a5367d35d353e320f031a85
@coverage internal/hostbox d48f1a63963edb2551230477c8162f7ac182761c80571c3d753fe19dd7acacba
@coverage internal/hostguard 94912485e1e5fad4bad56985dd48ede776cfcb5fe18e0f4a59953584ddab06c0
internal/host/claimservinglegacy.go servingLegacyKey authority C,J4 rev3=PROPOSED-seed; box, host and incarnation derive private stop and consumption keys; never a caller path 93c10e40202ab23986b54a366edd7abfab9f8d942736305b5732e8741e96f3ab
@@ -120,3 +120,4 @@ internal/hostbox/servingpark_transport.go journalServingParkV4 authority C,J4 re
internal/hostbox/servingpark_transport.go servingParkResponse authority C,J4 rev3=PROPOSED-seed; typed retirement reply and exact requested successor; missing or mismatched result stays uncertain fc8602dfe28b78ee04809509b1357778777b26c191b089a47d70defbca8ffd88
internal/hostbox/servingpark_transport.go validateServingParkRequest authority C,J4 rev3=PROPOSED-seed; v4 envelope, nonce, digest and identities; rejects mixed operation fields and unsupported pressure before any effect 2c87ae130afc670925c87f52b9688fdc24d940d0a5fcdbf09667df2b662f7f59
internal/host/claimservingpark_transport.go var openClaimServingParkAuthority authority C,J4 rev3=PROPOSED-seed; descriptor-relative private rootstate at /var/lib/carlos-claims/authority/serving-parks with creation enabled and public reads disabled file:Open@78 7a1db088a7240e6a65ba0bed96fc5f973a69f70c2273c4475c486a6e0359fb23
+internal/host/claimwakestop.go ClaimAssembly.collectCapturedStop authority C,G,J4 rev3=PROPOSED-seed; fresh claim and verified local stop identity; session-zero legacy holder may name only the local writer box, with no admissions; root independently proves and retains no-process evidence file:Exec@2994,file:Read@605 d4cd80663dfae5f9737a74e319d3423f4ef853e1843666b02b4cd2341e869d72