Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Configuration Reference

hoike is configured with a single TOML file, loaded once at startup. The default path is /etc/hoike/hoike.toml; override it with --config:

hoike serve --config /path/to/hoike.toml

Every key can also be set via environment variable using the HOIKE_ prefix, double-underscore section separators, and uppercase names. For example, server.listen becomes HOIKE_SERVER__LISTEN. Environment variables take precedence over the config file.


[server]

Top-level server settings that control the process mode, listener, and request limits.

KeyTypeDefaultDescription
modestringrequiredOperating mode: "signer", "edge", or "combined". See Signer, Edge, and Combined mode pages.
listenstring"0.0.0.0:2560"Socket address for the HTTP listener. Port 2560 is the IANA-assigned port for OCSP over HTTP.
max_requestinteger8192Maximum OCSP request body size in bytes. RFC 6960 POST bodies are typically small; RFC 9919 GET requests encode the request in the URL path and are capped at 255 bytes by the URI length constraint. This limit protects against oversized or malformed requests.
[server]
mode   = "edge"
listen = "0.0.0.0:2560"
max_request = 8192

Mode validation

mode is the single most important setting. It determines which code paths are active:

  • signer — reads revocation sources, produces ahu bundles, does not serve OCSP queries.
  • edge — serves pre-signed responses from bundles, holds no private keys.
  • combined — runs both signer and edge in one process.

hoike validates mode-specific constraints at startup. For example, nonce_policy = "live" on an edge node is a fatal error (edge nodes have no signing keys).


[storage]

Paths and limits for bundle storage and persistent state.

KeyTypeDefaultDescription
bundle_dirstring"/var/lib/hoike/bundles"Directory where ahu bundles are stored. The signer writes here; the edge reads from here. Must be readable (edge) or read-write (signer/combined).
state_dbstring"/var/lib/hoike/state"Path to the persistent state database. Stores epoch high-water marks for anti-rollback protection. This path must survive restarts — losing it resets rollback protection. See Anti-Rollback Protection.
max_chaininteger24Maximum number of delta bundles in a chain before the edge demands a full bundle. Lower values increase bandwidth (more full bundles); higher values save bandwidth but increase recovery time after a missed delta.
[storage]
bundle_dir = "/var/lib/hoike/bundles"
state_db   = "/var/lib/hoike/state"
max_chain  = 24

Operational note: Back up state_db alongside your bundle directory. If state_db is lost, the node cannot detect rollback or fork attacks until it re-establishes its high-water marks from a trusted source.


[gossip]

SWIM gossip protocol settings for edge fleet coordination. Gossip provides membership tracking, generation announcements (new bundles), and urgent revocation notices. See Gossip Configuration for a deep dive.

KeyTypeDefaultDescription
enabledbooleantrueEnable or disable gossip. Set to false for air-gap/enclave deployments. See Air-Gap Deployments.
bindstring"0.0.0.0:7946"UDP/TCP address for the SWIM protocol listener.
seedsarray of strings[]Initial seed nodes for cluster join. At least one seed must be reachable for a new node to join the fleet. Format: "hostname:port".
identity_keystringPath to the node’s gossip identity key. All gossip messages are signed with this key.
node_namestringhostnameHuman-readable node identifier. Must be unique within the gossip cluster. Defaults to the system hostname if omitted.
[gossip]
enabled      = true
bind         = "0.0.0.0:7946"
seeds        = ["edge-a.pki.example:7946", "edge-b.pki.example:7946"]
identity_key = "/etc/hoike/gossip.key"
node_name    = "edge-01"

Disabling gossip

For air-gap or single-node deployments, disable gossip entirely:

[gossip]
enabled = false

When gossip is disabled, bundles must be delivered out-of-band (removable media, hoike import, or a scheduled file copy). See Air-Gap Deployments.


[[ca]]

Each [[ca]] section configures one CA whose certificates this responder handles. hoike supports multiple [[ca]] sections for multi-CA deployments. Requests are routed to the correct CA by issuerKeyHash lookup. See Multi-CA Routing.

Identity and source

KeyTypeDefaultDescription
labelstringrequiredHuman-readable label for this CA. Used in logs, metrics, and bundle filenames. Must be unique across all [[ca]] sections.
sourceinline tablerequiredRevocation data source. See Source types below.

Signing

KeyTypeDefaultDescription
signingstring"ca-direct"Signing mode. "ca-direct" signs with the CA’s own key. "delegated" uses a separate OCSP responder certificate and key (requires responder_cert and responder_key).
sig_algstring"ecdsa-p256"Signature algorithm. Supported values: "ecdsa-p256", "ecdsa-p384", "ed25519", "rsa-sha256", "ml-dsa-44", "ml-dsa-65", "ml-dsa-87".
responder_certstringPath to the OCSP responder certificate. Required when signing = "delegated".
responder_keystringPath to the OCSP responder private key. Required when signing = "delegated".
responder_idstring"by-key"ResponderID format in OCSP responses: "by-key" (SubjectPublicKeyInfo hash) or "by-name" (Distinguished Name). "by-key" is recommended — it survives certificate renewal.

CertID and compatibility

KeyTypeDefaultDescription
certid_compatstring"dual"CertID hash algorithm compatibility. "dual" indexes responses by both SHA-256 and SHA-1 issuerKeyHash (for clients that still send SHA-1). "sha256" accepts only SHA-256. "sha1" accepts only SHA-1 (not recommended).

Nonce handling

KeyTypeDefaultDescription
nonce_policystring"ignore"How to handle nonces in OCSP requests. "ignore" omits the nonce from responses (appropriate for pre-signed). "forward" proxies the request to the signer for a live-signed response with nonce. "live" signs a fresh response with nonce on every request (signer mode only). See Nonce Policies.
forward_tostringURL of the signer to forward nonce-bearing requests to. Required when nonce_policy = "forward".

Timing and batch production

KeyTypeDefaultDescription
validityduration string"24h"Response validity window (nextUpdate − thisUpdate). Determines how long a cached response remains valid.
batch_intervalduration string"1h"How often the signer produces a new batch of responses. The signer outage budget is validity − batch_interval — if the signer is down longer than this, edge nodes will begin serving expired responses.
jitterduration string"2h"Random jitter added to thisUpdate to prevent response expiration thundering herds. Each response’s thisUpdate is shifted by a random offset within [0, jitter].
max_age_fractionfloat0.5Fraction of remaining validity used for Cache-Control: max-age. A value of 0.5 means the max-age header is set to half the time remaining until nextUpdate.
urgent_revocationbooleantrueWhen true, the signer produces an off-cycle delta bundle immediately upon detecting a new revocation, rather than waiting for the next batch_interval.
archive_cutoffduration string"1y"How far back to keep responses for expired certificates. Certificates that expired more than this duration ago are dropped from bundles.

Completeness

KeyTypeDefaultDescription
completenessstring"authoritative-complete"Declares whether this responder has complete revocation data for the CA. "authoritative-complete" means the responder is the authoritative source for this CA’s revocation status — any certificate not found in the bundle is reported as good. "partial" means the responder only knows about certificates listed in a CRL — unlisted certificates return unauthorized (unknown).

Source types

The source field is an inline table that specifies where revocation data comes from.

CRL source (implemented):

source = { type = "crl", path = "/var/lib/hoike/crls/enterprise.crl" }
FieldTypeDescription
typestringMust be "crl".
pathstringPath to the CRL file. hoike watches this path for changes and reloads automatically.

Dogtag source (planned):

source = { type = "dogtag", url = "https://ca01.pki.example:8443", auth = "mtls", cert = "/etc/hoike/ra.pem" }
FieldTypeDescription
typestringMust be "dogtag".
urlstringDogtag CA REST API endpoint.
authstringAuthentication method: "mtls".
certstringPath to the client certificate for mTLS authentication.
[[ca]]
label          = "enterprise-issuing-01"
source         = { type = "crl", path = "/var/lib/hoike/crls/enterprise.crl" }
signing        = "ca-direct"
sig_alg        = "ecdsa-p256"
responder_id   = "by-key"
certid_compat  = "dual"
nonce_policy   = "ignore"
validity       = "24h"
batch_interval = "1h"
jitter         = "2h"
archive_cutoff = "1y"
completeness   = "authoritative-complete"

Duration strings

Duration values use a human-readable format: a number followed by a unit suffix.

SuffixMeaningExample
sseconds"30s"
mminutes"15m"
hhours"24h"
ddays"7d"
yyears (365 days)"1y"

Validation rules

hoike validates the configuration at startup and exits with a descriptive error if any rule is violated:

RuleError
mode is missing or not one of signer, edge, combinedinvalid server mode
nonce_policy = "live" with mode = "edge"live nonce signing requires signer mode
nonce_policy = "forward" without forward_toforward_to is required when nonce_policy = "forward"
signing = "delegated" without responder_cert or responder_keydelegated signing requires responder_cert and responder_key
batch_interval >= validitybatch_interval must be less than validity
max_chain < 1max_chain must be at least 1
Duplicate label across [[ca]] sectionsduplicate CA label
bundle_dir does not exist or is not writable (signer/combined)bundle_dir is not writable
state_db parent directory does not existstate_db path is invalid
gossip.enabled = true without identity_keygossip identity_key is required when gossip is enabled

Complete annotated example

# /etc/hoike/hoike.toml — Edge node serving two CAs with gossip

[server]
mode        = "edge"           # Keyless serving from pre-signed bundles
listen      = "0.0.0.0:2560"   # IANA-assigned OCSP port
max_request = 8192             # Max POST body; GET is path-limited to ~255 bytes

[storage]
bundle_dir = "/var/lib/hoike/bundles"   # Where ahu bundles are read from
state_db   = "/var/lib/hoike/state"     # Epoch high-water marks — MUST persist across restarts
max_chain  = 24                         # Accept up to 24 delta bundles before requiring a full

[gossip]
enabled      = true
bind         = "0.0.0.0:7946"
seeds        = ["edge-a.pki.example:7946", "edge-b.pki.example:7946"]
identity_key = "/etc/hoike/gossip.key"
node_name    = "edge-01"

# Enterprise issuing CA — CRL-based, pre-signed responses
[[ca]]
label          = "enterprise-issuing-01"
source         = { type = "crl", path = "/var/lib/hoike/crls/enterprise.crl" }
signing        = "ca-direct"
sig_alg        = "ecdsa-p256"
responder_id   = "by-key"
certid_compat  = "dual"          # Accept both SHA-256 and SHA-1 CertID hashes
nonce_policy   = "ignore"        # Pre-signed — nonce omitted from responses
validity       = "24h"           # Signer outage budget: 24h − 1h = 23h
batch_interval = "1h"
jitter         = "2h"
archive_cutoff = "1y"
completeness   = "authoritative-complete"

# Partner issuing CA — delegated responder, nonces forwarded to signer
[[ca]]
label          = "partner-issuing-01"
source         = { type = "crl", path = "/var/lib/hoike/crls/partner.crl" }
signing        = "delegated"
responder_cert = "/etc/hoike/partner-ocsp.pem"
responder_key  = "/etc/hoike/partner-ocsp.key"
sig_alg        = "ecdsa-p384"
responder_id   = "by-key"
certid_compat  = "sha256"        # Partner clients all support SHA-256
nonce_policy   = "forward"       # Proxy nonce-bearing requests to signer
forward_to     = "https://signer.pki.example:2560"
validity       = "12h"
batch_interval = "30m"
jitter         = "1h"
archive_cutoff = "6m"            # Partner certs are short-lived
completeness   = "partial"       # CRL may not list every certificate

Environment variable overrides

Any configuration key can be overridden with an environment variable. The naming convention is:

HOIKE_<SECTION>__<KEY>

Double underscores separate section from key; single underscores within a key name are preserved.

Config keyEnvironment variable
server.modeHOIKE_SERVER__MODE
server.listenHOIKE_SERVER__LISTEN
storage.bundle_dirHOIKE_STORAGE__BUNDLE_DIR
gossip.enabledHOIKE_GOSSIP__ENABLED

Environment variables are useful for container deployments where the base config file is baked into the image and per-instance settings (like node_name or listen) vary.

Note: [[ca]] array sections cannot be fully configured via environment variables due to TOML array-of-tables semantics. Use the config file for CA definitions.