Complete specification of all TOML configuration options.

ztick is configured via a TOML file passed with the -c / --config flag. All sections and keys are optional; omitted values use the defaults below.

zig build run -- -c /path/to/config.toml

Sections

[log]

Logging configuration.

KeyTypeDefaultDescription
levelstring"info"Log verbosity threshold: off, error, warn, info, debug, trace. Messages at or above this level are written to stderr; messages below are suppressed.

Levels (ordered from least to most verbose):

  • off — All log output suppressed, including startup messages
  • error — Errors only (e.g. controller start failures)
  • warn — Warnings and above (e.g. database load failures)
  • info — Startup info (config path, log level, listen address, loaded job/rule counts), client connect/disconnect, and above (recommended for production)
  • debug — Instruction receipt, execution outcomes, and above
  • trace — Maximum verbosity (maps to debug internally)

[controller]

TCP server configuration.

KeyTypeDefaultDescription
listenstring"127.0.0.1:5678"TCP address and port for the protocol server
auth_filestring (path)nullPath to TOML auth token file. When set, all connections require AUTH command before accepting other commands. When unset (default), authentication is disabled and connections are accepted immediately.
tls_certstring (path)nullPath to PEM-encoded TLS certificate file (requires tls_key to be set)
tls_keystring (path)nullPath to PEM-encoded TLS private key file (requires tls_cert to be set)

Address Format: <host>:<port> where host is IPv4 or IPv6

  • 127.0.0.1:5678 — Localhost only
  • 0.0.0.0:5678 — All interfaces (IPv4)
  • [::1]:5678 — Localhost only (IPv6)

Authentication Configuration Notes:

  • When auth_file is set, the server requires all clients to authenticate before issuing commands
  • The auth file uses TOML format with [token.<name>] sections containing secret and namespace keys
  • Secrets are matched using constant-time comparison to prevent timing attacks
  • Tokens are loaded once at startup; changes require a server restart
  • Omit auth_file to disable authentication (default for backward compatibility)
  • See Configuring Authentication for setup instructions and examples

TLS Configuration Notes:

  • Both tls_cert and tls_key must be set together to enable TLS
  • If only one is set, ztick exits with ConfigError.InvalidValue at startup
  • Omit both to run in plaintext mode (default)
  • By convention, use port 5678 for plaintext and port 5679 for TLS to avoid confusion between encrypted and unencrypted endpoints
  • Important: When using authentication, TLS is strongly recommended to prevent tokens from being transmitted in plaintext
  • Requires libssl-dev (Debian/Ubuntu) or equivalent on the build machine
  • See README TLS section for certificate generation instructions

[http]

HTTP REST API server configuration. This section is optional; omit it to disable the HTTP API.

KeyTypeDefaultDescription
listenstringnullHTTP address and port. When set, enables the HTTP server on the specified address. When omitted or null, the HTTP API is disabled.

Address Format: Same as [controller]<host>:<port> where host is IPv4 or IPv6

  • 127.0.0.1:5680 — Localhost only
  • 0.0.0.0:5680 — All interfaces (IPv4)
  • [::1]:5680 — Localhost only (IPv6)

HTTP Server Notes:

  • The HTTP API is optional and disabled by default. To enable it, add an [http] section with a listen address.
  • The HTTP server handles multiple concurrent requests by spawning a dedicated thread per connection (thread-per-connection model, F022)
  • All HTTP endpoints require Bearer token authentication (same tokens as TCP protocol via auth_file)
  • Public endpoints /health and /openapi.json do not require authentication
  • The HTTP server shares the same scheduler and storage as the TCP server—both protocols interact with the same data
  • Connection handling includes a 5-second graceful shutdown timeout; in-flight requests have up to 5 seconds to complete before server exit
  • By convention, use port 5680 for HTTP and port 5681 for HTTPS (if TLS support is added in the future)

Concurrency: The HTTP server is designed for concurrent request handling. Multiple clients can send requests simultaneously without blocking each other. This enables higher throughput under concurrent load compared to a sequential accept loop.

Configuration Examples:

# Disable HTTP API (default)
# [http] section omitted

# Enable HTTP API on localhost only
[http]
listen = "127.0.0.1:5680"

# Enable HTTP API on all interfaces
[http]
listen = "0.0.0.0:5680"

[shell]

Shell execution configuration. Controls which shell binary and arguments are used when executing shell runner jobs.

KeyTypeDefaultDescription
pathstring"/bin/sh"Absolute path to the shell binary used for shell runner execution. Validated at startup — if the path does not exist, ztick exits with ConfigError.InvalidValue.
argsarray of strings["-c"]Arguments passed to the shell binary before the command string. The job command is appended as the final argument.

Startup Validation:

  • The shell path must exist on disk at startup. If it does not, ztick exits immediately with a configuration error.
  • The path is validated once at startup and not re-checked at job execution time.

Configuration Examples:

# Default (equivalent to omitting [shell] entirely)
[shell]
path = "/bin/sh"
args = ["-c"]

# Use bash instead of sh
[shell]
path = "/bin/bash"
args = ["-c"]

# Use dash with errexit for stricter error handling
[shell]
path = "/bin/dash"
args = ["-e", "-c"]

# Use echo for dry-run testing (executes /bin/echo <command>)
[shell]
path = "/bin/echo"
args = []

Direct Execution Mode:

In addition to configurable shell execution, ztick supports a direct runner type that bypasses the shell entirely. Direct runner rules are created via the protocol:

RULE SET <id> <pattern> direct <executable> [args...]

