Help us shape the future of Go! Take the 2026 Go Developer Survey

postgres

package
v0.5.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 3, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package postgres implements authoritative token persistence through explicit Foundry migrations and generated model queries. Construction performs no I/O.

Index

Constants

View Source
const (
	MigrationOrigin migrate.Origin = "foundry.tokens"
	CreateTokens    migrate.ID     = "000001_create_tokens"
	AddTokenState   migrate.ID     = "000002_token_device_refresh_history"
	// ValidateTokenState validates 000002's NOT VALID checks in a separate transaction.
	ValidateTokenState migrate.ID = "000003_validate_token_state"
	// CreateTickets stores hashed single-use handshake tickets per family.
	CreateTickets migrate.ID      = "000004_create_tickets"
	Introduced    migrate.Version = "v0.1.0"
)

Variables

This section is empty.

Functions

func Migrations

func Migrations() []migrate.Definition

Migrations returns immutable definitions for the schema selected by Config. Apply explicitly through the ordinary runner; no startup schema synchronization.

Types

type Backend

type Backend struct {
	// contains filtered or unexported fields
}

Backend borrows a pool. Mutations own a read-committed transaction; request verification and listing are single statements without one. Explicit In methods join verified caller transactions. It never retries writes or connects at construction.

func New

func New(db *database.DB, config Config) (*Backend, error)

New validates configuration and creates an adapter borrowing db.

func (*Backend) ConsumePendingIn

func (b *Backend) ConsumePendingIn(ctx context.Context, tx *database.Tx, address token.Address, expected token.Record) error

ConsumePendingIn deletes only the exact, still-live pending credential under its subject lock. The caller already holds model/factor locks. Pool ownership and schema restoration follow the shared savepoint boundary. Nothing commits.

func (*Backend) Create

func (b *Backend) Create(ctx context.Context, address token.Address, creation token.Creation) (token.Record, error)

Create atomically creates a token under the subject lock and enforces credential capacity.

func (*Backend) CreateBefore

func (b *Backend) CreateBefore(ctx context.Context, address token.Address, creation token.Creation, deadline temporal.DateTime, check func(context.Context, *database.Tx) error) (token.Record, error)

CreateBefore uses ordinary creation and rechecks the pending credential's deadline after insertion, before the transaction returns to commit. Expiry rolls back the inserted credential and all joined factor/consumption writes.

func (*Backend) CreateChecked

func (b *Backend) CreateChecked(ctx context.Context, address token.Address, creation token.Creation, check func(context.Context, *database.Tx) error) (token.Record, error)

CreateChecked runs check before credential locks and creates the token in the same transaction, without retries.

func (*Backend) IssueTicket

func (b *Backend) IssueTicket(ctx context.Context, address token.Address, identity model.Identity, family model.ID[token.Record], hash token.Digest, lifetime time.Duration, max int) (value.Optional[temporal.DateTime], error)

IssueTicket stores a ticket hash. It first locks the live family row, then, in a later statement whose snapshot sees every ticket committed by earlier lock holders, drops the family's expired tickets and the oldest beyond max-1 and inserts, so concurrent issuance for one family keeps the cap exact. A family that is revoked, expired or another subject's inserts nothing.

func (*Backend) List

func (b *Backend) List(ctx context.Context, address token.Address, identity model.Identity, limit int) ([]token.Record, error)

List reads the subject's live families with their current generation in one statement without locking the subject, so listing never blocks issuance, refresh or request authentication.

func (*Backend) Lookup

func (b *Backend) Lookup(ctx context.Context, address token.Address, hash token.Digest, touch bool) (value.Optional[token.Record], error)

Lookup is the per-request read path: one schema-qualified statement joins the generation, family and subject without a transaction or row lock, so parallel requests never serialize on the subject. Expiry is checked against the clock sampled after the read; a revocation committing concurrently applies from the next auth scope. Touch records activity with one conditional UPDATE that cannot revive a refreshed, revoked or expired generation.

