package host import ( "context" "github.com/carlosframework/platform/internal/hostbox" "github.com/carlosframework/platform/internal/hostguard" ) type servingParkDisposition uint8 const ( servingParkRefuse servingParkDisposition = iota servingParkArtifactFree servingParkDiscard servingParkRetain ) // servingGenerationParkDisposition is a root-side decision made after source // capture and stop authentication. The database must exist to own the slot. // A prelaunch record alone proves no tenant started. Once a main-role launch // was intended, missing start evidence is ambiguous: StartedUnit is recorded // after the process may have run. Preserve that source and refuse retirement. // Task 0R's filesystem driver must persist this decision with its source // identities before either replacing the slot or discarding a restore. func servingGenerationParkDisposition(ctx context.Context, journal *hostbox.LaunchJournal, prior servingArtifactIdentity, receipts []hostguard.FenceReceipt, databasePresent bool) (servingParkDisposition, error) { if ctx == nil || journal == nil || prior.Host == "" || prior.Box == "" || prior.Region == "" || prior.Incarnation == "" || prior.Generation == "" || prior.Stamp == 0 || prior.Session == 0 || len(receipts) == 0 { return servingParkRefuse, hostbox.ErrChanged } launched := false for _, receipt := range receipts { t := receipt.Target if t.Host != prior.Host || t.Box != prior.Box || t.Region != prior.Region || t.Incarnation != prior.Incarnation || t.Stamp != prior.Stamp || t.Session != prior.Session || t.Op != "" || t.Phase != "" || t.ObligationID != "" || verifyRetirementReceipt(ctx, receipt) != nil { return servingParkRefuse, ErrClaimStopReceipt } } if databasePresent { t := receipts[0].Target records, err := journal.RootSessionLaunches(ctx, prior.Host, prior.Incarnation, prior.Stamp, prior.Session, prior.Box, t.Enrollment) if err != nil { return servingParkRefuse, err } if len(records) != 1 { return servingParkRefuse, hostbox.ErrChanged } record := records[0] matched := false for _, receipt := range receipts { matched = matched || hostbox.SameLaunchOwner(record.Target, receipt.Target) } if !matched { return servingParkRefuse, hostbox.ErrChanged } seen := make(map[string]bool) for _, reservation := range record.Reservations { if reservation.Role != "main" { continue } if reservation.ID == "" || seen[reservation.ID] || !hostbox.SameLaunchOwner(reservation.Target, record.Target) { return servingParkRefuse, hostbox.ErrChanged } seen[reservation.ID] = true if reservation.NeverIntended && record.WritersClosed && reservation.State == hostbox.UnitSettled && reservation.PID == 0 && reservation.ProcessStart == "" && reservation.Group.InvocationID == "" && reservation.Group.Device == 0 && reservation.Group.Inode == 0 { continue } if !hostbox.IsUnitReservation(reservation) || reservation.State != hostbox.UnitSettled || reservation.PID <= 0 || reservation.ProcessStart == "" || reservation.Group.InvocationID == "" || reservation.Group.Device == 0 || reservation.Group.Inode == 0 || reservation.Executable.Device == 0 || reservation.Executable.Inode == 0 { return servingParkRefuse, hostbox.ErrChanged } launched = true } } if !databasePresent { return servingParkArtifactFree, nil } if launched { return servingParkRetain, nil } return servingParkDiscard, nil }