Documentation
¶
Overview ¶
Package database provides SQLite-backed storage for servers, manifests, and approval records. Manifest content is append-only: the only mutation ever performed on an existing manifest row is a state transition — see Store.UpdateManifestState.
Index ¶
- Constants
- Variables
- type Approval
- type ManifestRecord
- type OutboxRow
- type SQLiteStore
- func (s *SQLiteStore) Close() error
- func (q SQLiteStore) CreateServer(ctx context.Context, name, endpoint string) (*Server, error)
- func (q SQLiteStore) DueNotifications(ctx context.Context, now time.Time, maxAttempts, limit int) ([]OutboxRow, error)
- func (q SQLiteStore) EnqueueNotification(ctx context.Context, eventType string, manifestID int64) (int64, error)
- func (q SQLiteStore) GetApprovedManifest(ctx context.Context, serverID int64) (*ManifestRecord, error)
- func (q SQLiteStore) GetManifestByHash(ctx context.Context, serverID int64, hash string) (*ManifestRecord, error)
- func (q SQLiteStore) GetManifestByID(ctx context.Context, id int64) (*ManifestRecord, error)
- func (q SQLiteStore) GetServerByID(ctx context.Context, id int64) (*Server, error)
- func (q SQLiteStore) GetServerByName(ctx context.Context, name string) (*Server, error)
- func (q SQLiteStore) InsertApproval(ctx context.Context, a *Approval) (int64, error)
- func (q SQLiteStore) InsertManifest(ctx context.Context, m *ManifestRecord) (int64, error)
- func (q SQLiteStore) ListApprovalsForManifest(ctx context.Context, manifestID int64) ([]Approval, error)
- func (q SQLiteStore) ListPendingManifests(ctx context.Context) ([]ManifestRecord, error)
- func (q SQLiteStore) ListServers(ctx context.Context) ([]Server, error)
- func (q SQLiteStore) ListUndeliveredNotifications(ctx context.Context, minAttempts int) ([]OutboxRow, error)
- func (q SQLiteStore) MarkNotificationDelivered(ctx context.Context, id int64, now time.Time) error
- func (q SQLiteStore) MarkNotificationFailed(ctx context.Context, id int64, nextAttempt time.Time, lastError string) error
- func (q SQLiteStore) UpdateManifestState(ctx context.Context, id int64, newState string) error
- func (s *SQLiteStore) WithTx(ctx context.Context, fn func(Store) error) error
- type Server
- type Store
Constants ¶
const ( StatePending = "PENDING" StateApproved = "APPROVED" StateRejected = "REJECTED" StateSuperseded = "SUPERSEDED" )
Manifest lifecycle states.
const ( DecisionApproved = "APPROVED" DecisionRejected = "REJECTED" )
Approval decisions.
const ( EventManifestPending = "manifest.pending" EventManifestApproved = "manifest.approved" EventManifestRejected = "manifest.rejected" )
Notification event types written to the outbox. These strings are part of the webhook payload's public contract (the "event" field), so they are defined once here rather than spelled out at each call site.
Variables ¶
var ErrNotFound = errors.New("database: not found")
ErrNotFound is returned by every Store lookup when the row does not exist. There is exactly one absence convention: no lookup returns a nil object with a nil error, so callers use errors.Is rather than a nil check that is easy to forget and fails as a nil dereference when they do.
Functions ¶
This section is empty.
Types ¶
type ManifestRecord ¶
type OutboxRow ¶ added in v0.1.1
type OutboxRow struct {
ID int64
EventType string
ManifestID int64
Attempts int
NextAttemptAt time.Time
DeliveredAt *time.Time // nil until a 2xx has been observed
LastError string
CreatedAt time.Time
}
OutboxRow is one pending notification. It carries no payload: the event body is composed from the manifest at delivery time, so a change to the payload format never has to migrate rows already queued. ID doubles as the receiver's idempotency key, which is what makes at-least-once delivery safe to deduplicate on the far side.
type SQLiteStore ¶
type SQLiteStore struct {
// contains filtered or unexported fields
}
SQLiteStore is the top-level Store implementation backed by a *sql.DB.
func Open ¶
func Open(path string) (*SQLiteStore, error)
Open opens (creating if necessary) a SQLite database at path, applies the schema, and configures WAL journaling and foreign keys. The parent directory is created if it does not exist.
func (*SQLiteStore) Close ¶
func (s *SQLiteStore) Close() error
func (SQLiteStore) CreateServer ¶
func (SQLiteStore) DueNotifications ¶ added in v0.1.1
func (SQLiteStore) EnqueueNotification ¶ added in v0.1.1
func (SQLiteStore) GetApprovedManifest ¶
func (q SQLiteStore) GetApprovedManifest(ctx context.Context, serverID int64) (*ManifestRecord, error)
func (SQLiteStore) GetManifestByHash ¶
func (SQLiteStore) GetManifestByID ¶
func (q SQLiteStore) GetManifestByID(ctx context.Context, id int64) (*ManifestRecord, error)
func (SQLiteStore) GetServerByID ¶
func (SQLiteStore) GetServerByName ¶
func (SQLiteStore) InsertApproval ¶
func (SQLiteStore) InsertManifest ¶
func (q SQLiteStore) InsertManifest(ctx context.Context, m *ManifestRecord) (int64, error)
func (SQLiteStore) ListApprovalsForManifest ¶
func (SQLiteStore) ListPendingManifests ¶
func (q SQLiteStore) ListPendingManifests(ctx context.Context) ([]ManifestRecord, error)
func (SQLiteStore) ListServers ¶
func (SQLiteStore) ListUndeliveredNotifications ¶ added in v0.1.1
func (SQLiteStore) MarkNotificationDelivered ¶ added in v0.1.1
func (SQLiteStore) MarkNotificationFailed ¶ added in v0.1.1
func (SQLiteStore) UpdateManifestState ¶
type Store ¶
type Store interface {
CreateServer(ctx context.Context, name, endpoint string) (*Server, error)
GetServerByName(ctx context.Context, name string) (*Server, error)
GetServerByID(ctx context.Context, id int64) (*Server, error)
ListServers(ctx context.Context) ([]Server, error)
InsertManifest(ctx context.Context, m *ManifestRecord) (int64, error)
GetManifestByHash(ctx context.Context, serverID int64, hash string) (*ManifestRecord, error)
GetManifestByID(ctx context.Context, id int64) (*ManifestRecord, error)
GetApprovedManifest(ctx context.Context, serverID int64) (*ManifestRecord, error)
ListPendingManifests(ctx context.Context) ([]ManifestRecord, error)
UpdateManifestState(ctx context.Context, id int64, newState string) error
InsertApproval(ctx context.Context, a *Approval) (int64, error)
ListApprovalsForManifest(ctx context.Context, manifestID int64) ([]Approval, error)
// Notification outbox. EnqueueNotification is deliberately a plain
// INSERT with no network or filesystem work of its own, so it can be
// called inside the transaction that records a manifest without
// lengthening it measurably. Nothing else in this interface may be
// added to that transaction.
EnqueueNotification(ctx context.Context, eventType string, manifestID int64) (int64, error)
DueNotifications(ctx context.Context, now time.Time, maxAttempts, limit int) ([]OutboxRow, error)
MarkNotificationDelivered(ctx context.Context, id int64, now time.Time) error
MarkNotificationFailed(ctx context.Context, id int64, nextAttempt time.Time, lastError string) error
ListUndeliveredNotifications(ctx context.Context, minAttempts int) ([]OutboxRow, error)
WithTx(ctx context.Context, fn func(Store) error) error
}
Store is the persistence interface used by the approval workflow and API layers. Lookups return ErrNotFound when the row does not exist; no method returns a nil object with a nil error. Note what is deliberately absent: there is no method that updates a manifest's hash or canonical_json after insert. UpdateManifestState is the only mutation on an existing manifest row, so manifest content is physically immutable once written, not just immutable by convention.