func (*Backend) LookupFamily

func (b *Backend) LookupFamily(ctx context.Context, address token.Address, identity model.Identity, family model.ID[token.Record]) (value.Optional[token.Record], error)

LookupFamily reads the family's current generation in one statement without a lock, like request authentication, and omits a family that is not live.

func (*Backend) LookupWithin

func (b *Backend) LookupWithin(ctx context.Context, address token.Address, hash token.Digest, grace time.Duration) (value.Optional[token.Record], error)

LookupWithin also accepts the generation immediately before the current one while its successor is younger than grace and its own access is live.

func (*Backend) Prune

func (b *Backend) Prune(ctx context.Context, address token.Address, limit int) (uint64, error)

Prune deletes at most limit expired families of this address, with their generations and consumed digests, in one set-based statement. Candidates come from the family absolute-expiry and generation expiry indexes; the dead predicate is rechecked on each locked family against its current generation, and families locked by a concurrent refresh or revocation are skipped. Subject rows remain for issuance/revocation serialization.

func (*Backend) PruneTickets

func (b *Backend) PruneTickets(ctx context.Context, address token.Address, limit int) (uint64, error)

PruneTickets deletes at most limit expired tickets of this address. The candidate set is materialized once so its LIMIT bounds the delete.

func (*Backend) RedeemTicket

func (b *Backend) RedeemTicket(ctx context.Context, address token.Address, hash token.Digest) (value.Optional[token.TicketRecord], error)

RedeemTicket deletes the ticket in the statement that reads it, so concurrent redemptions have at most one success. A ticket of a family that is no longer live is left for pruning and reported absent, like an expired one.

func (*Backend) Refresh

func (b *Backend) Refresh(ctx context.Context, address token.Address, old, access, refresh token.Digest) (value.Optional[token.Record], error)

Refresh atomically rotates the live generation; replay, expiry or exhausted rotations remove the family and return omission.

func (*Backend) Revoke

func (b *Backend) Revoke(ctx context.Context, address token.Address, hash token.Digest) (bool, error)

Revoke removes the family matching the current or immediately previous access generation and reports whether it existed.

func (*Backend) RevokeAll

func (b *Backend) RevokeAll(ctx context.Context, address token.Address, identity model.Identity) (uint64, error)

RevokeAll atomically removes the subject's token families and returns the count.

func (*Backend) RevokeAllIn

func (b *Backend) RevokeAllIn(ctx context.Context, tx *database.Tx, address token.Address, identity model.Identity) (uint64, error)

RevokeAllIn returns a provisional count: only the caller's outer transaction can commit. It rejects a transaction from another pool and scopes schema changes inside a savepoint. The original standalone method reuses the same row logic.

func (*Backend) RevokeID

func (b *Backend) RevokeID(ctx context.Context, address token.Address, identity model.Identity, id model.ID[token.Record]) (bool, error)

RevokeID locks the subject and removes the identified token family, reporting whether it existed.

func (*Backend) RevokeOthers

func (b *Backend) RevokeOthers(ctx context.Context, address token.Address, identity model.Identity, keep model.ID[token.Record]) (uint64, error)

RevokeOthers removes every family of the subject except keep, under the subject lock. A missing subject has nothing to revoke.

func (*Backend) RevokeRefresh

func (b *Backend) RevokeRefresh(ctx context.Context, address token.Address, hash token.Digest) (value.Optional[model.Identity], error)

RevokeRefresh deletes the family of a current, previous or consumed refresh digest in one transaction, locking its subject before the family like every other writer. Expired families are removed as well.

type Config

type Config struct {
	Schema string
	Clock  clock.Clock
}

Config selects the same schema used by explicit migrations. Instances sharing a store need synchronized clocks; tests may inject a shared testkit.Clock.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns the public schema and system clock.

func (Config) Validate

func (c Config) Validate() error

Validate requires a valid SQL schema and a non-nil clock.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL