package hostbox import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "github.com/carlosframework/platform/internal/hostguard" "math" "slices" ) type ProducerCompletion struct { Count uint64 Digest string } func (c ProducerCompletion) Valid() bool { if c.Count == 0 { return c.Digest == "" } b, err := hex.DecodeString(c.Digest) return err == nil && len(b) == sha256.Size } // CompleteProducer atomically removes a joined producer and extends its durable // completion chain. The previous value permits replay after a lost commit reply. func (j *LaunchJournal) CompleteProducer(ctx context.Context, t hostguard.FenceTarget, id string, previous ProducerCompletion) (ProducerCompletion, error) { if j == nil { return previous, ErrUnsupported } if j.remote != nil { out, err := journalCall(ctx, j.remote, JournalRequest{Op: "complete", Target: t, Producer: id, Previous: previous}) return out.Completion, err } if !component(id) || !previous.Valid() || previous.Count == math.MaxUint64 { return previous, ErrTypedInventory } raw, _ := json.Marshal(struct { Previous ProducerCompletion ID string }{previous, id}) digest := sha256.Sum256(raw) next := ProducerCompletion{Count: previous.Count + 1, Digest: hex.EncodeToString(digest[:])} err := j.mutate(ctx, t, func(in *LaunchInventory) error { i := slices.Index(in.ProducerIDs, id) if i < 0 && in.Completed == next { return nil } if i < 0 || in.Completed != previous { return ErrChanged } in.ProducerIDs = slices.Delete(in.ProducerIDs, i, i+1) in.Completed = next return nil }) if err != nil { return previous, err } return next, nil } func (j *LaunchJournal) RecordProducer(ctx context.Context, t hostguard.FenceTarget, id string) error { if j == nil { return ErrUnsupported } if j.remote != nil { out, err := journalCall(ctx, j.remote, JournalRequest{Op: "producer", Target: t, Producer: id}) _ = out return err } if !component(id) { return ErrTypedInventory } return j.mutate(ctx, t, func(in *LaunchInventory) error { for _, old := range in.ProducerIDs { if old == id { return ErrTypedInventory } } in.ProducerIDs = append(in.ProducerIDs, id) return nil }) }