The first token after direct is the executable path; remaining tokens are passed as literal argv elements. No shell interpretation occurs, eliminating shell injection risks. This mode uses execve directly — shell metacharacters like ;, |, and $() are passed as literal strings, not interpreted.

[database]

Persistence and scheduling configuration.

KeyTypeDefaultDescription
persistencestring"logfile"Persistence backend: "logfile" (disk-backed, persistent) or "memory" (ephemeral, no disk I/O)
logfile_pathstring"logfile"Path to the append-only persistence logfile (ignored when persistence = "memory")
fsync_on_persistbooltrueCall fsync after each persistence write (safer, slower; ignored when persistence = "memory")
framerateinteger512Scheduler tick rate in Hz (valid range: 1-65535)
compression_intervalinteger (seconds)3600Interval between background compression cycles in seconds; set to 0 to disable compression (logfile backend only; ignored for memory backend)

[telemetry]

Observability and monitoring configuration via OpenTelemetry.

KeyTypeDefaultDescription
enabledboolfalseEnable telemetry export to an OTLP collector. When disabled (default), no observability data is exported and telemetry instrumentation has zero overhead.
endpointstring(required if enabled)HTTP endpoint of the OpenTelemetry collector (e.g., "http://localhost:4318"). The collector must accept OTLP/HTTP JSON at /v1/metrics, /v1/traces, and /v1/logs paths.
service_namestring"ztick"Logical name for this service in observability backends (appears as service.name resource attribute).
flush_interval_msinteger (milliseconds)5000Batch export interval in milliseconds. Accumulated metrics, traces, and logs are sent to the collector at this interval.

Telemetry Features:

  • Metrics: Job throughput (ztick.jobs.scheduled, ztick.jobs.executed, ztick.jobs.removed), execution latency (ztick.execution.duration_ms), connection counts (ztick.connections.active), rule counts (ztick.rules.active), and compression events (ztick.persistence.compactions).
  • Traces: ztick.request span (kind: server) covering TCP request lifecycle (receive → parse → dispatch → response), with attributes ztick.command, ztick.request.id, and ztick.success.
  • Structured Logs: Log records at warn level and above, correlated with traces via shared trace IDs.
  • Resource Attributes: All signals include service.name and service.version resource attributes.
  • Resilience: Export failures do not impact scheduler operation; ztick continues processing jobs normally when the collector is unreachable.

Configuration Examples:

# Telemetry disabled (default — zero overhead)
[telemetry]
enabled = false

# Telemetry enabled with local collector
[telemetry]
enabled = true
endpoint = "http://localhost:4318"
service_name = "my-ztick-instance"
flush_interval_ms = 5000

# Telemetry enabled with remote collector (Datadog, Grafana Agent, etc.)
[telemetry]
enabled = true
endpoint = "http://otel-collector.observability.svc.cluster.local:4318"
service_name = "ztick-prod"
flush_interval_ms = 10000

Collector Compatibility:

  • Tested with OpenTelemetry Collector (otelcontribcol)
  • Compatible with any OTLP/HTTP JSON receiver (Datadog Agent, Grafana Agent, New Relic, etc.)
  • Metrics and traces are exported via OTLP/HTTP protobuf to POST /v1/metrics and POST /v1/traces respectively
  • Requires the collector to be reachable and healthy for export; unavailable collectors do not cause scheduler failures

Persistence Modes:

  • "logfile" — (default) Store jobs and rules in an append-only binary logfile. Data persists across restarts. Use for production deployments where durability is critical.
  • "memory" — Store jobs and rules only in memory. No disk I/O occurs. Data is lost on shutdown. Useful for ephemeral deployments, CI environments, and testing where durability is not required.

Compression Interval:

  • 0 — Disable automatic compression entirely
  • 3600 — Default, compress once per hour
  • 60 — Compress every minute (high-mutation workloads)

Compression runs in a background thread and does not block the scheduler tick loop. When the persistence backend is "memory", compression is completely inactive regardless of this setting. If a leftover .to_compress file exists at startup (from a previously interrupted compression), it is compressed before the periodic timer begins. If a compression cycle is still running when the next interval triggers, the cycle is skipped.

Framerate:

  • 1 — Evaluate once per second (low CPU, long latency)
  • 512 — Default, evaluate 512 times per second (~2ms latency)
  • 1000 — Evaluate 1000 times per second (~1ms latency)

Full Example

[log]
level = "debug"

[controller]
listen = "0.0.0.0:5679"
tls_cert = "/etc/ztick/cert.pem"
tls_key = "/etc/ztick/key.pem"

[shell]
path = "/bin/bash"
args = ["-c"]

[database]
persistence = "logfile"
logfile_path = "data/ztick.log"
fsync_on_persist = false
framerate = 100
compression_interval = 1800

[telemetry]
enabled = true
endpoint = "http://otel-collector:4318"
service_name = "ztick-prod"
flush_interval_ms = 10000

Errors

ErrorCause
InvalidLogLevellevel is not one of the valid values
FramerateOutOfRangeframerate is 0 or exceeds 65535
UnknownSectionSection name is not log, controller, shell, database, or telemetry
UnknownKeyKey is not recognized within its section
InvalidValueValue cannot be parsed (e.g. non-boolean for fsync_on_persist), or only one of tls_cert/tls_key is set, or persistence is not "logfile" or "memory", or telemetry.enabled = true but endpoint is missing/malformed, or flush_interval_ms is not a valid u32, or shell.path does not exist on disk, or shell.args is not a valid array of quoted strings
MissingSecretAuth file: a [token.*] section is missing the secret key
EmptyNamespaceAuth file: a [token.*] section has an empty namespace or is missing it
DuplicateSecretAuth file: two or more tokens share the same secret value