package host import ( "context" "crypto/ecdh" "crypto/ed25519" "errors" "log" "os" "path/filepath" "runtime" "strings" "sync" "time" "github.com/carlosframework/platform/internal/activator" "github.com/carlosframework/platform/internal/appconfig" "github.com/carlosframework/platform/internal/boxid" "github.com/carlosframework/platform/internal/controlsock" "github.com/carlosframework/platform/internal/genselect" "github.com/carlosframework/platform/internal/hostactors" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostfloor" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/hostops" "github.com/carlosframework/platform/internal/hostwrite" "github.com/carlosframework/platform/internal/lease" "github.com/carlosframework/platform/internal/moves" "github.com/carlosframework/platform/internal/park" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/release" "github.com/carlosframework/platform/internal/version" ) // ClaimProductionMoveSources are the moves-owned C8 and regional contracts. // They do not grant local process or roster authority. Group D's public owner // and OperationAttempts signatures are unchanged. type ClaimProductionMoveSources interface { Open(context.Context, release.Store, lease.VersionedStore, hostbox.Actor, ClaimMoveReadAccess) (ClaimMovesOwners, ClaimFilesManifest, error) View(context.Context, hostclaim.Observed) (park.View, error) VerifyRetirement(context.Context, hostclaim.Observed, []lease.ObjectVersion) (string, time.Time, error) Close(context.Context) error } var claimMissingHomeLog sync.Once var claimMoveSources struct { sync.Mutex Source ClaimProductionMoveSources Parks *claimMoveParkSources } func RegisterProductionClaimMoveSources(s ClaimProductionMoveSources) error { claimMoveSources.Lock() defer claimMoveSources.Unlock() if s == nil || claimMoveSources.Source != nil { return hostwrite.ErrUnavailable } if claimMoveSources.Parks == nil { claimMoveSources.Parks = &claimMoveParkSources{View: s.View, Evidence: s} } claimMoveSources.Source = s return nil } // productionRosterRow is this box's verified roster row and what read it: // the release key, the box's host-actor enrollment, the signed directory, // and the directory's row for the box, whose enrollment and cloud instance // are the enrollment's. type productionRosterRow struct { home string pub ed25519.PublicKey enrollment hostfloor.Enrollment directory hostactors.Directory row hostactors.Box } // readProductionRosterRow is OpenClaimOwners' roster read, shared with the // moves park view's region (productionRosterRegion, Plan 5 Task 9a fix // round 1) so the two cannot disagree. func readProductionRosterRow(ctx context.Context, store release.Store, getenv func(string) string) (productionRosterRow, error) { var rr productionRosterRow pub, err := release.ParsePublicKey(getenv("CARLOS_RELEASE_PUBKEY"), "CARLOS_RELEASE_PUBKEY") if err != nil { return rr, err } enrollment, err := hostfloor.ReadEnrollment("/etc/carlos/host-actor.json") if err != nil { return rr, err } directory := hostactors.Directory{Store: store, PublicKey: pub, Bucket: getenv("CARLOS_DEPLOYMENT_BUCKET")} roster, err := directory.Read(ctx) if err != nil { return rr, err } row, ok := roster.Roster.Boxes[enrollment.Box] if !ok || row.Enrollment != enrollment.Enrollment || row.Cloud.InstanceID != enrollment.InstanceID { return rr, hostbox.ErrChanged } return productionRosterRow{home: roster.Roster.HomeRegion, pub: pub, enrollment: enrollment, directory: directory, row: row}, nil } // ProductionClaimOwnerProvider constructs claim-owned authority directly. Only // C8 and the moves regional snapshot/lease inventory come from the moves owner. type ProductionClaimOwnerProvider struct{} func (ProductionClaimOwnerProvider) OpenClaimOwners(ctx context.Context, store release.Store, r ClaimRuntimeResources) (ClaimOwnerBindings, error) { var out ClaimOwnerBindings if store == nil || r.Registry == nil || (r.Mode != "agent" && r.Mode != "adopt" && r.Mode != "instance-control") { return out, hostwrite.ErrUnavailable } rr, err := readProductionRosterRow(ctx, store, os.Getenv) if err != nil { return out, err } pub, enrollment, directory, row := rr.pub, rr.enrollment, rr.directory, rr.row certs, err := boxid.EmbeddedCerts() if err != nil { return out, err } dataBucket := os.Getenv("CARLOS_HIBERNATE_BUCKET") if dataBucket == "" { return out, hostwrite.ErrUnavailable } authority := productionClaimRoster{Directory: directory, DataBucket: dataBucket, FloorDir: filepath.Join(r.OptDir, "system", "guard")} _, homeRegion, err := claimRegionBinding(row.Region, os.Getenv("CARLOS_REGION"), rr.home) if err != nil { return out, err } if rr.home == "" { claimMissingHomeLog.Do(func() { log.Printf("host claim: signed roster has no home region; running with region %q and no home alias", row.Region) }) } out.Identity = hostbox.IdentityProvider{HomeRegion: homeRegion, AWSRegion: row.Cloud.Region, Account: row.Cloud.Account, Box: row.BoxID, Region: row.Region, Bucket: dataBucket, Namespace: row.Namespace, Certs: certs, Roster: authority, Evidence: productionBoxEvidence{store}} var actor hostbox.Actor if r.Mode == "instance-control" { out.cleanupEnrollment, err = verifyClaimCleanupEnrollment(ctx, out.Identity, directory) if err == nil { actor = out.cleanupEnrollment.actor } } else { actor, err = out.Identity.Verify(ctx) } if err != nil { return out, err } if actor.Enrollment != enrollment.Enrollment || actor.Cloud.InstanceID != enrollment.InstanceID { return out, hostbox.ErrChanged } running, err := (hostfloor.Disk{Dir: authority.FloorDir, Bucket: directory.Bucket, PublicKey: pub}).Running(version.Build, runtime.GOOS+"-"+runtime.GOARCH) if err != nil || !running.GuardReady { return out, errors.Join(err, hostfloor.ErrFloor) } for _, dir := range []string{filepath.Join(r.DataDir, ".hostexec"), "/run/carlos/hostexec"} { if err = prepareClaimParticipationDirectory(dir); err != nil { return out, err } } out.RetirementAuthority = &hostops.ProductionAuthority{Store: store, Directory: directory, Now: time.Now} if r.Mode == "instance-control" { out.EvidenceKey, err = readClaimEvidenceKey("/etc/carlos/host-evidence.key") if err != nil { return out, err } } out.Controllers = productionStopControllers{} out.Data, err = lease.NewVersionedS3(ctx, map[string]string{dataBucket: row.Cloud.Region}) if err != nil { return out, err } marker, err := newProductionClaimMarkers(ctx, dataBucket, row.Cloud.Region) if err != nil { return out, err } local := &productionClaimLocal{Resources: r, Identity: out.Identity, Directory: directory, Store: store, slots: map[string]*claimParticipation{}} out.Local = local backend := claimExecBackend(r, productionInstanceKeyReaders()) // The token mint is the only write this source makes. It runs under the // same guard and claim store the assembly's WorkSource uses. tokens := claimAdminTokens{Guard: hostguard.Guard{DataDir: r.DataDir, Claims: hostguard.StoreReader{Store: store}}, Claims: store, Verify: out.Identity.Verify, Local: out.Identity.Local(local), Policy: hostbox.Policy{Held: hostbox.RegistryHoldPolicy(r.Registry)}.Check, Now: time.Now} unitInputs := claimUnitInputReader(NewClaimRoleRootClient(controlsock.Path).UnitInputs) if r.Mode != "agent" { unitInputs = rootClaimUnitInputs } out.Serving.Launch = claimServingLaunchSource(backend, tokens, unitInputs) out.Serving.Markers = marker moveWakeEnabled := hostwrite.Enabled(claimEntryEnv(ctx)) && moves.Enabled(claimEntryEnv(ctx)) if moveWakeEnabled { out.moveRead = &claimMoveReadAccess{check: claimBoundIdentity(out.Identity.Verify, actor), actor: actor, route: r.Registry.Get, launch: claimMoveLaunchSource(backend)} } claimMoveSources.Lock() external := claimMoveSources.Source parks := claimMoveSources.Parks claimMoveSources.Unlock() if moveWakeEnabled { openProductionClaimMoveWake(ctx, store, actor, external, &out, claimBoundIdentity(out.Identity.Verify, actor)) } // The C8 files manifest provider (moves Task 6; platform-1e ruling of // 2026-10-12): registered under CARLOS_HOST_CLAIM alone, apart from the // moves owners, because Establish runs on every claim-on serving wake. closeFiles, err := openClaimFilesManifest(ctx, os.Getenv, productionClaimFilesSource(), store, out.Data, actor, &out, claimBoundIdentity(out.Identity.Verify, actor)) if err != nil { return out, err } // Region HA's relocation owners (claim Plan 11), with CARLOS_REGION_HA on. closeRelocation, err := openClaimRelocationOwners(ctx, os.Getenv, productionClaimRelocationSource(), store, out.Data, actor, &out) if err != nil { if closeFiles != nil { err = errors.Join(err, closeFiles(ctx)) } return out, err } bindClaimLegacyOwners(&out) out.InputPolicy = claimInputPolicy(r.Registry.Get, store, pub, backend, unitInputs) out.PolicyRefusals = append(out.PolicyRefusals, productionClaimUnitShapePolicy(r.Mode, r.Registry.Get)) out.MovesPolicy = MovesClaimPolicy(MovesRecordsFrom(store)) bindProductionClaimInputPolicy(claimEntryEnv(ctx), &out, MovesRecordsFrom(store)) regional := productionClaimRegional{Identity: out.Identity, Parks: parks, Local: local} out.Evidence = regional out.LeaseSet = regional.LeaseSet out.View = regional.View out.Authorize = regional.Authorize out.Close = func(ctx context.Context) error { err := local.Close(ctx) if closeRelocation != nil { err = errors.Join(err, closeRelocation(ctx)) } if closeFiles != nil { err = errors.Join(err, closeFiles(ctx)) } if external != nil && moveWakeEnabled { return errors.Join(err, external.Close(ctx)) } return err } return out, nil } // claimExecBackend is the claim path's launch backend: the box's // directories and cgroups, and the instance-key readers every launching // backend needs (final review I1). Without them a claim launch refused // every keyed replica ("cannot be checked against its home's key") and // could mint before the configuration was delivered; the agent's backend // is wired from the same pair. func claimExecBackend(r ClaimRuntimeResources, keys instanceKeyReaders) *activator.ExecBackend { eb := &activator.ExecBackend{DataDir: r.DataDir, OptDir: r.OptDir, Cgroups: r.Manager} keys.wire(eb) return eb } // claimInputPolicy is the serving claim's input policy: does a Work's // identity still name this host's inputs? A config-only identity is checked // against the bucket's rendered config, a release-only one against the // route's release, and a full one against the launch snapshot's identity. // The snapshot is the gateless read (SnapshotClaimLaunch): nothing is // launched here, so the replica gate does not run, no bucket is read for a // configured key, and the answer is hostbox.ErrChanged or nil, never a // launch refusal (final review I4). func claimInputPolicy(route func(string) (*registry.Route, error), store release.Store, pub ed25519.PublicKey, backend *activator.ExecBackend, readers ...claimUnitInputReader) func(context.Context, *hostclaim.Claim, hostbox.Selection, hostwrite.Identity) error { return func(ctx context.Context, c *hostclaim.Claim, s hostbox.Selection, id hostwrite.Identity) error { if c == nil || c.Holder == nil || c.Op.Kind != hostclaim.KindServing || s.Host != c.Host || s.Captured.Incarnation != c.Holder.Incarnation || s.Captured.Stamp != c.Holder.Stamp { return hostbox.ErrChanged } rt, err := route(s.Host) if err != nil { return err } if rt == nil { return hostbox.ErrChanged } var unit *hostbox.UnitLaunchInputs if len(readers) == 1 { unit, err = readers[0](ctx, *rt) if err != nil { return err } } if unit != nil { if id.Config != "" && id.Release == "" { if id.Config != unit.Program { return hostbox.ErrChanged } return nil } if id.Release != "" && id.Config == "" { return release.CheckRouteReleaseIdentity(ctx, store, pub, *rt, id) } snapshot, err := claimBoundSnapshot(ctx, backend, *rt, func(context.Context, registry.Route) (*hostbox.UnitLaunchInputs, error) { return unit, nil }) if err != nil { return err } if id != snapshot.Identity { return hostbox.ErrChanged } return nil } if id.Config != "" && id.Release == "" { var private *ecdh.PrivateKey if raw := os.Getenv("CARLOS_SECRETS_KEY"); raw != "" { private, err = appconfig.ParsePrivateKey(raw, "CARLOS_SECRETS_KEY") if err != nil { return err } } return appconfig.CheckRenderedIdentity(ctx, store, private, *rt, id) } if id.Release != "" && id.Config == "" { return release.CheckRouteReleaseIdentity(ctx, store, pub, *rt, id) } snapshot, err := backend.SnapshotClaimLaunch(ctx, *rt) if err != nil { return err } if id != snapshot.Identity { return hostbox.ErrChanged } return nil } } // bindClaimLegacyOwners fills the claims-off fallback. Claimed serving uses // out.Serving independently of moves and never dispatches through these fields. func bindClaimLegacyOwners(out *ClaimOwnerBindings) { legacy := claimLegacyRefusal{} out.Inputs = legacy out.AdmissionEvidence = legacy.AdmissionEvidence out.PublicationEvidence = legacy.PublicationEvidence } // claimLegacyRefusal is main's refusal for the held inputs, publication and // regional evidence adapters it never had. type claimLegacyRefusal struct{} func (claimLegacyRefusal) err() error { return &ClaimDependencyError{Contracts: []string{"moves held inputs, publication and regional evidence adapters"}} } func (r claimLegacyRefusal) Select(context.Context, registry.Route, hostclaim.Observed, activator.StartPolicy) (ClaimLaunchInputs, error) { return ClaimLaunchInputs{}, r.err() } func (r claimLegacyRefusal) Materialize(context.Context, hostclaim.Observed, ClaimLaunchInputs, hostwrite.Work) error { return r.err() } func (r claimLegacyRefusal) EmptyAuthority(context.Context, hostclaim.Observed, ClaimLaunchInputs) error { return r.err() } func (r claimLegacyRefusal) Publish(context.Context, hostclaim.Observed, ClaimLaunchInputs) error { return r.err() } func (r claimLegacyRefusal) AdmissionEvidence(context.Context, hostclaim.Observed, hostclaim.Admission, *genselect.Plan) error { return r.err() } func (r claimLegacyRefusal) PublicationEvidence(context.Context, hostclaim.Observed) error { return r.err() } type productionBoxEvidence struct{ Store release.Store } func (e productionBoxEvidence) Load(ctx context.Context, box string) ([]byte, error) { raw, _, err := e.Store.Get(ctx, boxid.Key(box)) return raw, err } type productionClaimRoster struct { Directory hostactors.Directory DataBucket, FloorDir string } func (p productionClaimRoster) Actor(ctx context.Context, box string) (hostbox.Actor, error) { var a hostbox.Actor o, err := p.Directory.Read(ctx) if err != nil { return a, err } b, ok := o.Roster.Boxes[box] if !ok || b.State != "active" || o.Roster.RequestLimitNS != int64(hostbox.RequestLimit) { return a, hostbox.ErrChanged } fenced, err := p.Directory.TerminationFence(ctx, o, box, time.Now()) if err != nil || fenced { return a, errors.Join(err, hostbox.ErrChanged) } disk := hostfloor.Disk{Dir: p.FloorDir, Bucket: p.Directory.Bucket, PublicKey: p.Directory.PublicKey} _, local, err := disk.Load() if err != nil { return a, err } remote, err := hostfloor.Read(ctx, p.Directory.Store, p.Directory.PublicKey, p.Directory.Bucket) if err != nil { return a, err } if local.Digest != remote.Digest || o.Roster.Publication != "published" || o.Roster.FloorSHA256 != remote.Digest { return a, hostfloor.ErrFloor } // The binding comes from this same verified read, so it names exactly the // row and deployment facts checked above. binding, ok := hostactors.BoxBinding(o.Roster, box) if !ok { return a, hostbox.ErrChanged } return hostbox.Actor{FleetHomeRegion: o.Roster.HomeRegion, ControlBucket: p.Directory.Bucket, Box: box, Enrollment: b.Enrollment, Revision: o.Roster.Revision, Region: b.Region, Bucket: p.DataBucket, Namespace: b.Namespace, Eligible: true, Binding: binding, Cloud: hostbox.CloudIdentity{Provider: b.Cloud.Provider, Account: b.Cloud.Account, Region: b.Cloud.Region, InstanceID: b.Cloud.InstanceID}}, nil } type productionClaimRegional struct { Now func() time.Time Identity hostbox.IdentityProvider Parks *claimMoveParkSources Local *productionClaimLocal } func (p productionClaimRegional) View(ctx context.Context, o hostclaim.Observed) (park.View, error) { if p.Parks == nil { return p.ordinaryView(ctx, o) } if p.Parks.View == nil { return park.View{}, &ClaimDependencyError{Contracts: []string{"moves Plan 5 exact-version generation and files-manifest view"}} } actor, err := p.Identity.Verify(ctx) if err != nil { return park.View{}, err } if o.Claim == nil { return park.View{}, park.ErrUnsafe } home, err := park.VerifiedHome(actor.FleetHomeRegion, actor.HomeRegion, actor.Region, o.Claim.Op.Intent["home_region"]) if err != nil { return park.View{}, err } v, err := p.Parks.View(ctx, park.HomeView(o, home)) if err != nil { return park.View{}, err } v.HomeRegion = home v.ArchivedIncarnations = claimArchivedLocations(o.Claim) v.RefusedLocations = map[string]error{} if _, err := claimLegacyIncarnation(o.Claim); err != nil { v.RefusedLocations["instances/"+o.Host] = err } if o.Claim.Holder == nil && len(o.Claim.RetiredIncarnations) == 0 { v.RefusedLocations["moves/"+o.Host] = claimRetirementDependency() } return v, nil } func (p productionClaimRegional) LeaseSet(ctx context.Context, bucket, scope string) ([]string, error) { a, err := p.Identity.Verify(ctx) if err != nil { return nil, err } if a.Bucket != bucket { return nil, hostbox.ErrChanged } return hostbox.ReplicaParkLeaseSet(ctx, bucket, scope) } func (p productionClaimRegional) VerifyRetirement(ctx context.Context, o hostclaim.Observed, v []lease.ObjectVersion) (string, time.Time, error) { if _, err := p.Identity.Verify(ctx); err != nil { return "", time.Time{}, err } return p.retirement(ctx, o, v) } func (p productionClaimRegional) Authorize(ctx context.Context, o hostclaim.Observed, ob hostclaim.Obligation) error { a, err := p.Identity.Verify(ctx) if err != nil { return err } ordinary := p.Parks == nil && ob.Kind == "park-generation" && strings.HasPrefix(ob.Op, "session:") if ordinary { if _, err := p.VerifyObligation(ctx, o, ob); err != nil { return err } } if o.Claim == nil || (!ordinary && ob.Op != o.Claim.Op.ID) || (ob.Owner != "box:"+a.Box && (ob.Owner != "region:"+ob.Region || !park.SameRegion(a.HomeRegion, ob.Region, a.Region))) || (ob.Box != "" && ob.Box != a.Box) || (ob.Region != "" && !park.SameRegion(a.HomeRegion, ob.Region, a.Region)) { return hostbox.ErrChanged } found := false for _, live := range o.Claim.Obligations { if live.ID == ob.ID && live.Owner == ob.Owner && live.Op == ob.Op { found = true } } if !found { return hostbox.ErrChanged } if ordinary { return nil } if p.Parks == nil && ob.Owner == "region:"+ob.Region && p.Local != nil && p.Local.assembly != nil && (p.Local.assembly.Guard.Root != nil || p.Local.assembly.Guard.FenceView != nil) { return p.authorizeOrdinaryRetirement(ctx, o, ob) } return p.Local.Prepare(ctx, o) } // claimAdminTokens authorizes a first wake's CARLOS_ADMIN_TOKEN mint. The // legacy launch preparation mints it before reading the env. The claim path // never runs that preparation, and SnapshotClaimLaunch refuses a missing // token, so selection would refuse every first wake. The mint is a host // write, so it runs only under the claim's current serving holder for this // box. The checks are the ones a selected "spawn" Work makes (hostbox // WorkSource): exact op, holder stamp, session and generation; quiet-after; // local authority; and the serving policy with a zero identity. Before // Admit there is no local hostguard record, so the write runs under // Guard.WithNoInstallation. After Admit it runs under Guard.With at the // holder's captured stamp. A record for any other incarnation or stamp // refuses the write. type claimAdminTokens struct { Guard hostguard.Guard Claims release.Store Verify func(context.Context) (hostbox.Actor, error) Local func(context.Context, *hostclaim.Claim, hostbox.Selection) error Policy func(context.Context, *hostclaim.Claim, hostbox.Selection, hostwrite.Identity) error Now func() time.Time } func (t claimAdminTokens) With(ctx context.Context, host string, effect func() error) error { if ctx == nil || effect == nil || t.Guard.DataDir == "" || t.Guard.Claims == nil || t.Claims == nil || t.Verify == nil || t.Local == nil || t.Policy == nil || t.Now == nil { return hostwrite.ErrUnavailable } o, err := hostclaim.Read(ctx, t.Claims, host) if err != nil { return err } if o.Absent || o.Claim == nil || o.Claim.Holder == nil || o.Claim.Host != host || o.Claim.Op.Kind != hostclaim.KindServing || o.Claim.Holder.Stamp == 0 { return hostbox.ErrChanged } actor, err := t.Verify(ctx) if err != nil { return err } c, h := o.Claim, o.Claim.Holder sel := hostbox.Selection{HomeRegion: actor.HomeRegion, FleetHomeRegion: actor.FleetHomeRegion, Host: host, Action: "spawn", Box: actor.Box, Region: actor.Region, Captured: hostguard.Captured{Incarnation: h.Incarnation, Stamp: h.Stamp}, Kind: c.Op.Kind, Op: c.Op.ID, Phase: c.Op.Phase, Generation: h.Generation, Session: h.Session} check := func(c *hostclaim.Claim) error { if c.Op.Kind != sel.Kind || c.Op.ID != sel.Op || c.Op.Phase != sel.Phase || c.Holder.Incarnation != sel.Captured.Incarnation || c.Holder.Stamp != sel.Captured.Stamp || c.Holder.Session != sel.Session || c.Holder.Generation != sel.Generation { return hostbox.ErrChanged } if until := park.QuietUntil(c.QuietAfter, actor.HomeRegion, sel.Region); !until.IsZero() && !t.Now().After(until) { return hostbox.ErrChanged } if err := t.Local(ctx, c, sel); err != nil { return err } return t.Policy(ctx, c, sel, hostwrite.Identity{}) } captured, err := t.Guard.Capture(host) if errors.Is(err, hostguard.ErrNoRecord) { return t.Guard.WithNoInstallation(ctx, host, sel.Captured.Incarnation, check, effect) } if err != nil { return err } if captured != sel.Captured { return hostbox.ErrChanged } return t.Guard.With(ctx, host, captured, check, effect) } // claimServingLaunchSource is the production serving launch source: the // authorized token initialization, then the read-only snapshot with the // replica gate (SelectClaimLaunch), then a declined box's launch gate // (declinedClaimGate). func claimServingLaunchSource(backend *activator.ExecBackend, tokens claimAdminTokens, readers ...claimUnitInputReader) func(context.Context, registry.Route) (ClaimServingLaunch, error) { return func(ctx context.Context, rt registry.Route) (ClaimServingLaunch, error) { if len(readers) != 0 { unit, err := readers[0](ctx, rt) if err != nil { return ClaimServingLaunch{}, err } if unit != nil { return claimBoundSnapshot(ctx, backend, rt, func(context.Context, registry.Route) (*hostbox.UnitLaunchInputs, error) { return unit, nil }) } } if err := ensureClaimLaunchToken(ctx, backend, rt, func(ctx context.Context, effect func() error) error { if layout, _ := backend.Layout(rt); layout == activator.LayoutNested { if err := tokens.prepareState(ctx, rt); err != nil { return err } } return tokens.With(ctx, rt.Host, effect) }); err != nil { return ClaimServingLaunch{}, err } snapshot, err := backend.SelectClaimLaunch(ctx, rt) // snapshot.InstanceKeySHA256 and InstanceKeySource are dropped here // until O6 threads them to the launch's adoption verb (plan ruling // 23; O6's obligation): a claim launch binds no control socket yet, // so nothing can report against them. if err == nil { err = declinedClaimGate(backend.OptDir, rt, snapshot) } return ClaimServingLaunch{Identity: snapshot.Identity, Executable: snapshot.Executable, ExecutableSHA256: snapshot.SHA256, Args: snapshot.Args, Env: snapshot.Env, Roles: snapshot.Roles, Version: snapshot.Version, Staged: snapshot.Staged, Limit: snapshot.Limit}, err } } // declinedClaimGate is a declined box's launch gate (failureHolds.gate) for // a claims-backed wake (Task 9, plan review #6). Task 1 makes a claims-on // box decline CARLOS_SWAP=overlap, so this is a declined box's wake, and its // launch runs the bytes the snapshot hashed from the live link (the launch // hashes the executable again before it execs). The snapshot names the // version from a second read of the link, so the link is resolved once more // here (release.ResolveLive, one read of the link and a hash of the file it // names): only a version whose bytes are the snapshot's is gated, and a // repoint between the snapshot's two reads is a changed claim // (hostbox.ErrChanged), never a start. With nothing honoured (every box but // a declined one with records or markers) it is nil and reads nothing. func declinedClaimGate(optDir string, rt registry.Route, s activator.ClaimLaunchSnapshot) error { h := heldHolds() if h == nil { return nil } id, err := release.ResolveLive(optDir, rt.Host) if err != nil { return err } if id.Version != s.Version || !strings.EqualFold(id.Digest, s.SHA256) { return hostbox.ErrChanged } return h.gate(rt, s.Version) }