package release import ( "bytes" "context" "crypto/ed25519" "errors" "os" ) // SystemPublication retains the exact CAS and signed bytes across retries. type SystemPublication struct { Component string `json:"component"` Channel string `json:"channel"` ID string `json:"id"` ExpectedETag string `json:"expected_etag"` Before []byte `json:"before,omitempty"` After []byte `json:"after"` } func (p SystemPublication) Verify(pub ed25519.PublicKey) (*SystemPointer, error) { next, err := verifySystemPointer(pub, p.After, p.Component, p.Channel) if err != nil { return nil, err } if next.ActivationID != p.ID || next.CancellationID != "" { return nil, ErrBadSystemPointer } return next, nil } func PublishSystemPublication(ctx context.Context, s Store, pub ed25519.PublicKey, p SystemPublication) (*SystemPointer, error) { next, err := p.Verify(pub) if err != nil { return nil, err } key := SystemPointerKey(p.Component, p.Channel) _, err = s.Put(ctx, key, p.After, p.ExpectedETag) if err == nil { return next, nil } // A failed response can follow a committed write. Only these exact signed // bytes prove completion; a different pointer never authorizes another CAS. raw, _, readErr := s.Get(ctx, key) if readErr == nil && bytes.Equal(raw, p.After) { return next, nil } return nil, err } // CancelSystemPublication competes with publication on its original pointer // ETag. The fence must be durable before the actor reservation is removed. func CancelSystemPublication(ctx context.Context, s Store, sg Signer, p SystemPublication) error { if p.ID == "" { return ErrBadSystemPointer } if _, err := p.Verify(sg.verifyPub()); err != nil { return err } before := &SystemPointer{Schema: 1, Component: p.Component, Channel: p.Channel} if len(p.Before) != 0 { var err error before, err = verifySystemPointer(sg.verifyPub(), p.Before, p.Component, p.Channel) if err != nil { return err } } before.CancellationID = p.ID fence, err := SignRootEnvelope(sg, before) if err != nil { return err } key := SystemPointerKey(p.Component, p.Channel) raw, etag, err := s.Get(ctx, key) if err == nil && bytes.Equal(raw, fence) { return nil } if err != nil && !errors.Is(err, os.ErrNotExist) { return err } if etag != p.ExpectedETag || (err == nil && bytes.Equal(raw, p.After)) { return ErrCASConflict } _, err = s.Put(ctx, key, fence, p.ExpectedETag) if err == nil { return nil } raw, _, readErr := s.Get(ctx, key) if readErr == nil && bytes.Equal(raw, fence) { return nil } return err }