Aller au contenu principal

Database Schema

Certeasy's schema is managed automatically via embedded migrations. This page documents the tables, their purpose, and their lifecycle.

ACME Flow Overview

StepEndpointServer ActionTables Affected
New AccountPOST /acme/newAccountCreates a client accountacme_accounts
New OrderPOST /acme/newOrderCreates an order with authorizations and challengesacme_orders, acme_order_identifiers, acme_authorizations, acme_challenges
Challenge ValidationPOST /acme/chall/<id>Client responds, server validates asynchronouslyacme_challenges, acme_authorizations
FinalizationPOST /acme/finalize/<id>Client sends CSR → certificate issuedacme_orders, acme_certificates
Certificate RetrievalGET /acme/cert/<id>Client downloads certificateRead only
RevocationPOST /acme/revoke-certRevokes a certificateacme_certificates
Replay ProtectionAutomaticAnti-replay noncesacme_nonces
AuditingAutomaticAll significant actionsacme_audit_logs

Table: acme_accounts

Stores registered ACME client accounts (RFC 8555 §7.1.2).

ColumnDescription
account_idLogical account ID (e.g. /acme/acct/123)
jwkClient public key in canonical JSON
contactList of mailto: addresses
statusvalid, deactivated, revoked
tos_agreed_atTimestamp of Terms of Service acceptance

Created: POST /acme/newAccount (when JWK thumbprint is new)

State transitions:

  • validdeactivated: via POST /acme/acct/{id} with "status":"deactivated"
  • validrevoked: on full account revocation

Table: acme_nonces

Anti-replay nonces used in JWS request headers.

ColumnDescription
nonceRandom nonce value
created_atWhen the nonce was issued
used_atWhen the nonce was consumed (null if unused)

Every ACME response generates a new nonce. Once a nonce is used in a valid JWS request, it is marked used and cannot be reused. Expired nonces are cleaned up periodically.


Table: acme_orders

Represents a certificate order.

ColumnDescription
order_idOrder identifier
account_idOwning account
statuspending, ready, processing, valid, invalid
csrBase64url-encoded CSR (set at finalize)
not_before, not_afterRequested validity window
expires_atOrder expiry
certificate_idLinked certificate (set when issued)

Created: POST /acme/newOrder

Status flow:

pending → ready (all authorizations valid)
→ processing (finalize received)
→ valid (certificate issued)
→ invalid (challenge or issuance failure)

Table: acme_order_identifiers

DNS identifiers requested in an order. One row per identifier.

An order for ["app.corp.internal", "*.corp.internal"] creates two rows.

Created: during POST /acme/newOrder


Table: acme_authorizations

Proof of control for each identifier in an order.

ColumnDescription
authz_idPublic URL
identifier_valueDNS name (e.g. app.corp.internal)
statuspending, valid, invalid
expires_atAuthorization expiry
wildcardWhether this is a wildcard authorization
errorValidation error detail (if failed)

When at least one challenge for an authorization becomes valid, the authorization becomes valid. When all authorizations for an order are valid, the order status moves to ready.


Table: acme_challenges

Validation challenges (DNS-01, HTTP-01, TLS-ALPN-01) for each authorization.

ColumnDescription
chall_idChallenge identifier
typedns-01, http-01, tls-alpn-01
statuspending, processing, valid, invalid
tokenChallenge token
key_authorizationComputed key authorization
validated_atTimestamp of successful validation

Created: automatically with newOrder

Updated: on POST /acme/challenge/<id> → moves to processing, then valid or invalid


Table: acme_certificates

Issued TLS certificates.

ColumnDescription
certificate_idCertificate identifier
account_idOwning account
order_idOriginating order
pem_chainPEM certificate chain (never includes private key)
not_before, not_afterValidity window
fingerprintSHA-256 fingerprint of the leaf certificate
revoked_atRevocation timestamp (null if active)
revoke_reasonRFC 5280 reason code (0–10, excluding 7)

Created: POST /acme/finalize/<id> after successful ADCS issuance

Updated: POST /acme/revoke-cert


Table: acme_audit_logs

Internal audit trail for all significant ACME operations.

ColumnDescription
actionOperation type: newAccount, newOrder, challengeRespond, finalize, revokeCert, etc.
account_idAssociated account
order_idAssociated order
authz_idAssociated authorization
chall_idAssociated challenge
detailsJSON blob with operation details
created_atTimestamp

Populated on every significant ACME action including errors.


Full Flow Reference

POST /acme/newAccount
→ INSERT acme_accounts

POST /acme/newOrder
→ INSERT acme_orders
→ INSERT acme_order_identifiers (one per identifier)
→ INSERT acme_authorizations (one per identifier)
→ INSERT acme_challenges (one per auth × challenge type)

POST /acme/challenge/{id}
→ UPDATE acme_challenges (status → processing)
[async job validates DNS/HTTP/TLS]
→ UPDATE acme_challenges (status → valid/invalid)
→ UPDATE acme_authorizations (status → valid if one challenge valid)
→ UPDATE acme_orders (status → ready if all authorizations valid)

POST /acme/finalize/{id}
→ UPDATE acme_orders (status → processing)
[async job submits CSR to ADCS]
→ INSERT acme_certificates
→ UPDATE acme_orders (status → valid, certificate_id → ...)

GET /acme/cert/{id}
→ SELECT acme_certificates

POST /acme/revoke-cert
→ UPDATE acme_certificates (revoked_at, revoke_reason)