package host import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "errors" "path/filepath" "github.com/carlosframework/platform/internal/activator" "github.com/carlosframework/platform/internal/cgroup" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/hostwrite" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/release" ) type ClaimRoleAssembly struct { Guard hostguard.Guard Claims release.Store // Root is the reservation-bearing isolated root launch protocol (claim // Plan 11, R10). Nil keeps every claimed launch refused at Preflight. prepareGroup func(string, cgroup.Limit) (*cgroup.Prepared, hostbox.LaunchGroup, error) Root activator.RootRoleLauncher Manager *cgroup.Manager Journal *hostbox.LaunchJournal Inventory *hostbox.StopInventoryDB ManagedRoots map[string]string Identity hostbox.IdentityProvider BootID, ControllerID string } func claimContainmentName(target hostguard.FenceTarget) string { identity := struct { Host, Incarnation string Stamp, Session uint64 Box, Enrollment, Boot, Controller string }{target.Host, target.Incarnation, target.Stamp, target.Session, target.Box, target.Enrollment, target.BootID, target.ControllerID} raw, _ := json.Marshal(identity) sum := sha256.Sum256(raw) return cgroup.ClaimContainmentName(hex.EncodeToString(sum[:24])) // the one format (merge item 8) } // launchTarget binds the observed session to this controller's verified tuple. func (a *ClaimRoleAssembly) launchTarget(ctx context.Context, o hostclaim.Observed) (hostguard.FenceTarget, error) { if a == nil || a.BootID == "" || a.ControllerID == "" || o.Claim == nil || o.Claim.Holder == nil { return hostguard.FenceTarget{}, hostwrite.ErrUnavailable } actor, err := a.Identity.Verify(ctx) if err != nil { return hostguard.FenceTarget{}, err } h := o.Claim.Holder return hostguard.FenceTarget{Region: actor.Region, Host: o.Host, Incarnation: h.Incarnation, Stamp: h.Stamp, Session: h.Session, Op: o.Claim.Op.ID, Phase: o.Claim.Op.Phase, Box: actor.Box, Enrollment: actor.Enrollment, BootID: a.BootID, ControllerID: a.ControllerID, ClaimETag: o.ETag, DirectoryRevision: actor.Binding, RosterRevision: actor.Revision}, nil } // Preflight admits a launch before retry reservation, admission or state // preparation only while root speaks the complete isolated launch protocol. // Every other answer, including no answer, is the isolation refusal. func (a *ClaimRoleAssembly) Preflight(ctx context.Context, _ ClaimLaunchInputs) error { if a == nil || a.Root == nil { return activator.ErrClaimIsolationUnavailable } protocol, err := a.Root.Capability(ctx) if err != nil { return errors.Join(activator.ErrClaimIsolationUnavailable, err) } if protocol != hostbox.RoleLaunchProtocol { return activator.ErrClaimIsolationUnavailable } return nil } // PreflightLaunch refuses, before admission, selected inputs the isolated // unit cannot carry. The runtime's prerequisites call it after Select. func (a *ClaimRoleAssembly) PreflightLaunch(host string, in ClaimLaunchInputs) error { if a == nil || a.Root == nil { return activator.ErrClaimIsolationUnavailable } if a.ManagedRoots["data"] != "/data" { return hostbox.ErrRoleLaunchShape } if len(in.Roles) != 1 || in.Roles[0] != "main" { return hostbox.ErrUnsupported } if !filepath.IsAbs(in.Executable) || filepath.Clean(in.Executable) != in.Executable || !claimHexDigest(in.ExecutableSHA256) || in.Identity.Config == "" || in.Identity.Release == "" { return hostbox.ErrRoleLaunchShape } paths, err := claimIsolatedPaths(a.ManagedRoots, host, in) if err != nil { return err } if in.Unit != nil { return claimBoundInputMatch(host, in) } // The same validator startIsolated runs on the built command. _, socket, db, err := activator.IsolatedArgv(host, paths.LaunchDir, paths.RunDir, in.Args, in.Env) if err != nil { return err } if db != in.DBPath || socket != provision.NestedSocket(host) { return hostbox.ErrRoleLaunchShape } return nil } // claimIsolatedLaunch is where main's isolated run unit finds a host: its // instance directory (the only data the unit binds), its run directory, its // stdout socket and its slice tier. type claimIsolatedLaunch struct { LaunchDir, RunDir, LogSocket string MemoryMax int64 } // claimIsolatedPaths admits only the nested layout's database, directly in // the host's instance directory, and a platform tier. func claimIsolatedPaths(roots map[string]string, host string, in ClaimLaunchInputs) (claimIsolatedLaunch, error) { data := roots["data"] if !filepath.IsAbs(data) || !provision.ValidHostShape(host) || !filepath.IsAbs(in.DBPath) || filepath.Clean(in.DBPath) != in.DBPath || (in.Limit.MemoryMax != cgroup.TierDefault.MemoryMax && in.Limit.MemoryMax != cgroup.TierChildren.MemoryMax) { return claimIsolatedLaunch{}, hostbox.ErrRoleLaunchShape } dir := provision.InstanceDir(data, host) if filepath.Dir(in.DBPath) != dir { return claimIsolatedLaunch{}, hostbox.ErrRoleLaunchShape } return claimIsolatedLaunch{LaunchDir: dir, RunDir: provision.InstanceRunDir(host), LogSocket: provision.LogSocket(host, "run"), MemoryMax: in.Limit.MemoryMax}, nil } // PreflightKind checks root's installed verifier before any attempt/admission. // The existing RootRoleLauncher signatures remain unchanged. func (a *ClaimRoleAssembly) PreflightKind(ctx context.Context, kind string) error { if kind == hostclaim.KindServing { return nil } client, ok := a.Root.(interface { KindCapability(context.Context, string) error }) if !ok { return activator.ErrClaimIsolationUnavailable } return client.KindCapability(ctx, kind) }