Plattform
ScaiWave ScaiGrid ScaiCore ScaiBot ScaiDrive ScaiKey Modelle Tools & Services
Lösungen
Organisationen Entwickler Internet Service Provider Managed Service Provider AI-in-a-Box
Ressourcen
Support Documentation Blog Downloads
Unternehmen
Über uns Forschung Karriere Investieren Kontakt
Anmelden

Certificate Agent

A small daemon-less program that keeps TLS certificates current on a host: renews them before they expire, writes the bundle where your service expects it, and reloads that service — only when something actually changed.

It closes the last gap in certificate automation. ScaiVault already renews certificates on a schedule, but until now getting the renewed material onto the machine that serves it was manual.

Install#

bash
1
sudo ./install.sh scaivault_agent-1.0.0-py3-none-any.whl

Get the wheel from https://www.scailabs.ai/downloads under ScaiVault.

It depends on cryptography and httpx and nothing else — it does not require the ScaiVault server, so installing it on a web server does not put a secrets manager's database drivers in your DMZ. Python 3.11+, Linux/systemd.

Enrol the host#

Rather than copying a long-lived API key onto every machine, mint a short-lived token and let the host trade it for its own credential:

bash
1
2
3
4
5
6
7
# on an admin machine — single-use, expires in an hour
curl -sS -X POST https://scaivault.scailabs.ai/api/v1/enrollment/tokens \
  -H "X-API-Key: $ADMIN_KEY" -H 'Content-Type: application/json' \
  -d '{"roles":["pki-agent"],"ttl_seconds":3600,"fqdn_pattern":"web*.example.com"}'

# on the host
scaivault-agent enroll --token enroll_...

The host receives its own service account, named after its hostname, so it can be revoked individually. See Enrollment for the full API.

A long-lived key still works if you prefer: write it to /etc/scaivault/agent.key with mode 0600.

Add a certificate#

bash
1
scaivault-agent add www.example.com --service nginx

This infers the four things you would otherwise have to look up — the certificate path, the CA to sign against, the output directory, and the correct reload command — then prints the config to paste:

ini
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[[certificate]]
path        = "hosts/web01.example.com/www.example.com"
mode        = "csr"
ca_id       = "ca_intermediate"
common_name = "www.example.com"
out_dir     = "/etc/ssl/scaivault/www.example.com"
reload      = "nginx -t && systemctl reload nginx"

Add to your nginx config:

    ssl_certificate     /etc/ssl/scaivault/www.example.com/fullchain.pem;
    ssl_certificate_key /etc/ssl/scaivault/www.example.com/privkey.pem;

Then:

bash
1
2
3
scaivault-agent --dry-run     # says what it would do, changes nothing
scaivault-agent               # issue and deploy
sudo systemctl enable --now scaivault-agent.timer

scaivault-agent services lists the services it knows and marks the ones installed on this host.

Key modes#

Set per certificate, because not every certificate type can use a locally generated key.

Mode Key handling Scope needed
csr (default) Generated on the host; only a CSR is sent. The private key never crosses the network. pki:write
managed Key lives in ScaiVault and is downloaded. Needed where the key is not the agent's to generate — ACME above all. pki:export-key

Prefer csr. A stolen agent credential can then request certificates but cannot exfiltrate existing private keys. Scope managed service accounts to only the paths that host serves.

The agent will not silently fall back from csr to managed: a CSR that will not sign is an error to report, not to work around.

Supported services#

Each profile knows how to reload safely and which files the service wants.

Service Files it uses Reload
nginx fullchain.pem + privkey.pem nginx -t && systemctl reload nginx
Apache certificate.pem + chain.pem + privkey.pem apachectl configtest && systemctl reload apache2
HAProxy combined haproxy.pem (built by the hook) haproxy -c -f … && systemctl reload haproxy
Postfix fullchain.pem + privkey.pem (group-readable) postfix check && systemctl reload postfix
Dovecot fullchain.pem + privkey.pem (group-readable) doveconf -n && systemctl reload dovecot

Every reload validates the configuration first. That turns "the reload broke the site" into "the reload refused, the old certificate is still serving".

Postfix and Dovecot run unprivileged, so those profiles set a group and a 0640 key mode automatically — a root-only 0600 key would silently break them.

Files written#

File Contents
certificate.pem leaf only
chain.pem issuer chain
fullchain.pem leaf + chain
privkey.pem private key, 0600 unless the service needs otherwise

Writes are atomic: each file goes to a temporary file in the same directory with ownership and mode already applied, then rename(). A service reloading concurrently never reads a half-written PEM.

Scheduling#

The agent is a one-shot process, not a daemon. The bundled systemd timer runs it twice daily with up to an hour of jitter so a fleet does not stampede the API. A run with nothing due makes no API calls at all.

Exit codes#

Code Meaning
0 everything current, or renewed and reloaded cleanly
1 configuration error
2 a certificate failed to renew, or a reload hook failed
3 ScaiVault unreachable

2 and 3 are distinct on purpose: "one host's reload hook is broken" is a different alert from "the CA is down".

Fleet visibility#

After each run the agent reports what it manages, so you can see which hosts are about to break without ssh-ing into each one. See Fleet.

Set report_status = false under [agent] for hosts that must not phone home. A check-in failure never affects renewal — by that point the certificates are already deployed.

Other platforms#

Linux/systemd today. The wire contract — config schema, state file, exit codes, reload semantics — is specified language-agnostically in PROTOCOL.md inside the distribution, so a Go or C# build for Windows/IIS is a port rather than a redesign.

What's next#

  • Enrollment — minting and redeeming tokens.
  • Fleet — which hosts are about to break.
  • PKI — the certificate API the agent uses.
Updated 2026-08-11 21:00:56 View source (.md) rev 2