Atom owns the certificate authority registry, per-tenant issuance, revocation state, publication artifacts, subject enrollment (native + RFC 7030 EST), and runtime resolution. The legacy v1 "file issuer" mode has been removed — there is no ATOM_CERTS_* env, no ca_chain GraphQL query, no /certs/ca-chain route, no /certs/crl / /certs/ocsp global endpoints, no issueCertificate / renewCertificate / revokeCertificate v1 mutations, and no ResolveCertificate v1 gRPC method.
Every CA in an Atom deployment lives in the pki_authorities table and is managed through the same lifecycle mutations.
One offline root trust anchor, imported into pki_authorities as a PublicOnly row. The root's private key never enters Atom.
One online platform intermediate, generated by Atom, signed offline by the root operator, then imported back.
Optional platform leaf issuer for global (tenantless) entities.
One active tenant intermediate per tenant, provisioned automatically once a platform intermediate is active. Rotation replaces the active row while leaving retired versions available for CRL/OCSP.
CA private keys are stored envelope-encrypted in Postgres by default. ATOM_PKI_CA_KEY_BACKEND=pkcs11 swaps in a PKCS#11 HSM.
Every issued leaf carries an issuer_id pointing at its authority. Per-tenant CRL and OCSP responders are keyed by that ID.
Public artifact URLs (ocsp, crl, ca_issuers) are embedded in every issued leaf's AIA and CRL-distribution-point extensions at issuance, so relying parties don't need to know the tenant / issuer / URL scheme.
Runtime resolution v2 accepts a leaf DER, fingerprint, or (issuer fingerprint, serial) tuple and returns the credential-owning entity plus its tenant.
Subject-driven first enrollment and re-enrollment are exposed as native POST /pki/enroll / POST /pki/reenroll and as RFC 7030 EST (/.well-known/est/*) on a dedicated TLS listener.
Signs tenant intermediates. Config-only bootstrap (bring your own pre-signed cert + key).
Encrypted DB or PKCS#11.
platform_leaf_issuer
Signs leaves for global / tenantless entities.
Encrypted DB or PKCS#11.
tenant_intermediate
Signs leaves for one tenant. Auto-provisioned per tenant.
Encrypted DB or PKCS#11.
AuthorityKind::can_issue_leaf_credentials() returns true only for platform_leaf_issuer and tenant_intermediate. Publication URLs (ocsp_url, ca_issuers_url, crl_distribution_point_url) are populated at activation only for those two — they're what the PkiIssuer requires.
Root and platform intermediate are config-only — no GraphQL mutation, no UI. Both are one-time, security-critical trust-anchor decisions that belong in the deployment manifest.
Generate the root offline — this key never enters Atom:
All three are idempotent: same fingerprint = no-op. If the platform intermediate PEM changes, Atom retires the previous one and activates the new. Atom wraps the platform intermediate private key with the CA KEK before persisting.
Provision a tenant intermediate — after any tenant is created:
provisionTenantAuthorityAutomatically(tenantId) — Atom generates + signs with the active platform intermediate + activates. Populates OCSP / CA-issuers / CRL URLs from ATOM_PUBLIC_BASE_URL. Also reachable from /pki/actions in the UI.
Shortcut for local dev / demos:make pki-material generates all four PEMs in ./certs/, wires the three env vars into .env, and restarts the atom container. make up already depends on it, so a fresh clone just works. For an end-to-end visual walkthrough, see the UI test playbook.
CA private keys are AES-GCM-encrypted with a data-encryption key that's itself wrapped by the deployment's KEK (ATOM_PKI_CA_KEY_ENCRYPTION_KEY, base64 of 32 bytes) and stored on the pki_authorities row. Startup rejects keys wrapped by an unknown KEK ID.
Atom holds only a token-object reference. Signing goes through the PKCS#11 module (see PKCS#11 Operations). Configure via ATOM_PKI_PKCS11_MODULE_PATH, _TOKEN_LABEL, _USER_PIN plus optional _OPERATION_TIMEOUT_MS, _MAX_RETRIES, _MAX_IN_FLIGHT, _CIRCUIT_FAILURE_THRESHOLD, _CIRCUIT_RESET_SECS.
The two backends coexist per authority: a deployment can hold some CAs encrypted-in-DB and others in PKCS#11. Backend selection is stored on the authority row and cannot be changed after provisioning.
Serial-number uniqueness is (issuer_id, identifier) — independent issuers may reuse serials. Fingerprints (fingerprint_sha256) remain globally unique.
Generated leaf private keys are shown once in the issuance response as privateKeyPem and never stored. CSR-issued certificates never expose a private key to Atom.
Revocation state is recorded in certificate_revocations (the immutable ledger, migrations 016 / 022 / 023). Publication continuity survives authority purge so already-issued certificates retain durable revocation evidence.
Rolling per-entity / per-tenant enrollment counters.
Useful queries:
-- All active managed authoritiesSELECT id, kind, tenant_id, subject, status, not_afterFROM pki_authoritiesWHERE status = 'active'ORDER BY kind, subject;-- Every leaf for a tenantSELECT c.id AS credential_id, c.identifier AS serial, c.status, c.expires_at, a.subject AS issuer_subjectFROM credentials cJOIN pki_authorities a ON a.id = c.issuer_idWHERE c.kind = 'certificate' AND a.tenant_id = $1ORDER BY c.created_at DESC;-- Fresh CRLs (regenerated on demand)SELECT issuer_fingerprint_sha256, crl_number, next_update, dirtyFROM certificate_crl_state;
CSR-issued leaves are forced to non-CA digitalSignature + clientAuth. TTLs above the profile / issuer bounds are rejected. Serial-number collisions retry inside a savepoint. All issuance / renewal / revoke paths commit inside a single transaction alongside the audit event and outbox row (see AGENTS.md § three channels).
Dedicated TLS listener, opt-in with ATOM_PKI_ENROLLMENT_ENABLED=true. Binds ATOM_PKI_ENROLLMENT_LISTEN_ADDR (default 0.0.0.0:8443) with TLS material at ATOM_PKI_ENROLLMENT_TLS_CERT_PATH + _TLS_KEY_PATH.
POST /pki/enroll — first enrollment. Authenticates a non-certificate credential (Bearer token or existing session) and derives the subject from that credential.
POST /pki/reenroll — re-enrollment. Ignores bearer credentials; accepts only the leaf certificate verified by the in-process TLS handshake and maps that DER to the exact credential through the v2 runtime resolver.
Both handlers accept CSR + optional TTL + idempotency key. Tenant / entity / issuer / profile / scope come from the authenticated subject — the caller cannot select them.
Rate limits: ATOM_PKI_ENROLLMENT_ENTITY_RATE_LIMIT, _TENANT_RATE_LIMIT, _ENTITY_RATE_WINDOW_SECS, _TENANT_RATE_WINDOW_SECS, plus the separate IP-based ATOM_HTTP_RATE_LIMIT_ENROLLMENT policy when ATOM_RATE_LIMIT_ENABLED is true. IPv6 source buckets use /64 by default and can be tuned separately with ATOM_PKI_ENROLLMENT_IPV6_PREFIX_LEN (connections) and ATOM_HTTP_RATE_LIMIT_IPV6_PREFIX_LEN (requests). TLS/HTTP connection bounds: _MAX_CONNECTIONS, _MAX_CONNECTIONS_PER_IP, _HANDSHAKE_TIMEOUT_SECS, _HTTP_HEADER_TIMEOUT_SECS, _REQUEST_TIMEOUT_SECS, _CONNECTION_TIMEOUT_SECS, _SHUTDOWN_DRAIN_TIMEOUT_SECS; HTTP keep-alive is disabled by default and may be enabled with ATOM_PKI_ENROLLMENT_HTTP_KEEP_ALIVE=true.
RFC 7030 EST is served on the same listener at /.well-known/est/{cacerts,csrattrs,simpleenroll,simplereenroll,serverkeygen} — see EST and ACME.
(Root import, platform intermediate import, and offline CSR signing are removed from GraphQL — bootstrap those via ATOM_PKI_ROOT_CERT_PATH / ATOM_PKI_PLATFORM_INTERMEDIATE_{CERT,KEY}_PATH.)
GET /certs/trust-bundle.pem — the deployment's trust anchors as PEM.
GET /certs/issuers/:issuer_id/crl — DER CRL per issuer.
POST /certs/issuers/:issuer_id/ocsp — RFC 6960 OCSP per issuer.
The URLs above are what Atom embeds in every issued leaf's AIA / CRL-distribution-point extensions, so relying parties don't have to know the URL scheme.
Certificate operations use the standard Atom credential authorization surface. Each operation applies the scope rules described by the GraphQL schema and the access model.
Issue / renew / revoke on entity certs: credential.manage on the entity, or exact credential.rotate / .revoke on the credential.
Authority provisioning / retirement: pki.provision on the target tenant scope + pki.provision_automated on platform for the auto flow.
List authorities: platform-scoped read, or tenant-scoped read for that tenant's authorities only.
Runtime resolve: authz.check on the resolved tenant or platform.
All ATOM_CERTS_* env vars (_ENABLED, _CA_MODE, _ROOT_CA_*, _INTERMEDIATE_CA_*, _LEAF_*).
CertsCaMode enum, CertificateIssuer struct, load_file_issuer_if_enabled, in-process global CA state.
GraphQL: caChain, issueCertificate, issueCertificateFromCsr, renewCertificate, revokeCertificate — replaced by the *V2 set above.
HTTP: GET /certs/ca-chain, GET /certs/crl, POST /certs/ocsp — replaced by /certs/trust-bundle.pem and the per-issuer /certs/issuers/:id/{crl,ocsp} routes.
Test binary m17_certificates (v1 file-issuer coverage).
Health status field certificate_issuer (there is no single global issuer to report on).
Existing credentials rows with kind='certificate' and issuer_id IS NULL (legacy leaves from v1 deployments) remain in the database but are no longer resolvable through any v2 code path. They will expire on their own timeline and can be swept later; no runtime consumer accepts them.