Agent configuration reference
Where the config lives
The agent reads config.json from its state directory, which the installer creates at /opt/tokentimer-agent/state/config.json with mode 0600, owned by the tokentimer-agent user.
The directory is resolved in this order:
- an explicit path passed to the agent,
- the
TOKENTIMER_AGENT_CONFIG_DIRenvironment variable, - the OS default.
Restart the agent after editing: sudo systemctl restart tokentimer-agent.
Every field is validated at load time and a malformed value aborts startup with a descriptive error naming the field. This is deliberate: a typo in an allowlist must never be silently ignored, because silently ignoring it is what would permit unsafe work.
Connection
| Field | Type | Default | Description |
|---|---|---|---|
serverUrl | string | required | Control-plane base URL. Must be https:. |
agentId | string | null | null | Set by the agent after registration. Do not hand-edit. |
protocolVersion | string | current | Agent protocol version advertised at registration. |
heartbeatIntervalMs | integer | 30000 | Heartbeat frequency. Also how quickly the agent notices retirement and signing-key rotation. |
pollIntervalMs | integer | 15000 | How often the agent polls for claimable jobs. |
caBundlePath | string | null | null | PEM CA bundle used to trust TokenTimer, for a TLS-inspecting egress proxy. Overridden by TOKENTIMER_AGENT_CA_BUNDLE. |
allowInsecureLocalHttp | boolean | false | Permits plain http: for loopback hosts only. Not applicable on Cloud. |
Capability declaration
These tell the control plane what this agent is willing and able to do, and are what job routing matches against:
| Field | Type | Default | Description |
|---|---|---|---|
declaredTargetSelectors | string[] | [] | Targets (hosts, domains) this agent handles. |
declaredCommandProfileNames | string[] | [] | Command profiles this agent offers, for example a certbot or acme.sh profile. |
Declaring a capability does not grant it. The local policy block still has to allow the concrete command, path, CA endpoint, and DNS zone.
Execution
| Field | Type | Default | Description |
|---|---|---|---|
execution.enabled | boolean | false | Master switch for doing real work. While false the agent still registers, heartbeats, and reports. |
execution.dryRun | boolean | true | Plan and report the steps a job would run, with no filesystem or process side effects. |
execution.keysDir | string | <configDir>/keys | Where locally generated private keys are written, mode 0600. Keys never leave the host. |
execution.replayStorePath | string | <configDir>/replay-store.json | Persisted nonce cache that makes replayed jobs unexecutable across restarts. |
execution.outboxDir | string | <configDir>/outbox | Durable queue for results that could not be delivered yet. |
execution.clockDriftToleranceMs | integer | 30000 | Accepted skew when validating a signed job's validity window. |
A fresh install has enabled: false and dryRun: true. Setting enabled: true alone still yields dry-run planning. Real execution requires enabled: true and dryRun: false. Validate a rollout in dry-run first; it exercises the entire trust chain (claim, signature verification, replay check, policy evaluation) without touching the host.
Policy (default-deny)
The policy block is the agent's own authorization boundary. It always wins over control-plane intent: a job that exceeds it is rejected locally, and the rejection is reported back as evidence.
| Field | Type | Description |
|---|---|---|
policy.allowedCommands | object | Map of profile name to { argv: [...] }. The argv template is the only shape the agent will execute. |
policy.allowedPaths | string[] | Absolute paths the agent may write certificates into. |
policy.allowedCaEndpoints | string[] | ACME CA endpoints the agent may talk to, including internal PKI. |
policy.allowedDnsZones | string[] | DNS zones the agent may publish challenge records in. |
policy.allowedDnsProviders | string[] | DNS provider ids, exact-match. See DNS-01 providers. |
{
"policy": {
"allowedCommands": {
"certbot-csr": { "argv": ["/usr/bin/certbot", "certonly", "--csr"] }
},
"allowedPaths": ["/etc/nginx/certs"],
"allowedCaEndpoints": ["https://acme-v02.api.letsencrypt.org/directory"],
"allowedDnsZones": ["example.com"],
"allowedDnsProviders": ["cloudflare"]
}
}
Requests to export private key material are rejected unconditionally. There is no policy field, config flag, or control-plane setting that permits it. That is the zero-custody invariant, not a default you can relax.
policy.allowedPaths and the systemd ReadWritePaths list are two independent gates, and a deploy needs both. The installer's --write-path manages the systemd side; this field manages the agent side.
Trust pinning
| Field | Type | Default | Description |
|---|---|---|---|
pinnedSigningKey | object | null | null | { signingKeyId, publicKeyPem } of the control-plane job-signing key the agent trusts. |
The agent pins the signing key on first contact and then refuses jobs signed by anything else. It is written by the agent, not by you. When TokenTimer rotates its signing key, the agent re-pins automatically after learning about the new key on heartbeat; no action is needed on your side.
Discovery
| Field | Type | Default | Description |
|---|---|---|---|
discovery.directories | string[] | none | Directories scanned for existing certificates to report into inventory. |
discovery.intervalMs | integer | 3600000 | Scan interval. |
Discovery is read-only: it reports what it finds and never modifies or moves certificates.
DNS and ACME
| Field | Type | Default | Description |
|---|---|---|---|
dnsProviders | object | null | null | Map of provider id to { credentialsFile } plus the reserved zoneProviderMap. |
dnsPropagation | object | see below | Propagation wait behaviour after publishing and cleanup. |
acmeAccounts | object | null | null | Map of account reference to { credentialsFile }. |
Credentials are always referenced by absolute path to a 0600 file, never inlined into config.json. The agent refuses a credentials file that is group- or other-readable on POSIX hosts.
Full per-provider fields, zone routing, and propagation tuning: DNS-01 providers.
Minimal working example
{
"serverUrl": "https://app.tokentimer.ch",
"declaredTargetSelectors": ["web01.example.com"],
"declaredCommandProfileNames": ["certbot-csr"],
"execution": {
"enabled": true,
"dryRun": false
},
"policy": {
"allowedCommands": {
"certbot-csr": { "argv": ["/usr/bin/certbot", "certonly", "--csr"] }
},
"allowedPaths": ["/etc/nginx/certs"],
"allowedCaEndpoints": ["https://acme-v02.api.letsencrypt.org/directory"],
"allowedDnsZones": ["example.com"],
"allowedDnsProviders": ["cloudflare"]
},
"dnsProviders": {
"cloudflare": { "credentialsFile": "/etc/tokentimer-agent/dns/cloudflare.json" }
}
}
Related
- Install and upgrade an agent - installer flags that write parts of this file.
- DNS-01 providers - provider credentials and propagation.
- Automation and executors - how jobs reach the agent.