Documentation
¶
Overview ¶
Package sqlstore persists acme.Store records in SQLite, PostgreSQL and MySQL.
Design ¶
The implementation reuses server/sqlstore/sqlcommon dialects and migrations and owns acme_schema_migrations. JSON records retain protocol values beside indexed lookup fields. Unique constraints coordinate account-key registration and one-time identifier claims; transactional nonce removal permits only one consumer. Attestation bytes round-trip unchanged for re-verification at finalize.
The store does not seal records with a keyring. Public keys and certificates do not contain private key material, but protocol and attestation records still require database access controls. storage/acme/acmetest defines the shared behavior.
References ¶
- Decision record 0031: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/research/decisions/0031-acme-server-and-state-store.md
- Decision record 0012: https://github.com/deploymenttheory/go-apple-dm/blob/main/docs/research/decisions/0012-sql-storage-backends.md (dialects and migrations)
- RFC 8555 (ACME): https://www.rfc-editor.org/rfc/rfc8555
- draft-ietf-acme-device-attest: https://datatracker.ietf.org/doc/draft-acme-device-attest/
- Migrations: acme/sqlstore/migrations/{sqlite,postgres,mysql}/0001_init.sql
Index ¶
- Constants
- Variables
- func Migrate(ctx context.Context, db *sql.DB, d sqlcommon.Dialect) ([]int, error)
- func MigrationSet(d sqlcommon.Dialect) (sqlcommon.MigrationSet, error)
- func Rollback(ctx context.Context, db *sql.DB, d sqlcommon.Dialect, target int) ([]int, error)
- func Version(ctx context.Context, db *sql.DB, d sqlcommon.Dialect) (int, error)
- type Options
- type Store
- func (s *Store) AccountByThumbprint(ctx context.Context, thumbprint string) (*acme.Account, error)
- func (s *Store) DB() *sql.DB
- func (s *Store) GetAccount(ctx context.Context, id string) (*acme.Account, error)
- func (s *Store) GetAuthorization(ctx context.Context, id string) (*acme.Authorization, error)
- func (s *Store) GetCertificate(ctx context.Context, id string) (*acme.Certificate, error)
- func (s *Store) GetChallenge(ctx context.Context, id string) (*acme.Challenge, error)
- func (s *Store) GetOrder(ctx context.Context, id string) (*acme.Order, error)
- func (s *Store) ListCertificates(ctx context.Context, q acme.CertificateQuery, p paging.Page) (paging.Result[acme.Certificate], error)
- func (s *Store) ListOrders(ctx context.Context, accountID string, p paging.Page) (paging.Result[acme.Order], error)
- func (s *Store) Prune(ctx context.Context, before time.Time) (int, error)
- func (s *Store) PutNonce(ctx context.Context, n acme.Nonce) error
- func (s *Store) TakeNonce(ctx context.Context, value string) (*acme.Nonce, error)
- func (s *Store) Update(ctx context.Context, fn func(acme.Tx) error) error
- func (s *Store) UpdateOrder(ctx context.Context, id string, fn func(acme.Tx) error) error
Constants ¶
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 ¶
var ErrUnsupportedDialect = errors.New("sqlstore: unsupported dialect")
ErrUnsupportedDialect is returned for a dialect without embedded migrations (anything but sqlite, postgres, and mysql).
Functions ¶
func MigrationSet ¶
func MigrationSet(d sqlcommon.Dialect) (sqlcommon.MigrationSet, error)
MigrationSet returns the ACME migrations for the dialect.
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 ¶
Open wraps an opened pool for the dialect and, unless o.SkipMigrate, applies pending migrations.
func (*Store) AccountByThumbprint ¶
AccountByThumbprint implements acme.Reader.
func (*Store) GetAccount ¶
GetAccount implements acme.Reader.
func (*Store) GetAuthorization ¶
GetAuthorization implements acme.Reader.
func (*Store) GetCertificate ¶
GetCertificate implements acme.Reader.
func (*Store) GetChallenge ¶
GetChallenge implements acme.Reader.
func (*Store) ListCertificates ¶
func (s *Store) ListCertificates( ctx context.Context, q acme.CertificateQuery, p paging.Page, ) (paging.Result[acme.Certificate], error)
ListCertificates implements acme.Reader.
func (*Store) ListOrders ¶
func (s *Store) ListOrders( ctx context.Context, accountID string, p paging.Page, ) (paging.Result[acme.Order], error)
ListOrders implements acme.Reader.
func (*Store) TakeNonce ¶
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 ¶
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.
func (*Store) UpdateOrder ¶
UpdateOrder implements acme.Store. The no-op update takes a write lock before the first snapshot read, on SQLite as well as PostgreSQL and MySQL. No callback is retried, since it may perform pure certificate signing.