Data Types Reference
Complete specification of all core ztick data types.
HTTP API Users: These Zig types correspond directly to the HTTP API schemas defined in openapi.json and documented in the HTTP API Reference. The HTTP API uses the field names shown in the “HTTP Schema” sections below.
Core Types
Job
A scheduled action with an execution timestamp.
pub const Job = struct {
identifier: []const u8, // Unique job identifier
execution: i64, // Execution timestamp (nanoseconds since Unix epoch)
status: JobStatus, // Current state
};Fields:
identifier (string): Unique identifier for this job
- Alphanumeric, dots allowed
- Example:
app.job.123,backup.daily,report.monthly - HTTP API field name:
id
execution (i64): Unix timestamp in nanoseconds
1711612800000000000= 2024-03-28 12:00:00 UTC- Used for precise timing (nanosecond resolution)
- Persisted as big-endian i64 in logfiles
- HTTP API field name:
execution(response as nanoseconds, request as ISO 8601 string)
status (JobStatus): Current state (see below)
- HTTP API field name:
status
- HTTP API field name:
Lifecycle:
┌─────────┐
│ planned │ (newly created, waiting for execution time)
└────┬────┘
│
├─────────────────────────────────────────┐
│ │
▼ ▼
┌───────────┐ (execution time reached, ┌──────────┐
│ triggered │ waiting for runner) │ failed │
└─────┬─────┘ └──────────┘
│
▼
┌──────────┐
│ executed │ (runner completed successfully)
└──────────┘JobStatus
Enumeration of possible job states.
pub const JobStatus = enum {
planned, // Created but execution time not yet reached
triggered, // Execution time reached, runner in progress
executed, // Runner completed successfully
failed, // Runner failed or was aborted
};| Status | Meaning | Next States |
|---|---|---|
planned | Awaiting execution time | triggered, failed |
triggered | Runner executing | executed, failed |
executed | Success | (terminal) |
failed | Error or abort | (terminal) |
Rule
Pattern-based mapping of jobs to runners.
pub const Rule = struct {
identifier: []const u8, // Unique rule identifier (usually the pattern)
pattern: []const u8, // Prefix pattern to match job identifiers
runner: Runner, // What to execute (tagged union)
pub fn supports(self: *const Rule, job: []const u8) ?usize;
};Fields:
identifier: Unique rule name (typically the pattern)
- HTTP API field name:
id
- HTTP API field name:
pattern: Prefix pattern matching job identifiers
supports()returns the pattern length (as weight) if the job identifier starts with the pattern,nullotherwise- Longer patterns take priority (more specific match wins)
- Example:
SETRULE backup. SHELL /bin/backup.shmatchesbackup.daily,backup.weekly, etc. - HTTP API field name:
pattern
runner: Execution target (see Runner below)
- HTTP API field name:
runner(as discriminated union)
- HTTP API field name:
Pattern Matching Examples:
| Pattern | Matches | Does not match |
|---|---|---|
backup. | backup.daily, backup.hourly | app.backup, backup |
app.job. | app.job.1, app.job.2 | app.job, job.app.1 |
Runner
Tagged union of possible execution targets.
pub const Runner = union(enum) {
shell: struct {
command: []const u8,
},
direct: struct {
executable: []const u8,
args: []const []const u8,
},
http: struct {
method: []const u8,
url: []const u8,
},
awf: struct {
workflow: []const u8,
input: ?[]const u8,
},
amqp: struct {
dsn: []const u8,
exchange: []const u8,
routing_key: []const u8,
},
redis: struct {
url: []const u8,
command: []const u8,
key: []const u8,
},
};Types:
shell: Execute a command in a POSIX shell
- Supported via TCP protocol and HTTP API
- HTTP schema:
{"type": "shell", "command": "..."} - Example:
shell /usr/bin/backup.sh
direct: Execute a binary directly via execve without shell interpretation
- Supported via TCP protocol and HTTP API
- Fields:
executable(path to binary),args(literal argv elements) - HTTP schema:
{"type": "direct", "executable": "...", "args": [...]} - Example:
direct /usr/bin/curl -s http://example.com
http: Trigger an external webhook via HTTP/HTTPS request
- Supported via TCP protocol and HTTP API
- Fields:
method(HTTP verb: GET, POST, PUT, DELETE),url(target URL) - TCP syntax:
http <METHOD> <URL>(e.g.,http POST https://hooks.example.com/webhook) - HTTP request:
{"pattern": "...", "runner": "http", "args": ["POST", "https://hooks.example.com/webhook"]} - POST and PUT requests include JSON body:
{"job_id":"<identifier>","execution":<timestamp_ns>} - GET and DELETE requests send no body
- TLS support:
https://URLs establish encrypted connections - Timeout: 30-second connection and read timeout to prevent blocking
- Success: HTTP 2xx status codes; all others are treated as failure
awf: Execute an AWF (AI Workflow) via the AWF CLI
- Supported via TCP protocol and HTTP API
- Fields:
workflow(workflow name),inputs(array of key=value parameters) - HTTP request:
{"pattern": "...", "runner": "awf", "args": ["<workflow>", "--input", "key=value", ...]} - Example:
awf code-revieworawf generate-report --input format=pdf --input target=main
amqp: Publish a message to an AMQP 0-9-1 broker
- Supported via TCP protocol only (not exposed in HTTP API)
- Fields:
dsn(connection string),exchange,routing_key - DSN format:
amqp://[user[:password]@]host[:port][/vhost] - Message body is the job identifier (u128 hex string)
- TCP syntax:
amqp <dsn> <exchange> <routing_key>(e.g.amqp amqp://guest:guest@localhost:5672/ jobs notifications) - Plaintext AMQP only; TLS support (
amqps://) deferred - Per-execution connect/handshake/publish/close (no pooling); 30-second socket timeout
- See user-guide/writing-rules.md#amqp-runner for end-to-end usage, broker setup, verification, and troubleshooting
redis: Send a single Redis command (PUBLISH/RPUSH/LPUSH/SET) to a Redis server
- Supported via TCP protocol only (not exposed in HTTP API)
- Fields:
url(connection string),command(one ofPUBLISH,RPUSH,LPUSH,SET— case-sensitive),key(channel/list/key) - URL format:
redis://[user[:password]@]host[:port][/db](plaintext only —rediss://is rejected at parse time) - The job identifier is sent as the value/payload of the configured command
- TCP syntax:
redis <url> <command> <key>(e.g.redis redis://127.0.0.1:6379/0 PUBLISH deploy:events) - Per-execution connect → optional
AUTH(single- or two-arg form) → optionalSELECT <db>→ command → close (no pooling); 30-second socket timeout - Credentials present in the URL are redacted from logs
- See
ADR-0006 for the persistence discriminant rationale (byte
5, the next available value after the existing 0–4 mapping) - See user-guide/writing-rules.md#redis-runner for end-to-end usage and worked examples
Instruction
Commands received from clients via the TCP protocol.
pub const Instruction = union(enum) {
set: struct {
identifier: []const u8,
execution: i64,
},
rule_set: struct {
identifier: []const u8,
pattern: []const u8,
runner: Runner,
},
};Types:
| Type | Purpose | Example |
|---|---|---|
set | Create/update job | SET my.job 1711612800 |
rule_set | Create/update rule | SETRULE my.rule my. SHELL /bin/cmd |
Query Request
Query request from client.
pub const Client = u128;
pub const Request = struct {
client: Client, // Client ID (TCP connection)
identifier: []const u8, // Request identifier
instruction: Instruction, // What to do
};Query Response
Query response to client.
pub const Response = struct {
request: Request, // Original request
success: bool, // Whether the operation succeeded
body: ?[]const u8 = null, // Optional response body (for GET commands)
error_code: ?ErrorCode = null, // Optional error code (for failed requests)
error_message: ?[]const u8 = null, // Optional error message (for failed requests)
};Error Code
Machine-readable error classification for failed requests.
pub const ErrorCode = enum {
not_found, // Resource does not exist
invalid_args, // Missing or malformed arguments
auth_required, // Authentication required before command
auth_failed, // Invalid authentication token
auth_denied, // Authorized but insufficient namespace scope
internal, // Unexpected server error
};| Code | When Used | Example |
|---|---|---|
not_found | GET/REMOVE for non-existent job or rule | req-1 ERROR not_found job "missing.job" does not exist |
invalid_args | Recognized command with missing/invalid arguments | req-2 ERROR invalid_args missing required argument: timestamp |
auth_required | Non-AUTH command sent before authentication (when auth enabled) | ERROR auth_required |
auth_failed | AUTH sent with unrecognized token | ERROR auth_failed |
auth_denied | Post-auth command targets identifier outside token’s namespace | req-3 ERROR auth_denied insufficient namespace scope |
internal | Unexpected server failure | req-4 ERROR internal |
Execution Request
Sent from the scheduler to the processor thread when a job is triggered.
pub const Request = struct {
identifier: u128, // UUID for this execution attempt
job_identifier: []const u8, // Which job to execute
runner: Runner, // How to execute it
};Execution Response
Sent from the processor thread back to the scheduler with the result.
pub const Response = struct {
identifier: u128, // Matches the execution request UUID
success: bool, // Whether the runner succeeded
};Type Examples
Creating a Job
const job = Job{
.identifier = "backup.daily",
.execution = 1711612800000000000, // nanoseconds
.status = .planned,
};Creating a Rule
const rule = Rule{
.identifier = "backup.",
.pattern = "backup.",
.runner = .{ .shell = .{ .command = "/usr/bin/backup.sh" } },
};Creating an HTTP Rule
const http_rule = Rule{
.identifier = "rule.notify",
.pattern = "deploy.",
.runner = .{ .http = .{ .method = "POST", .url = "https://hooks.example.com/webhook" } },
};
const http_get_rule = Rule{
.identifier = "rule.ping",
.pattern = "health.",
.runner = .{ .http = .{ .method = "GET", .url = "https://api.internal/trigger" } },
};Creating an AMQP Rule
const amqp_rule = Rule{
.identifier = "rule.notify",
.pattern = "notify.",
.runner = .{ .amqp = .{
.dsn = "amqp://guest:guest@localhost:5672/",
.exchange = "jobs",
.routing_key = "notifications",
} },
};Creating a Redis Rule
// PUBLISH to a channel
const redis_publish_rule = Rule{
.identifier = "rule.publish",
.pattern = "deploy.",
.runner = .{ .redis = .{
.url = "redis://127.0.0.1:6379/0",
.command = "PUBLISH",
.key = "deploy:events",
} },
};
// RPUSH to a list (worker queue)
const redis_queue_rule = Rule{
.identifier = "rule.queue",
.pattern = "backup.",
.runner = .{ .redis = .{
.url = "redis://127.0.0.1:6379/0",
.command = "RPUSH",
.key = "backup:tasks",
} },
};Creating an AWF Rule
// AWF rule without inputs
const awf_rule_simple = Rule{
.identifier = "rule.review",
.pattern = "code-review.",
.runner = .{ .awf = .{ .workflow = "code-review", .inputs = &.{} } },
};
// AWF rule with inputs
const awf_rule_with_inputs = Rule{
.identifier = "rule.report",
.pattern = "report.",
.runner = .{ .awf = .{ .workflow = "generate-report", .inputs = &.{ "format=pdf", "target=main" } } },
};Creating an Instruction
const instr = Instruction{
.set = .{
.identifier = "job.1",
.execution = 1711612800000000000,
},
};Memory Ownership
All strings are borrowed references ([]const u8):
- Allocated by the caller
- Must remain valid for the lifetime of use
- Persisted structures dupe strings as needed
Example:
var job = try allocator.create(Job);
job.identifier = try allocator.dupe(u8, "my.job"); // Own the string
defer allocator.free(job.identifier);Encoding (Persistence)
Types are persisted in binary format:
Job Encoding
[1 byte: type marker = 0]
[2 bytes: identifier length (big-endian u16)]
[N bytes: identifier string]
[8 bytes: execution timestamp (big-endian i64)]
[1 byte: status enum discriminant (0=planned, 1=triggered, 2=executed, 3=failed)]Rule Encoding
[1 byte: type marker = 1]
[2 bytes: identifier length (big-endian u16)]
[N bytes: identifier string]
[2 bytes: pattern length (big-endian u16)]
[N bytes: pattern string]
[1 byte: runner type discriminant]
└─ if shell (0):
[2 bytes: command length]
[N bytes: command string]
└─ if amqp (1):
[2 bytes: dsn length]
[N bytes: dsn string]
[2 bytes: exchange length]
[N bytes: exchange string]
[2 bytes: routing_key length]
[N bytes: routing_key string]
└─ if direct (2):
[2 bytes: executable length]
[N bytes: executable string]
[2 bytes: args count (big-endian u16)]
for each arg:
[2 bytes: arg length]
[N bytes: arg string]
└─ if awf (3):
[2 bytes: workflow length]
[N bytes: workflow string]
[2 bytes: inputs count (big-endian u16)]
for each input:
[2 bytes: input length]
[N bytes: input string (key=value)]
└─ if http (4):
[2 bytes: method length]
[N bytes: method string (GET|POST|PUT|DELETE)]
[2 bytes: url length]
[N bytes: url string]
└─ if redis (5):
[2 bytes: url length]
[N bytes: url string]
[2 bytes: command length]
[N bytes: command string (PUBLISH|RPUSH|LPUSH|SET)]
[2 bytes: key length]
[N bytes: key string]Discriminant note: Redis uses byte
5(the next available value after0=shell,1=amqp,2=direct,3=awf,4=http). Existing logfiles already encode these five values, so the redis variant appended a new discriminant rather than renumbering. See ADR-0006 for the rationale.
See Persistence Format for complete details.
See Also
- Persistence Format — Binary encoding details
- Protocol Reference — How types map to protocol commands
- Architecture — Domain layer design