nullTickets
April 17, 2026 ยท View on GitHub
Headless task tracker for autonomous AI agents.
nullTickets is a single Zig binary backed by SQLite. It exposes a REST API for
pipeline-driven task tracking and execution coordination: claim work by role, report progress, transition
stages, and attach artifacts.
Why
- Keep agents running without manual nudging.
- Track long-running autonomous work with durable state.
- Enforce lease-based execution and safe retries.
Design Principles
nullTickets should be treated as the task tracker and source of truth for AI-agent execution.
nullTickets(this repository) is responsible for durable task state:- pipelines, stages, transitions
- runs, leases, events, artifacts
- dependencies, assignments
- idempotent writes and optimistic transition checks
nullTicketsis intentionally orchestration-light:- it does not decide global scheduling strategy
- it does not run agent processes itself
nullboileris the orchestrator layer:- repository: nullboiler
- plans execution, selects agents, balances queues, applies policies, and drives transitions
nullclawis the agent runtime/executor layer:- repository: nullclaw
- executes role prompts, produces outputs, reports back through tracker APIs
Practical architecture:
nullTicketsstores the work graph and execution history.nullboilerorchestrates who should do what and when.nullclawagents do the actual work and publish evidence/results.
You do not have to use all three components.
nullclaw+nullTicketsis a valid setup for sequential execution.nullTicketscan be used with other agent runtimes as long as they implement the tracker contract (claim -> events -> transition/fail).nullboileris optional and is mainly needed for advanced multi-agent orchestration.
Adoption Path
- Use
nullclawonly for one-off tasks. - Add
nullTicketswhen you need a durable backlog (for example 100 tasks) and sequential execution with one agent loop. - Add
nullboilerwhen you need multi-agent scheduling, balancing, and policy automation.
Tech Stack
- Zig
0.16.0 - SQLite (vendored, static dependency)
- JSON over HTTP/1.1
Quick Start
zig build
zig build run -- --port 7700 --db tracker.db
Default values:
--port:7700--db:nulltickets.db
Config location:
- Default config path:
~/.nulltickets/config.json - Override instance home with
NULLTICKETS_HOME=/path/to/dir - Override config file directly with
--config /path/to/config.json
When NULLTICKETS_HOME is set, nulltickets reads config.json from that directory and
resolves relative db paths relative to that config file.
Test
zig build test
bash tests/test_e2e.sh
Project Layout
src/main.zig- process entrypoint, argument parsing, socket accept loopsrc/api.zig- HTTP routing, request validation, response serializationsrc/store.zig- SQLite queries, transactions, migrations, ownership/free helperssrc/domain.zig- pipeline FSM parsing and validationsrc/ids.zig- UUID/token/hash/time helperssrc/config.zig- config loading and resolutionsrc/export_manifest.zig- nullhub manifest exportsrc/from_json.zig- JSON config bootstrapsrc/migrations/001_init.sql- database schemasrc/migrations/003_store.sql- KV store tablesrc/migrations/004_store_fts.sql- FTS5 full-text search indextests/test_e2e.sh- end-to-end API flow
API Surface
| Method | Path | Description |
|---|---|---|
GET | /openapi.json | Full OpenAPI 3.1 schema for agent integration |
GET | /.well-known/openapi.json | Well-known OpenAPI discovery endpoint |
POST | /v1/traces | OTLP traces ingest (application/json or application/x-protobuf) |
POST | /otlp/v1/traces | OTLP collector-compatible traces ingest path |
GET | /health | Service and queue health |
POST | /pipelines | Create pipeline definition |
GET | /pipelines | List pipelines |
GET | /pipelines/{id} | Get pipeline by id |
POST | /tasks | Create task |
POST | /tasks/bulk | Bulk create tasks |
GET | /tasks?stage=&pipeline_id=&limit=&cursor= | List tasks (cursor paginated) |
GET | /tasks/{id} | Get task details |
POST | /tasks/{id}/dependencies | Add dependency |
GET | /tasks/{id}/dependencies | List dependencies |
POST | /tasks/{id}/assignments | Assign task to agent |
GET | /tasks/{id}/assignments | List assignments |
DELETE | /tasks/{id}/assignments/{agent_id} | Unassign task |
POST | /leases/claim | Claim next task by role |
POST | /leases/{id}/heartbeat | Extend lease |
GET | /tasks/{id}/run-state | Get task run_id |
POST | /runs/{id}/events | Append run event |
GET | /runs/{id}/events?limit=&cursor= | List run events (cursor paginated) |
POST | /runs/{id}/transition | Move task to next stage |
POST | /runs/{id}/fail | Mark run as failed |
POST | /artifacts | Attach artifact |
GET | /artifacts?task_id=&run_id=&limit=&cursor= | List artifacts (cursor paginated) |
GET | /ops/queue | Per-role queue stats for orchestrator |
PUT | /store/{namespace}/{key} | Put KV store entry |
GET | /store/{namespace}/{key} | Get KV store entry |
DELETE | /store/{namespace}/{key} | Delete KV store entry |
GET | /store/{namespace} | List entries in namespace |
DELETE | /store/{namespace} | Delete all entries in namespace |
GET | /store/search?q=&namespace=&limit=&filter_path=&filter_value= | Full-text search store |
Store API Notes
- Store path segments are URL-decoded by the server. Clients should percent-encode reserved characters in
namespaceandkey(for example spaces or/). - The namespace name
searchis reserved forGET /store/searchand cannot be listed viaGET /store/{namespace}. GET /store/searchalso supports exact JSON filtering withfilter_pathandfilter_valuein addition to FTS search.
Agent Loop
POST /leases/claim { agent_id, agent_role, lease_ttl_ms? }
-> 200 { task, run, lease_id, lease_token, expires_at_ms }
-> 204 (no work)
POST /runs/{run_id}/events (Bearer <lease_token>)
POST /runs/{run_id}/transition (Bearer <lease_token>)
POST /runs/{run_id}/fail (Bearer <lease_token>)
GET /tasks?limit=&cursor=
GET /runs/{run_id}/events?limit=&cursor=
GET /artifacts?limit=&cursor=
For practical nullclaw integration patterns (single-agent first, multi-agent optional), see nullclaw.md.
Documentation
Agent bootstrap endpoint:
GET /openapi.json
OpenTelemetry
nullTickets accepts OTLP traces on:
POST /v1/tracesPOST /otlp/v1/traces
Behavior:
application/json: parses OTLPExportTraceServiceRequestand stores normalized spans inotlp_spans.- Non-JSON payloads (for example
application/x-protobuf): stores raw payload inotlp_batches.payload_blob.
To link telemetry to tracker entities, include span/resource attributes:
nulltickets.run_idnulltickets.task_id