45 lines
1.5 KiB
Go
45 lines
1.5 KiB
Go
|
|
// 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)
|
||
|
|
}
|