Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions docs/.vitepress/census-generated.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"routeCount": 126,
"sectionCount": 22,
"routeCount": 129,
"sectionCount": 23,
"roles": [
"viewer",
"auditor",
Expand All @@ -10,11 +10,11 @@
"methods": [
{
"method": "GET",
"count": 49
"count": 50
},
{
"method": "POST",
"count": 46
"count": 47
},
{
"method": "PATCH",
Expand All @@ -26,11 +26,11 @@
},
{
"method": "DELETE",
"count": 19
"count": 20
}
],
"displayTokenReadable": 41,
"documentedBodies": 42,
"documentedBodies": 43,
"modelCount": 31,
"source": "core/api/router.go",
"sections": [
Expand Down Expand Up @@ -102,6 +102,10 @@
"text": "Display Tokens",
"count": 3
},
{
"text": "ACME credentials",
"count": 3
},
{
"text": "Accounts",
"count": 4
Expand Down
2 changes: 1 addition & 1 deletion docs/.vitepress/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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/',
Expand Down
4 changes: 4 additions & 0 deletions docs/.vitepress/guide-sidebar.json
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@
"text": "The host agent",
"link": "/agent"
},
{
"text": "ACME clients, no agent",
"link": "/acme-server"
},
{
"text": "All platforms",
"link": "/platforms/"
Expand Down
5 changes: 5 additions & 0 deletions docs/.vitepress/sidebar-generated.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
170 changes: 170 additions & 0 deletions docs/acme-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
editLink: false
lastUpdated: 2026-10-18T16:50:02Z
source:
repo: certpilot/certpilot
path: docs/acme-server.md
commit: 924a408c35ec75e6908892086698aca6cf5dbff9
---

<!-- Synced from docs/acme-server.md in certpilot/certpilot at 924a408c35ec,
last changed 2026-10-18T16:50:02Z, by scripts/sync-pages.mjs. Edit it there, not here. -->

# 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/<template-slug>/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 [email protected]
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 [email protected]
acme.sh --issue --server "$DIRECTORY_URL" -d web.example.com -w /var/www/html

# lego
lego --server "$DIRECTORY_URL" --email [email protected] --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://<name>/.well-known/acme-challenge/<token>`.
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.<name>`. 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.
12 changes: 6 additions & 6 deletions docs/agent.md
Original file line number Diff line number Diff line change
@@ -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
---

<!-- Synced from docs/agent.md in certpilot/certpilot-agent at 8eea1ef7e94a,
last changed 2026-10-11T18:48:29Z, by scripts/sync-pages.mjs. Edit it there, not here. -->
<!-- Synced from docs/agent.md in certpilot/certpilot-agent at 723415db60f5,
last changed 2026-10-18T16:58:03Z, by scripts/sync-pages.mjs. Edit it there, not here. -->

# The host agent

Expand All @@ -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)

---
Expand Down Expand Up @@ -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.

Expand Down
11 changes: 7 additions & 4 deletions docs/gateways/acme.md
Original file line number Diff line number Diff line change
@@ -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
---

<!-- Synced from docs/gateways/acme.md in certpilot/certpilot at 51f86f743471,
last changed 2026-09-26T16:23:02Z, by scripts/sync-pages.mjs. Edit it there, not here. -->
<!-- Synced from docs/gateways/acme.md in certpilot/certpilot at 924a408c35ec,
last changed 2026-10-18T16:50:02Z, by scripts/sync-pages.mjs. Edit it there, not here. -->

# The ACME gateway

Expand All @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion guides/agents.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Agents". Edit it there, not here. -->

Two APIs, kept completely apart.
Expand Down
2 changes: 1 addition & 1 deletion guides/ca-accounts-gateways.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "CA accounts and gateways". Edit it there, not here. -->

## Registering
Expand Down
2 changes: 1 addition & 1 deletion guides/certificate-transparency.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Certificate Transparency". Edit it there, not here. -->

This is the half of discovery a network scan cannot reach. A scan answers "what
Expand Down
2 changes: 1 addition & 1 deletion guides/certificates.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Certificates". Edit it there, not here. -->

`GET /certificates` filters on `status`, `environment`, `common_name`,
Expand Down
2 changes: 1 addition & 1 deletion guides/cloud-inventory.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Cloud inventory". Edit it there, not here. -->

The third place certificates hide. A scan finds what is **served**; Certificate
Expand Down
2 changes: 1 addition & 1 deletion guides/dashboard.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Dashboard". Edit it there, not here. -->

## Statistics
Expand Down
2 changes: 1 addition & 1 deletion guides/deployment.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Deployment", "Cryptographic posture". Edit it there, not here. -->

Everything above deployment in this document observes. This changes something
Expand Down
2 changes: 1 addition & 1 deletion guides/discovery.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Discovery". Edit it there, not here. -->

Scanning is operator-gated because it opens connections to third-party
Expand Down
2 changes: 1 addition & 1 deletion guides/display-tokens.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Display tokens". Edit it there, not here. -->

Create:
Expand Down
2 changes: 1 addition & 1 deletion guides/live-event-stream.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Live event stream". Edit it there, not here. -->

Opens with a `retry:` directive and a `snapshot` event carrying current
Expand Down
2 changes: 1 addition & 1 deletion guides/notification-channels.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Notification channels". Edit it there, not here. -->

Three channel types have a notifier: `slack`, `webhook`, `email`. Migration 001
Expand Down
2 changes: 1 addition & 1 deletion guides/pki-ca-management.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "PKI and CA authorities", "Ownership and acknowledgement". Edit it there, not here. -->

## Importing the CAs behind a CA account
Expand Down
2 changes: 1 addition & 1 deletion guides/policies.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 51f86f743471, last changed 2026-09-26T16:23:02Z.
<!-- Synced from docs/api-reference.md in certpilot/certpilot at 1fb03b7e2286, last changed 2026-10-18T16:46:04Z.
Source heading: "Policies". Edit it there, not here. -->

Rule types, all of which are evaluated:
Expand Down
Loading
Loading