xacme: automated certificates
The life-support of HTTPS: an ACME (RFC 8555) client packs account, order, dns-01 challenge and certificate fetch into one function call — paired with disk storage for expiry-driven renewal.
Overview
build.bat xacme variant. Live testing needs a real domain and DNS control.38.1 The ACME flow
The RFC 8555 loop: register an account with the CA's directory (ES256 key + JOSE signatures) → order domains (wildcards allowed) → satisfy the dns-01 challenge (publish the CA-provided TXT record in DNS) → finalize with a CSR → download the chain. xacmeClientInit builds the client (parses the directory, registers/reuses the account); xacmeClientIssue runs the whole loop and returns a PEM chain. CA-neutral: LE / ZeroSSL / Google / Buypass presets — switching CA is switching URL.
38.2 DNS providers
dns-01 needs DNS write access: the provider interface is a plain struct (id / Add / Remove / optional Propagate callbacks); Aliyun is built in (dns_ali, V3 signing); other vendors plug in by filling the struct — no registration, no global state. The library carries its own TXT propagation probe — a UDP query to the authoritative NS confirms the record is visible before telling the CA to verify.
38.3 Issuing and renewing
xacmeclient* pClient;
xstrview tDomain = xrtStrView("example.com");
bool bRenewed;
pClient = ...; /* xacmeClientInit(engine, CA pem, directory URL, account key) */
char* sChain = xacmeClientIssueStored(pClient, &tDomain, 1,
&tDnsProvider, sStoreRoot, 30, &bRenewed);
if ( sChain != NULL && bRenewed ) {
install_cert(sChain); /* hot swap (drilled in ch. 47) */
}
xrtFree(sChain);
IssueStored semantics: if the local certificate's remaining lifetime is at least the threshold in days, return the existing chain directly (pbRenewed=false); otherwise run a full issuance and persist. The remaining-days check is based on x509 parsing (xrt), with no external process.
38.4 Storage and recovery
The store is a directory convention: accounts/ holds account keys (PKCS#8 PEM; Init reuses them to skip re-registration), certs/<domain>/ holds the full chain and CA provenance, all written atomically (xrtFileWriteAtomic). Renewal checks, certificate readback and CA-chain readback each have standalone functions — you can build your own renewal policy without IssueStored.
38.5 The contract
| Concern | Key points |
|---|---|
| Ownership | client and Issue-returned chains are library-allocated; Destroy / xrtFree release |
| Networking | own synchronous HTTPS transport (borrow an engine or self-owned); traffic concentrates in the issuance window |
| Credentials | account and DNS keys go through store/env (ch. 44 layers), never into source |
| Failure | thread error carries stable codes under the "xrt.acme.flow" domain, classifiable for retries |
Common mistakes
- Symptom
- Frequent re-issuing trips Let's Encrypt rate limits and the domain locks for hours.
- Cause
- Debug against the LE staging preset (no production quota); don't set IssueStored's renewal threshold to "renew every time".
- Symptom
- After restart the account doesn't match; the CA sees a fresh registration and order history is lost.
- Cause
- The account key must persist via the store and be passed to Init for reuse — IssueStored handles it; bypass it and you own the chore.
Exercises
Run the staging directory + account config + provider registry (Count/Id); NeedRenew returns true for a near-expiry certificate.
Complete one dns-01 issuance on a real domain against LE staging and persist it.
A ch. 41 timer checks daily; below 30 days, IssueStored plus a hot swap (the ch. 47 zero-downtime drill).
Summary
| Point | Conclusion |
|---|---|
| Flow | directory→account→order→dns-01→finalize→download, wrapped in one function |
| DNS | plain-struct provider interface, Aliyun built in, TXT probe included |
| Renewal | IssueStored decides by remaining days; atomic persistence |
| Storage | account-key reuse + full-chain provenance, clear layout |
| Discipline | staging for debug; persist keys; credentials out of source |
- xrt · x509 — expiry parsing and trust chains