sqlstore

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Sep 3, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package sqlstore is the SQL acme.Store: one implementation over database/sql for the SQLite, PostgreSQL, and MySQL dialects.

Why

The ACME server keeps no state of its own, so every property a device depends on is a property of the store, and a deployment that survives a restart needs those properties from a database rather than from a map. Phase 7 of the plan of record delivers the server against acme/inmem (decision record 0031); this package is the same contract where the records outlive the process. It reuses storage/sqlcommon's dialects and migration runner but owns its own migration set, recorded in acme_schema_migrations, so the ACME tables can share a database with the MDM, DDM, and DEP schemas or live apart.

Nothing here is sealed. An account key is a public JWK, an attestation object is a signed statement the device sent in clear, and an issued certificate is published to whoever asks; there is no secret at rest for a keyring to protect, so Options carries none and the storage/crypt dependency the sibling stores have is absent.

Each record is stored as the JSON of the whole value beside indexed copies of the fields that are looked up or filtered, so a record gains a field without a migration. Two rules are the store's own rather than SQL's: a duplicate account key and a second claim on a client identifier are decided by a unique index, never by reading first and writing after, because a read-then-write cannot be correct under concurrency; and a nonce is taken with DELETE ... RETURNING where the engine has it, and on MySQL by a read and a delete in one transaction whose row count says who won, so exactly one of two concurrent takers takes it. The contract suite in acme/acmetest runs against all three dialects.

References

Index

Constants

View Source
const MigrationsTable = "acme_schema_migrations"

MigrationsTable records the applied versions of the ACME schema, separate from the storage, DDM, and DEP tables so the version sequences never mix.

Variables

View Source
var ErrUnsupportedDialect = errors.New("sqlstore: unsupported dialect")

ErrUnsupportedDialect is returned for a dialect without embedded migrations (anything but sqlite, postgres, and mysql).

Functions

func Migrate

func Migrate(ctx context.Context, db *sql.DB, d sqlcommon.Dialect) ([]int, error)

Migrate applies every pending ACME migration and returns the versions applied.

func MigrationSet

func MigrationSet(d sqlcommon.Dialect) (sqlcommon.MigrationSet, error)

MigrationSet returns the ACME migrations for the dialect.

func Rollback

func Rollback(ctx context.Context, db *sql.DB, d sqlcommon.Dialect, target int) ([]int, error)

Rollback reverts ACME migrations newer than target (0 reverts all).

func Version

func Version(ctx context.Context, db *sql.DB, d sqlcommon.Dialect) (int, error)

Version returns the highest applied ACME migration (0 when none).

Types

type Options

type Options struct {
	// SkipMigrate leaves the schema alone; the caller has run Migrate.
	SkipMigrate bool
}

Options tune Open.

There is no Keyring here, unlike the DEP and MDM stores. Nothing this package holds is a secret at rest: an account key is a public JWK, the attestation object is a signed statement the device sent in clear, an issued certificate is published, and a nonce is worthless the moment it is taken. A keyring would add a strict-mode failure path guarding nothing.

type Store

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

Store implements acme.Store over a *sql.DB it does not own: closing the pool is the caller's job.

func Open

func Open(ctx context.Context, db *sql.DB, d sqlcommon.Dialect, o Options) (*Store, error)

Open wraps an opened pool for the dialect and, unless o.SkipMigrate, applies pending migrations.

func (*Store) AccountByThumbprint

func (s *Store) AccountByThumbprint(ctx context.Context, thumbprint string) (*acme.Account, error)

AccountByThumbprint implements acme.Reader.

func (*Store) DB

func (s *Store) DB() *sql.DB

DB exposes the pool for health checks and tests.

func (*Store) GetAccount

func (s *Store) GetAccount(ctx context.Context, id string) (*acme.Account, error)

GetAccount implements acme.Reader.

func (*Store) GetAuthorization

func (s *Store) GetAuthorization(ctx context.Context, id string) (*acme.Authorization, error)

GetAuthorization implements acme.Reader.

func (*Store) GetCertificate

func (s *Store) GetCertificate(ctx context.Context, id string) (*acme.Certificate, error)

GetCertificate implements acme.Reader.

func (*Store) GetChallenge

func (s *Store) GetChallenge(ctx context.Context, id string) (*acme.Challenge, error)

GetChallenge implements acme.Reader.

func (*Store) GetOrder

func (s *Store) GetOrder(ctx context.Context, id string) (*acme.Order, error)

GetOrder implements acme.Reader.

func (*Store) ListCertificates

func (s *Store) ListCertificates(
	ctx context.Context,
	q acme.CertificateQuery,
	p storage.Page,
) (storage.Result[acme.Certificate], error)

ListCertificates implements acme.Reader.

func (*Store) ListOrders

func (s *Store) ListOrders(
	ctx context.Context,
	accountID string,
	p storage.Page,
) (storage.Result[acme.Order], error)

ListOrders implements acme.Reader.

func (*Store) Prune

func (s *Store) Prune(ctx context.Context, before time.Time) (int, error)

Prune implements acme.Store.

func (*Store) PutNonce

func (s *Store) PutNonce(ctx context.Context, n acme.Nonce) error

PutNonce implements acme.Store.

func (*Store) TakeNonce

func (s *Store) TakeNonce(ctx context.Context, value string) (*acme.Nonce, error)

TakeNonce implements acme.Store. Removing the row is what takes the nonce, so the winner of a race is whichever caller's DELETE removed a row and the loser sees ErrNotFound, which is also how the server detects a replay: the first use removed it.

func (*Store) Update

func (s *Store) Update(ctx context.Context, fn func(acme.Tx) error) error

Update implements acme.Store. fn runs in one transaction that commits when fn returns nil and rolls back otherwise, so an order, its authorization, its challenge, and the claim on its identifier either all exist or none do.

Jump to

Keyboard shortcuts

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