tls (https)
Native HTTPS for the “facing the internet” setup (no nginx). Behind a reverse proxy the proxy terminates TLS — skip this. TLS is declared PER SITE, in the root settings.json’s site map, plus one shared server.tls_port; certificates are picked by SNI, so different sites serve different certificates on the same port. Two certificate sources per site, exactly one:
| key | default | description |
|---|---|---|
| server.tls_port | 443 | the HTTPS port; must differ from server.port |
| site tls.cert / tls.key | — | paths to fullchain and private-key PEM, kept in the site’s folder (mode 1) |
| site tls.acme | — | Let’s Encrypt: the certificate covers the site’s domain and its aliases without tls of their own; email optional — contact for expiry notices |
Let’s Encrypt (automatic) — the binary obtains and renews the certificates itself:
"server": { "host": "0.0.0.0", "port": 80, "tls_port": 443, "default": "example.com" },
"site": {
"example.com": {
"dir": "default",
"alias": [ { "domain": "www.example.com", "mode": "redirect" } ],
"tls": { "acme": { "email": "admin@example.com" } }
}
}cms tune direct --acme example.com,www.example.com writes exactly this shape. ACME state (account key, certificates) lives in the site’s tls/ folder, chmod 600. The domain list is hot: edit the map and cms reload — added domains are issued within a minute, removed ones stop being served.
Your own PEM files (certbot, corporate CA, …):
"tls": { "cert": "tls/fullchain.pem", "key": "tls/privkey.pem" }The server never talks to Let’s Encrypt in this mode. If certbot rewrites the files, they are picked up without a restart (./cms reload or an hourly check).
Ports change roles when tls is on. The site is served over HTTPS on server.tls_port. Your server.port (usually 80) becomes a tiny HTTP service: it answers the ACME challenge at /.well-known/acme-challenge/* (always, even past expiry — so renewal keeps working) and 301-redirects everything else to https. Ports below 1024 need a capability: setcap 'cap_net_bind_service=+ep' ./cms or run under systemd.
Before using ACME: the domain's A/AAAA record must point to THIS server and port 80 must be reachable from the internet — that's how Let's Encrypt validates you. On the very first run, before the certificate is issued, a self-signed placeholder is served (browsers warn); it is swapped for the real one automatically.
Testing. Let's Encrypt rate-limits the real service (~5 duplicate certs/week). While you sort out DNS and ports, use staging — set the environment variable, not a config field: CMS_ACME_STAGING=1 ./cms run. Staging certs are untrusted but the limits are generous. Clear the site’s tls/ folder when switching between staging and production.
Failures are loud where it matters: if the first ACME issue fails the server refuses to start and tells you what to check (DNS, port 80). A failed renewal is only a warning — the site keeps serving the current certificate and retries every 12 hours.