// Package store persists the verification-service's replay-guard // `verification_decisions` row. The verifier reserves a decision_id // exactly once per successful chain check; the second attempt sees // ErrReplay and short-circuits to a 409-shaped verdict in the HTTP // layer. // // Phase 1 ships an in-memory impl; the Postgres schema is in // db/trust/migrations/0005_verification_decisions.*.sql and wired via a // skeleton Postgres type so the HTTP + chain layers stay unchanged when // the real pgxpool wiring lands. package store import ( "context" "errors" "time" ) // ErrReplay is returned by Reserve when decision_id has already been // stored by a prior successful chain check. var ErrReplay = errors.New("store: decision_id already used") // Decision is the single row inserted on a successful chain verdict. // Actors are the (approval_signer_key, transform_signer_key) pair; they // are useful for post-hoc audit (who, together, authorised this sink // write) and are returned to the client in the verify response. type Decision struct { DecisionID string ApprovalRef string TransformRef string PayloadHash string SinkTarget string ApprovalKey string TransformKey string CreatedAt time.Time } // Store is the persistence contract. Reserve MUST be atomic: a second // Reserve of the same decision_id, even if interleaved with the first, // MUST return ErrReplay and NOT overwrite the stored row. type Store interface { Reserve(ctx context.Context, d Decision) error Get(ctx context.Context, decisionID string) (Decision, bool, error) }