Documentation
¶
Overview ¶
Package postgres implements authoritative token persistence through explicit Foundry migrations and generated model queries. Construction performs no I/O.
Index ¶
- Constants
- func Migrations() []migrate.Definition
- type Backend
- func (b *Backend) ConsumePendingIn(ctx context.Context, tx *database.Tx, address token.Address, ...) error
- func (b *Backend) Create(ctx context.Context, address token.Address, creation token.Creation) (token.Record, error)
- func (b *Backend) CreateBefore(ctx context.Context, address token.Address, creation token.Creation, ...) (token.Record, error)
- func (b *Backend) CreateChecked(ctx context.Context, address token.Address, creation token.Creation, ...) (token.Record, error)
- func (b *Backend) IssueTicket(ctx context.Context, address token.Address, identity model.Identity, ...) (value.Optional[temporal.DateTime], error)
- func (b *Backend) List(ctx context.Context, address token.Address, identity model.Identity, limit int) ([]token.Record, error)
- func (b *Backend) Lookup(ctx context.Context, address token.Address, hash token.Digest, touch bool) (value.Optional[token.Record], error)
- func (b *Backend) LookupFamily(ctx context.Context, address token.Address, identity model.Identity, ...) (value.Optional[token.Record], error)
- func (b *Backend) LookupWithin(ctx context.Context, address token.Address, hash token.Digest, ...) (value.Optional[token.Record], error)
- func (b *Backend) Prune(ctx context.Context, address token.Address, limit int) (uint64, error)
- func (b *Backend) PruneTickets(ctx context.Context, address token.Address, limit int) (uint64, error)
- func (b *Backend) RedeemTicket(ctx context.Context, address token.Address, hash token.Digest) (value.Optional[token.TicketRecord], error)
- func (b *Backend) Refresh(ctx context.Context, address token.Address, old, access, refresh token.Digest) (value.Optional[token.Record], error)
- func (b *Backend) Revoke(ctx context.Context, address token.Address, hash token.Digest) (bool, error)
- func (b *Backend) RevokeAll(ctx context.Context, address token.Address, identity model.Identity) (uint64, error)
- func (b *Backend) RevokeAllIn(ctx context.Context, tx *database.Tx, address token.Address, ...) (uint64, error)
- func (b *Backend) RevokeID(ctx context.Context, address token.Address, identity model.Identity, ...) (bool, error)
- func (b *Backend) RevokeOthers(ctx context.Context, address token.Address, identity model.Identity, ...) (uint64, error)
- func (b *Backend) RevokeRefresh(ctx context.Context, address token.Address, hash token.Digest) (value.Optional[model.Identity], error)
- type Config
Constants ¶
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 (*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 ¶
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 ¶
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.