diff --git a/docs/.vitepress/census-generated.json b/docs/.vitepress/census-generated.json index 0676f8b..3f711c9 100644 --- a/docs/.vitepress/census-generated.json +++ b/docs/.vitepress/census-generated.json @@ -1,6 +1,6 @@ { - "routeCount": 126, - "sectionCount": 22, + "routeCount": 129, + "sectionCount": 23, "roles": [ "viewer", "auditor", @@ -10,11 +10,11 @@ "methods": [ { "method": "GET", - "count": 49 + "count": 50 }, { "method": "POST", - "count": 46 + "count": 47 }, { "method": "PATCH", @@ -26,11 +26,11 @@ }, { "method": "DELETE", - "count": 19 + "count": 20 } ], "displayTokenReadable": 41, - "documentedBodies": 42, + "documentedBodies": 43, "modelCount": 31, "source": "core/api/router.go", "sections": [ @@ -102,6 +102,10 @@ "text": "Display Tokens", "count": 3 }, + { + "text": "ACME credentials", + "count": 3 + }, { "text": "Accounts", "count": 4 diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index b2c6d1b..ebe4e8e 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -91,7 +91,7 @@ export default withMermaid( { text: 'Walkthroughs', link: '/walkthroughs/', activeMatch: '^/walkthroughs' }, { text: 'Deploy', link: '/configuration', activeMatch: '^/(configuration|database|operations|security)' }, { text: 'Operate', link: '/discovery', activeMatch: '^/(discovery|monitoring|deployment|templates|posture|troubleshooting)' }, - { text: 'Integrate', link: '/agent', activeMatch: '^/(agent|platforms|gateways|writing-a-gateway)' }, + { text: 'Integrate', link: '/agent', activeMatch: '^/(agent|acme-server|platforms|gateways|writing-a-gateway)' }, { text: 'Reference', activeMatch: '^/api/', diff --git a/docs/.vitepress/guide-sidebar.json b/docs/.vitepress/guide-sidebar.json index a46f286..760fba8 100644 --- a/docs/.vitepress/guide-sidebar.json +++ b/docs/.vitepress/guide-sidebar.json @@ -119,6 +119,10 @@ "text": "The host agent", "link": "/agent" }, + { + "text": "ACME clients, no agent", + "link": "/acme-server" + }, { "text": "All platforms", "link": "/platforms/" diff --git a/docs/.vitepress/sidebar-generated.json b/docs/.vitepress/sidebar-generated.json index d268042..2e08c5f 100644 --- a/docs/.vitepress/sidebar-generated.json +++ b/docs/.vitepress/sidebar-generated.json @@ -84,6 +84,11 @@ "link": "/api/reference/display-tokens", "count": 3 }, + { + "text": "ACME credentials", + "link": "/api/reference/acme-credentials", + "count": 3 + }, { "text": "Accounts", "link": "/api/reference/accounts", diff --git a/docs/acme-server.md b/docs/acme-server.md new file mode 100644 index 0000000..d73f697 --- /dev/null +++ b/docs/acme-server.md @@ -0,0 +1,170 @@ +--- +editLink: false +lastUpdated: 2026-10-18T16:50:02Z +source: + repo: certpilot/certpilot + path: docs/acme-server.md + commit: 924a408c35ec75e6908892086698aca6cf5dbff9 +--- + + + +# The ACME server + +Most hosts that need a certificate already run an ACME client: certbot, +acme.sh, lego, win-acme, Caddy. The core can serve ACME (RFC 8555) itself, so +those clients get certificates from CertPilot with no agent installed. The +client generates and keeps its key. CertPilot issues through the template you +choose, records the certificate as held by the client, and alerts on it like +any other. + +This is the other direction from the [ACME gateway](/gateways/acme), where +CertPilot is the client of a public CA. + +## Turning it on + +Off by default, because it opens unauthenticated endpoints on the core's +listener. + +```yaml +acme: + enabled: true + external_url: "https://certpilot.example.com" +``` + +`external_url` is the address clients use. Every URL the server hands out is +built from it, and a request is refused unless it was signed for one of them. +It is required, and must be `https`, in production mode. +[configuration.md](/configuration#acme) has every setting. + +There is one directory per template, at +`https://certpilot.example.com/acme//directory`. A template +already pins its CA, the names it allows, its key types and its lifetime, so +the template is what you hand a client access to. + +## Giving a client access + +Registration is closed. Each client needs a credential, an External Account +Binding: a key ID and an HMAC key. Make one in **Settings → ACME clients**: +choose a name that says who it is for, and the template. The HMAC key is shown +once, with the directory URL and a certbot command filled in. CertPilot keeps +the key sealed and cannot show it again; if it is lost, revoke the credential +and make another. + +The same through the API, as an administrator: + +```bash +curl -s -X POST "$CERTPILOT/api/v1/acme/credentials" \ + -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \ + -d '{"name": "web team", "template": "internal-web"}' +``` + +One credential makes one account. Then, on the host: + +```bash +# certbot +certbot register --server "$DIRECTORY_URL" \ + --eab-kid "$KID" --eab-hmac-key "$HMAC_KEY" --email ops@example.com +certbot certonly --server "$DIRECTORY_URL" --webroot -w /var/www/html \ + -d web.example.com -d api.example.com + +# acme.sh +acme.sh --register-account --server "$DIRECTORY_URL" \ + --eab-kid "$KID" --eab-hmac-key "$HMAC_KEY" -m ops@example.com +acme.sh --issue --server "$DIRECTORY_URL" -d web.example.com -w /var/www/html + +# lego +lego --server "$DIRECTORY_URL" --email ops@example.com --accept-tos \ + --eab --kid "$KID" --hmac "$HMAC_KEY" \ + --domains web.example.com --http --http.webroot /var/www/html run +``` + +lego and acme.sh refuse a directory that is not `https`. All three are run +against the server in CI, as released (`make live-acme`). + +## What the template decides + +The CSR goes through the same rules as a request made in the console: the +template's names, key types, key usages and lifetime, and the estate-wide +policy above it. A name the template does not allow is refused when the order +is finalised, with the template's reason. The lifetime is the template's, so an +order that names `notBefore` or `notAfter` is refused. The CSR must ask for +exactly the names ordered. + +The certificate is recorded with `discovered_via: ACME` and +`key_custody: EXTERNAL`, and with automatic renewal off: the client renews it, +and CertPilot holds no key to renew it with. + +## Proof of control + +By default the credential is the authorisation. The template's name rules +bound what it covers, and the client is not asked to prove anything further. + +A credential made with **Require HTTP-01 or DNS-01** (`"require_challenge": +true`) also makes the client prove each name before anything is issued: + +- **HTTP-01**: a file at `http:///.well-known/acme-challenge/`. + Fetched on port 80. Redirects are not followed, so a name whose web server + redirects port 80 to https needs DNS-01. +- **DNS-01**: a TXT record at `_acme-challenge.`. The only one a wildcard + can use. + +The core looks the name up with `acme.challenges.dns_resolver`, or the system's +resolver when that is not set, so point it at the resolver that sees your +internal zones. A failed proof ends the order, and the client orders again. +Proving a name does not let a template issue what it does not allow: the +template's rules still apply when the order is finalised. + +TLS-ALPN-01 is not offered. + +## Renewal + +A client renews by ordering again. An order from the same account for the same +names and key type as a live certificate is a renewal: it is written over that +certificate's record, as the core's own renewals are. The renewal count goes +up, the old fingerprint is kept, and the check that the new certificate reached +its servers is scheduled. Different names, another key type or another account +make a second certificate. + +The server also offers ACME Renewal Information (RFC 9773). A client that asks +`renewalInfo` is given the window in which the core would renew the +certificate itself: the template's renewal lead before expiry. A revoked +certificate gets a window that has already passed, which tells the client to +replace it now. certbot and lego both ask. lego also sends `replaces`, which +renews the record it names even when the names change. + +## Revocation + +```bash +certbot revoke --server "$DIRECTORY_URL" --cert-name web.example.com --reason keycompromise +``` + +The account that ordered a certificate may revoke it, and so may anyone signing +with the certificate's own private key. Other accounts on the same template may +not. The CA is told first and the inventory changes only if it agrees, as with +a revocation made in the console, and it is audited under the same +`certificate.revoked` action. The certificate a renewal replaced can still be +revoked: the CA is sent it, and the record, which now describes its successor, +is left as it is. + +## Cutting a client off + +Revoke its credential in Settings → ACME clients. The account it made is +deactivated in the same step, so the client can no longer order, renew or +revoke, and no new account can be made with the credential. Certificates it was +already issued stay valid; revoke them as well if the client's keys are no +longer trusted. + +An account can also roll its key over (RFC 8555 key change): the request has to +be signed by both the old key and the new one. + +## Limitations + +- No open registration. Every client needs a credential. +- No TLS-ALPN-01, and HTTP-01 does not follow redirects. +- Caddy and win-acme speak the same protocol and should work; neither has been + run against it. +- A client renews its own certificates. CertPilot cannot renew one early for + it, except by revoking it, which renewalInfo then tells the client to + replace. diff --git a/docs/agent.md b/docs/agent.md index 702fd6b..57ec952 100644 --- a/docs/agent.md +++ b/docs/agent.md @@ -1,14 +1,14 @@ --- editLink: false -lastUpdated: 2026-10-11T18:48:29Z +lastUpdated: 2026-10-18T16:58:03Z source: repo: certpilot/certpilot-agent path: docs/agent.md - commit: 8eea1ef7e94a2d32b1e053bf5cdbf1d62e8eee4b + commit: 723415db60f578c16f624aafee1ec20f0e27c499 --- - + # The host agent @@ -29,7 +29,7 @@ Everything else here follows from that. - [Installing where the server reads](#installing-where-the-server-reads) - [Inventory](#inventory) - [Running it](#running-it) -- [When the agent can't run as shipped](#when-the-agent-cant-run-as-shipped) +- [When the agent cannot run as shipped](#when-the-agent-cannot-run-as-shipped) - [What can go wrong](#what-can-go-wrong) --- @@ -634,7 +634,7 @@ Where it does not, run it as a user that owns the certificate directory. --- -## When the agent can't run as shipped +## When the agent cannot run as shipped Most hosts that "can't run the agent" can, with one setting changed. diff --git a/docs/gateways/acme.md b/docs/gateways/acme.md index f0cb173..2416077 100644 --- a/docs/gateways/acme.md +++ b/docs/gateways/acme.md @@ -1,14 +1,14 @@ --- editLink: false -lastUpdated: 2026-09-26T16:23:02Z +lastUpdated: 2026-10-18T16:50:02Z source: repo: certpilot/certpilot path: docs/gateways/acme.md - commit: 51f86f743471b396830863eeb47a93b0947e281a + commit: 924a408c35ec75e6908892086698aca6cf5dbff9 --- - + # The ACME gateway @@ -20,6 +20,9 @@ Implemented by against the `provider.v1` contract in [`certpilot-gateway-sdk`](https://github.com/certpilot/certpilot-gateway-sdk). +This page is CertPilot as an ACME client of a CA. For ACME clients getting +certificates from CertPilot, see [the ACME server](/acme-server). + Two things shape how this gateway is used, and both are properties of ACME rather than of CertPilot. diff --git a/guides/agents.md b/guides/agents.md index d6904f5..103f31f 100644 --- a/guides/agents.md +++ b/guides/agents.md @@ -1,4 +1,4 @@ - Two APIs, kept completely apart. diff --git a/guides/ca-accounts-gateways.md b/guides/ca-accounts-gateways.md index cdecaea..b2d7f40 100644 --- a/guides/ca-accounts-gateways.md +++ b/guides/ca-accounts-gateways.md @@ -1,4 +1,4 @@ - ## Registering diff --git a/guides/certificate-transparency.md b/guides/certificate-transparency.md index 7a6cac6..fec3edb 100644 --- a/guides/certificate-transparency.md +++ b/guides/certificate-transparency.md @@ -1,4 +1,4 @@ - This is the half of discovery a network scan cannot reach. A scan answers "what diff --git a/guides/certificates.md b/guides/certificates.md index c1f1785..19f5e1b 100644 --- a/guides/certificates.md +++ b/guides/certificates.md @@ -1,4 +1,4 @@ - `GET /certificates` filters on `status`, `environment`, `common_name`, diff --git a/guides/cloud-inventory.md b/guides/cloud-inventory.md index 7f4b2fa..17b886a 100644 --- a/guides/cloud-inventory.md +++ b/guides/cloud-inventory.md @@ -1,4 +1,4 @@ - The third place certificates hide. A scan finds what is **served**; Certificate diff --git a/guides/dashboard.md b/guides/dashboard.md index 46a7c75..49cf574 100644 --- a/guides/dashboard.md +++ b/guides/dashboard.md @@ -1,4 +1,4 @@ - ## Statistics diff --git a/guides/deployment.md b/guides/deployment.md index 5f59499..45fcfa5 100644 --- a/guides/deployment.md +++ b/guides/deployment.md @@ -1,4 +1,4 @@ - Everything above deployment in this document observes. This changes something diff --git a/guides/discovery.md b/guides/discovery.md index 86d1679..9bc13a5 100644 --- a/guides/discovery.md +++ b/guides/discovery.md @@ -1,4 +1,4 @@ - Scanning is operator-gated because it opens connections to third-party diff --git a/guides/display-tokens.md b/guides/display-tokens.md index 1dbe0ca..d979bfb 100644 --- a/guides/display-tokens.md +++ b/guides/display-tokens.md @@ -1,4 +1,4 @@ - Create: diff --git a/guides/live-event-stream.md b/guides/live-event-stream.md index 922da2d..e1be5e2 100644 --- a/guides/live-event-stream.md +++ b/guides/live-event-stream.md @@ -1,4 +1,4 @@ - Opens with a `retry:` directive and a `snapshot` event carrying current diff --git a/guides/notification-channels.md b/guides/notification-channels.md index 68359a3..96a3350 100644 --- a/guides/notification-channels.md +++ b/guides/notification-channels.md @@ -1,4 +1,4 @@ - Three channel types have a notifier: `slack`, `webhook`, `email`. Migration 001 diff --git a/guides/pki-ca-management.md b/guides/pki-ca-management.md index badd340..845f22b 100644 --- a/guides/pki-ca-management.md +++ b/guides/pki-ca-management.md @@ -1,4 +1,4 @@ - ## Importing the CAs behind a CA account diff --git a/guides/policies.md b/guides/policies.md index 5e23713..1ff498d 100644 --- a/guides/policies.md +++ b/guides/policies.md @@ -1,4 +1,4 @@ - Rule types, all of which are evaluated: diff --git a/guides/renewal-queue.md b/guides/renewal-queue.md index 3486aee..cb10c20 100644 --- a/guides/renewal-queue.md +++ b/guides/renewal-queue.md @@ -1,4 +1,4 @@ - Renewal is the only part of this system that changes the world. Everything else diff --git a/routes.json b/routes.json index f5b54b2..a5029de 100644 --- a/routes.json +++ b/routes.json @@ -3828,8 +3828,168 @@ "operator", "admin" ], - "route_count": 126, + "route_count": 129, "routes": [ + { + "auth": "bearer", + "display_token": false, + "handler": "acmeHandler.List", + "method": "GET", + "min_role": "admin", + "note": "", + "path": "/api/v1/acme/credentials", + "responses": [ + { + "status": 200, + "shape": "object", + "keys": [ + { + "name": "data", + "type": "any", + "required": false + }, + { + "name": "enabled", + "type": "any", + "required": false + } + ] + }, + { + "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the ACME credentials", + "could not read the templates" + ] + } + ], + "section": "ACME credentials", + "source_line": 411 + }, + { + "auth": "bearer", + "display_token": false, + "handler": "acmeHandler.Create", + "method": "POST", + "min_role": "admin", + "note": "", + "path": "/api/v1/acme/credentials", + "request": { + "name": "mintACMECredentialRequest", + "fields": [ + { + "name": "name", + "type": "string", + "go_type": "string", + "required": false + }, + { + "name": "template", + "type": "string", + "go_type": "string", + "required": false, + "doc": "Template is a slug or an id." + }, + { + "name": "require_challenge", + "type": "boolean", + "go_type": "bool", + "required": false, + "doc": "RequireChallenge makes the client prove each name by HTTP-01 or DNS-01." + } + ] + }, + "responses": [ + { + "status": 201, + "shape": "object", + "keys": [ + { + "name": "data", + "type": "any", + "required": false + }, + { + "name": "directory_url", + "type": "any", + "required": false + }, + { + "name": "hmac_key", + "type": "any", + "required": false + }, + { + "name": "message", + "type": "string", + "required": false, + "value": "Copy the hmac_key now. It is not kept in a form that can be shown again." + } + ] + }, + { + "status": 400, + "errors": [ + "a name and a template are required — the name is what identifies which client to revoke" + ] + }, + { + "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the template", + "could not generate a key", + "could not generate a key id", + "could not seal the key", + "could not store the credential" + ] + } + ], + "section": "ACME credentials", + "source_line": 412 + }, + { + "auth": "bearer", + "display_token": false, + "handler": "acmeHandler.Revoke", + "method": "DELETE", + "min_role": "admin", + "note": "", + "path": "/api/v1/acme/credentials/:id", + "path_params": [ + "id" + ], + "responses": [ + { + "status": 200, + "shape": "object", + "keys": [ + { + "name": "message", + "type": "string", + "required": false, + "value": "Revoked. The account it made can no longer order, and no new one can be made with it." + } + ] + }, + { + "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the ACME credential" + ] + } + ], + "section": "ACME credentials", + "source_line": 413 + }, { "auth": "bearer", "display_token": true, @@ -3896,7 +4056,7 @@ } ], "section": "Agents", - "source_line": 362 + "source_line": 383 }, { "auth": "bearer", @@ -3933,7 +4093,7 @@ } ], "section": "Agents", - "source_line": 380 + "source_line": 401 }, { "auth": "bearer", @@ -4008,7 +4168,7 @@ } ], "section": "Agents", - "source_line": 381 + "source_line": 402 }, { "auth": "bearer", @@ -4039,7 +4199,7 @@ } ], "section": "Agents", - "source_line": 382 + "source_line": 403 }, { "auth": "bearer", @@ -4077,7 +4237,7 @@ } ], "section": "Agents", - "source_line": 373 + "source_line": 394 }, { "auth": "bearer", @@ -4179,7 +4339,7 @@ } ], "section": "Agents", - "source_line": 374 + "source_line": 395 }, { "auth": "bearer", @@ -4210,7 +4370,7 @@ } ], "section": "Agents", - "source_line": 375 + "source_line": 396 }, { "auth": "bearer", @@ -4269,7 +4429,7 @@ } ], "section": "Agents", - "source_line": 367 + "source_line": 388 }, { "auth": "agent-signature", @@ -4335,10 +4495,16 @@ }, { "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the agent" + ] } ], "section": "The agent API", - "source_line": 144 + "source_line": 165 }, { "auth": "agent-signature", @@ -4364,11 +4530,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the agent" + ] } ], "section": "The agent API", - "source_line": 153 + "source_line": 174 }, { "auth": "agent-signature", @@ -4445,7 +4614,11 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the agent", + "could not read the deployment job" + ] }, { "status": 503, @@ -4455,7 +4628,7 @@ } ], "section": "The agent API", - "source_line": 154 + "source_line": 175 }, { "auth": "agent-enrolment-token", @@ -4565,7 +4738,7 @@ } ], "section": "The agent API", - "source_line": 133 + "source_line": 154 }, { "auth": "agent-signature", @@ -4629,7 +4802,7 @@ } ], "section": "The agent API", - "source_line": 136 + "source_line": 157 }, { "auth": "agent-signature", @@ -4700,11 +4873,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the agent" + ] } ], "section": "The agent API", - "source_line": 148 + "source_line": 169 }, { "auth": "agent-signature", @@ -4782,11 +4958,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the agent" + ] } ], "section": "The agent API", - "source_line": 140 + "source_line": 161 }, { "auth": "bearer", @@ -4845,7 +5024,7 @@ } ], "section": "Agents", - "source_line": 352 + "source_line": 373 }, { "auth": "bearer", @@ -4880,11 +5059,14 @@ ] }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the agent" + ] } ], "section": "Agents", - "source_line": 356 + "source_line": 377 }, { "auth": "bearer", @@ -4917,10 +5099,16 @@ }, { "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the agent" + ] } ], "section": "Agents", - "source_line": 353 + "source_line": 374 }, { "auth": "bearer", @@ -4950,10 +5138,16 @@ }, { "status": 409 + }, + { + "status": 500, + "errors": [ + "could not read the agent" + ] } ], "section": "Agents", - "source_line": 355 + "source_line": 376 }, { "auth": "bearer", @@ -4988,7 +5182,7 @@ } ], "section": "Audit record integrity", - "source_line": 197 + "source_line": 218 }, { "auth": "none", @@ -5074,7 +5268,7 @@ } ], "section": "Sign-in discovery", - "source_line": 118 + "source_line": 120 }, { "auth": "none", @@ -5092,7 +5286,7 @@ } ], "section": "Sign-in discovery", - "source_line": 104 + "source_line": 106 }, { "auth": "none", @@ -5143,7 +5337,7 @@ } ], "section": "Sign-in discovery", - "source_line": 110 + "source_line": 112 }, { "auth": "bearer", @@ -5168,7 +5362,7 @@ } ], "section": "The caller's own identity", - "source_line": 181 + "source_line": 202 }, { "auth": "bearer", @@ -5232,7 +5426,7 @@ } ], "section": "The caller's own identity", - "source_line": 185 + "source_line": 206 }, { "auth": "bearer", @@ -5265,7 +5459,7 @@ } ], "section": "CA Accounts \u0026 Gateways", - "source_line": 238 + "source_line": 259 }, { "auth": "bearer", @@ -5384,7 +5578,7 @@ } ], "section": "CA Accounts \u0026 Gateways", - "source_line": 239 + "source_line": 260 }, { "auth": "bearer", @@ -5415,7 +5609,7 @@ } ], "section": "CA Accounts \u0026 Gateways", - "source_line": 241 + "source_line": 262 }, { "auth": "bearer", @@ -5453,12 +5647,18 @@ { "status": 404 }, + { + "status": 500, + "errors": [ + "could not read the CA account" + ] + }, { "status": 503 } ], "section": "CA Accounts \u0026 Gateways", - "source_line": 240 + "source_line": 261 }, { "auth": "bearer", @@ -5520,11 +5720,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the CA account" + ] } ], "section": "CA Accounts \u0026 Gateways", - "source_line": 244 + "source_line": 265 }, { "auth": "bearer", @@ -5557,7 +5760,7 @@ } ], "section": "Certificate templates", - "source_line": 441 + "source_line": 472 }, { "auth": "bearer", @@ -5778,7 +5981,7 @@ } ], "section": "Certificate templates", - "source_line": 443 + "source_line": 474 }, { "auth": "bearer", @@ -5811,11 +6014,14 @@ "status": 409 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate template" + ] } ], "section": "Certificate templates", - "source_line": 445 + "source_line": 476 }, { "auth": "bearer", @@ -5834,10 +6040,16 @@ }, { "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the certificate template" + ] } ], "section": "Certificate templates", - "source_line": 442 + "source_line": 473 }, { "auth": "bearer", @@ -6060,11 +6272,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate template" + ] } ], "section": "Certificate templates", - "source_line": 444 + "source_line": 475 }, { "auth": "bearer", @@ -6121,7 +6336,7 @@ } ], "section": "Certificates", - "source_line": 200 + "source_line": 221 }, { "auth": "bearer", @@ -6266,7 +6481,7 @@ } ], "section": "Certificates", - "source_line": 201 + "source_line": 222 }, { "auth": "bearer", @@ -6315,11 +6530,14 @@ ] }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate" + ] } ], "section": "Certificates", - "source_line": 215 + "source_line": 236 }, { "auth": "bearer", @@ -6340,10 +6558,16 @@ }, { "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the certificate" + ] } ], "section": "Certificates", - "source_line": 202 + "source_line": 223 }, { "auth": "bearer", @@ -6399,11 +6623,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate" + ] } ], "section": "Certificates", - "source_line": 205 + "source_line": 226 }, { "auth": "bearer", @@ -6448,11 +6675,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate" + ] } ], "section": "Deployment", - "source_line": 334 + "source_line": 355 }, { "auth": "bearer", @@ -6489,11 +6719,14 @@ ] }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate" + ] } ], "section": "Certificates", - "source_line": 214 + "source_line": 235 }, { "auth": "bearer", @@ -6532,11 +6765,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate" + ] } ], "section": "Certificates", - "source_line": 206 + "source_line": 227 }, { "auth": "bearer", @@ -6577,11 +6813,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate" + ] } ], "section": "Renewal queue", - "source_line": 305 + "source_line": 326 }, { "auth": "bearer", @@ -6614,7 +6853,7 @@ { "name": "certificate", "type": "Certificate", - "go_type": "*Certificate", + "go_type": "*store.Certificate", "required": false }, { @@ -6648,17 +6887,17 @@ ] }, { - "status": 500 - }, - { - "status": 502, + "status": 500, "errors": [ - "the CA account this certificate was issued by could not be read" + "could not read the certificate" ] + }, + { + "status": 502 } ], "section": "Certificates", - "source_line": 213 + "source_line": 234 }, { "auth": "bearer", @@ -6698,11 +6937,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate" + ] } ], "section": "Deployment", - "source_line": 327 + "source_line": 348 }, { "auth": "bearer", @@ -6770,11 +7012,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate" + ] } ], "section": "Deployment", - "source_line": 328 + "source_line": 349 }, { "auth": "bearer", @@ -6807,11 +7052,14 @@ ] }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate deployment" + ] } ], "section": "Deployment", - "source_line": 329 + "source_line": 350 }, { "auth": "bearer", @@ -6873,11 +7121,14 @@ ] }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the certificate" + ] } ], "section": "Renewal queue", - "source_line": 307 + "source_line": 328 }, { "auth": "bearer", @@ -6942,7 +7193,7 @@ } ], "section": "Cloud inventory", - "source_line": 291 + "source_line": 312 }, { "auth": "bearer", @@ -6995,7 +7246,7 @@ } ], "section": "Cloud inventory", - "source_line": 284 + "source_line": 305 }, { "auth": "bearer", @@ -7068,7 +7319,7 @@ } ], "section": "Cloud inventory", - "source_line": 285 + "source_line": 306 }, { "auth": "bearer", @@ -7097,11 +7348,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the cloud connection" + ] } ], "section": "Cloud inventory", - "source_line": 287 + "source_line": 308 }, { "auth": "bearer", @@ -7170,11 +7424,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the cloud connection" + ] } ], "section": "Cloud inventory", - "source_line": 286 + "source_line": 307 }, { "auth": "bearer", @@ -7226,7 +7483,10 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the cloud connection" + ] }, { "status": 502, @@ -7247,7 +7507,7 @@ } ], "section": "Cloud inventory", - "source_line": 290 + "source_line": 311 }, { "auth": "bearer", @@ -7331,11 +7591,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the cloud certificate" + ] } ], "section": "Cloud inventory", - "source_line": 292 + "source_line": 313 }, { "auth": "bearer", @@ -7389,7 +7652,7 @@ } ], "section": "Certificate Transparency", - "source_line": 278 + "source_line": 299 }, { "auth": "bearer", @@ -7437,7 +7700,7 @@ } ], "section": "Certificate Transparency", - "source_line": 273 + "source_line": 294 }, { "auth": "bearer", @@ -7503,7 +7766,7 @@ } ], "section": "Certificate Transparency", - "source_line": 274 + "source_line": 295 }, { "auth": "bearer", @@ -7532,11 +7795,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the CT monitor" + ] } ], "section": "Certificate Transparency", - "source_line": 276 + "source_line": 297 }, { "auth": "bearer", @@ -7599,11 +7865,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the CT monitor" + ] } ], "section": "Certificate Transparency", - "source_line": 275 + "source_line": 296 }, { "auth": "bearer", @@ -7650,7 +7919,10 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the CT monitor" + ] }, { "status": 502, @@ -7671,7 +7943,7 @@ } ], "section": "Certificate Transparency", - "source_line": 277 + "source_line": 298 }, { "auth": "bearer", @@ -7730,7 +8002,7 @@ } ], "section": "Dashboard", - "source_line": 190 + "source_line": 211 }, { "auth": "bearer", @@ -7763,7 +8035,7 @@ } ], "section": "Dashboard", - "source_line": 189 + "source_line": 210 }, { "auth": "bearer", @@ -7784,7 +8056,7 @@ } ], "section": "Dashboard", - "source_line": 188 + "source_line": 209 }, { "auth": "bearer", @@ -7827,7 +8099,7 @@ } ], "section": "Deployment", - "source_line": 317 + "source_line": 338 }, { "auth": "bearer", @@ -7900,7 +8172,7 @@ } ], "section": "Deployment", - "source_line": 318 + "source_line": 339 }, { "auth": "bearer", @@ -7929,11 +8201,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the deployment target" + ] } ], "section": "Deployment", - "source_line": 323 + "source_line": 344 }, { "auth": "bearer", @@ -8011,11 +8286,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the deployment target" + ] } ], "section": "Deployment", - "source_line": 319 + "source_line": 340 }, { "auth": "bearer", @@ -8089,7 +8367,7 @@ } ], "section": "Deployment", - "source_line": 344 + "source_line": 365 }, { "auth": "bearer", @@ -8120,10 +8398,16 @@ }, { "status": 409 + }, + { + "status": 500, + "errors": [ + "could not read the deployment job" + ] } ], "section": "Deployment", - "source_line": 346 + "source_line": 367 }, { "auth": "bearer", @@ -8151,10 +8435,16 @@ }, { "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the deployment job" + ] } ], "section": "Deployment", - "source_line": 345 + "source_line": 366 }, { "auth": "bearer", @@ -8260,11 +8550,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the discovery result" + ] } ], "section": "Discovery", - "source_line": 252 + "source_line": 273 }, { "auth": "bearer", @@ -8333,7 +8626,7 @@ } ], "section": "Discovery", - "source_line": 260 + "source_line": 281 }, { "auth": "bearer", @@ -8456,7 +8749,7 @@ } ], "section": "Discovery", - "source_line": 251 + "source_line": 272 }, { "auth": "bearer", @@ -8500,7 +8793,7 @@ } ], "section": "Discovery", - "source_line": 255 + "source_line": 276 }, { "auth": "bearer", @@ -8560,11 +8853,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the discovery scan" + ] } ], "section": "Discovery", - "source_line": 256 + "source_line": 277 }, { "auth": "bearer", @@ -8597,10 +8893,16 @@ }, { "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the discovery scan" + ] } ], "section": "Discovery", - "source_line": 259 + "source_line": 280 }, { "auth": "bearer", @@ -8638,7 +8940,7 @@ } ], "section": "Discovery", - "source_line": 264 + "source_line": 285 }, { "auth": "bearer", @@ -8711,7 +9013,7 @@ } ], "section": "Discovery", - "source_line": 265 + "source_line": 286 }, { "auth": "bearer", @@ -8740,11 +9042,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the discovery schedule" + ] } ], "section": "Discovery", - "source_line": 267 + "source_line": 288 }, { "auth": "bearer", @@ -8814,11 +9119,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the discovery schedule" + ] } ], "section": "Discovery", - "source_line": 266 + "source_line": 287 }, { "auth": "bearer", @@ -8865,11 +9173,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the discovery schedule" + ] } ], "section": "Discovery", - "source_line": 268 + "source_line": 289 }, { "auth": "bearer", @@ -8901,7 +9212,7 @@ } ], "section": "Display Tokens", - "source_line": 400 + "source_line": 431 }, { "auth": "bearer", @@ -8963,7 +9274,7 @@ } ], "section": "Display Tokens", - "source_line": 401 + "source_line": 432 }, { "auth": "bearer", @@ -8991,10 +9302,16 @@ }, { "status": 404 + }, + { + "status": 500, + "errors": [ + "could not revoke the display token" + ] } ], "section": "Display Tokens", - "source_line": 402 + "source_line": 433 }, { "auth": "bearer", @@ -9013,7 +9330,7 @@ } ], "section": "Live event stream", - "source_line": 174 + "source_line": 195 }, { "auth": "bearer", @@ -9042,7 +9359,7 @@ } ], "section": "CA Accounts \u0026 Gateways", - "source_line": 245 + "source_line": 266 }, { "auth": "bearer", @@ -9060,7 +9377,7 @@ } ], "section": "The caller's own identity", - "source_line": 180 + "source_line": 201 }, { "auth": "bearer", @@ -9098,7 +9415,7 @@ } ], "section": "Custom metadata fields", - "source_line": 422 + "source_line": 453 }, { "auth": "bearer", @@ -9177,7 +9494,7 @@ } ], "section": "Custom metadata fields", - "source_line": 423 + "source_line": 454 }, { "auth": "bearer", @@ -9212,11 +9529,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the metadata field" + ] } ], "section": "Custom metadata fields", - "source_line": 425 + "source_line": 456 }, { "auth": "bearer", @@ -9294,11 +9614,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the metadata field" + ] } ], "section": "Custom metadata fields", - "source_line": 424 + "source_line": 455 }, { "auth": "bearer", @@ -9341,7 +9664,7 @@ } ], "section": "Notification channels", - "source_line": 409 + "source_line": 440 }, { "auth": "bearer", @@ -9421,7 +9744,7 @@ } ], "section": "Notification channels", - "source_line": 410 + "source_line": 441 }, { "auth": "bearer", @@ -9451,11 +9774,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the notification channel" + ] } ], "section": "Notification channels", - "source_line": 412 + "source_line": 443 }, { "auth": "bearer", @@ -9531,11 +9857,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the notification channel" + ] } ], "section": "Notification channels", - "source_line": 411 + "source_line": 442 }, { "auth": "bearer", @@ -9568,6 +9897,12 @@ { "status": 404 }, + { + "status": 500, + "errors": [ + "could not read the notification channel" + ] + }, { "status": 502, "shape": "object", @@ -9581,7 +9916,7 @@ } ], "section": "Notification channels", - "source_line": 415 + "source_line": 446 }, { "auth": "bearer", @@ -9631,7 +9966,7 @@ } ], "section": "PKI / CA Management", - "source_line": 218 + "source_line": 239 }, { "auth": "bearer", @@ -9703,7 +10038,7 @@ } ], "section": "PKI / CA Management", - "source_line": 219 + "source_line": 240 }, { "auth": "bearer", @@ -9734,7 +10069,7 @@ } ], "section": "PKI / CA Management", - "source_line": 228 + "source_line": 249 }, { "auth": "bearer", @@ -9755,10 +10090,16 @@ }, { "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the CA authority" + ] } ], "section": "PKI / CA Management", - "source_line": 225 + "source_line": 246 }, { "auth": "bearer", @@ -9795,7 +10136,7 @@ } ], "section": "PKI / CA Management", - "source_line": 233 + "source_line": 254 }, { "auth": "bearer", @@ -9859,11 +10200,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the CA authority" + ] } ], "section": "PKI / CA Management", - "source_line": 232 + "source_line": 253 }, { "auth": "bearer", @@ -9899,7 +10243,7 @@ } ], "section": "PKI / CA Management", - "source_line": 234 + "source_line": 255 }, { "auth": "bearer", @@ -9930,7 +10274,7 @@ } ], "section": "PKI / CA Management", - "source_line": 226 + "source_line": 247 }, { "auth": "bearer", @@ -9953,11 +10297,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the CA authority" + ] } ], "section": "PKI / CA Management", - "source_line": 227 + "source_line": 248 }, { "auth": "bearer", @@ -10008,11 +10355,14 @@ "status": 404 }, { - "status": 500 + "status": 500, + "errors": [ + "could not read the CA authority" + ] } ], "section": "PKI / CA Management", - "source_line": 235 + "source_line": 256 }, { "auth": "bearer", @@ -10062,7 +10412,7 @@ } ], "section": "PKI / CA Management", - "source_line": 224 + "source_line": 245 }, { "auth": "bearer", @@ -10089,7 +10439,7 @@ } ], "section": "PKI / CA Management", - "source_line": 229 + "source_line": 250 }, { "auth": "bearer", @@ -10122,7 +10472,7 @@ } ], "section": "Policies", - "source_line": 428 + "source_line": 459 }, { "auth": "bearer", @@ -10195,7 +10545,7 @@ } ], "section": "Policies", - "source_line": 430 + "source_line": 461 }, { "auth": "bearer", @@ -10226,7 +10576,7 @@ } ], "section": "Policies", - "source_line": 432 + "source_line": 463 }, { "auth": "bearer", @@ -10247,10 +10597,16 @@ }, { "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the policy" + ] } ], "section": "Policies", - "source_line": 429 + "source_line": 460 }, { "auth": "bearer", @@ -10326,7 +10682,7 @@ } ], "section": "Policies", - "source_line": 431 + "source_line": 462 }, { "auth": "bearer", @@ -10358,7 +10714,7 @@ } ], "section": "Deployment", - "source_line": 338 + "source_line": 359 }, { "auth": "bearer", @@ -10377,7 +10733,7 @@ } ], "section": "Deployment", - "source_line": 342 + "source_line": 363 }, { "auth": "bearer", @@ -10428,7 +10784,7 @@ } ], "section": "Deployment", - "source_line": 339 + "source_line": 360 }, { "auth": "bearer", @@ -10495,7 +10851,7 @@ } ], "section": "Renewal queue", - "source_line": 297 + "source_line": 318 }, { "auth": "bearer", @@ -10526,10 +10882,16 @@ }, { "status": 409 + }, + { + "status": 500, + "errors": [ + "could not read the renewal job" + ] } ], "section": "Renewal queue", - "source_line": 302 + "source_line": 323 }, { "auth": "bearer", @@ -10562,10 +10924,16 @@ }, { "status": 404 + }, + { + "status": 500, + "errors": [ + "could not read the renewal job" + ] } ], "section": "Renewal queue", - "source_line": 298 + "source_line": 319 }, { "auth": "bearer", @@ -10598,7 +10966,7 @@ } ], "section": "Accounts", - "source_line": 389 + "source_line": 420 }, { "auth": "bearer", @@ -10681,7 +11049,7 @@ } ], "section": "Accounts", - "source_line": 390 + "source_line": 421 }, { "auth": "bearer", @@ -10746,7 +11114,7 @@ } ], "section": "Accounts", - "source_line": 391 + "source_line": 422 }, { "auth": "bearer", @@ -10785,12 +11153,13 @@ { "status": 500, "errors": [ + "could not read the account", "could not generate a password" ] } ], "section": "Accounts", - "source_line": 394 + "source_line": 425 }, { "auth": "none", @@ -10801,7 +11170,7 @@ "note": "Public, and deliberately says nothing about internals.", "path": "/healthz", "section": "Health", - "source_line": 73 + "source_line": 75 } ], "source": "core/api/router.go" diff --git a/scripts/sync-pages.mjs b/scripts/sync-pages.mjs index 07735b7..31634a2 100644 --- a/scripts/sync-pages.mjs +++ b/scripts/sync-pages.mjs @@ -123,6 +123,7 @@ const PAGES = [ { source: 'troubleshooting.md', sentinel: '# Troubleshooting' }, { source: 'status.md', sentinel: '# Implementation status' }, { source: 'templates.md', sentinel: '# Certificate templates' }, + { source: 'acme-server.md', sentinel: '# The ACME server' }, // The walkthroughs. Sentinels are the H1: they are the only headings on those // pages that are not shared with a sibling — every one of them has a