agent-dispatch
Agent Dispatch Queue
Hand any task off to an agent (Claude Code, Cursor CLI, a local model behind your own webhook) and let it work in your infrastructure. Every dispatch is HMAC-signed, every result comes back through an HMAC-verified callback, and every hop lands in the audit log.
Not a chatbot. Not autonomy-by-default. A queue with provenance.
What it is not
- Not a hosted agent. Ithura never runs the model. Your infrastructure does, on hardware you control.
- Not a chat surface. The dispatch happens once, the runner replies once (or streams status updates via callbacks). No back-and-forth in the UI.
- Not autonomous. A human dispatches; a human reviews the resulting PR. Height 2.0 tried autonomy-in-SaaS and shut down in September 2025. This is deliberately the opposite shape.
Register a runner
Workspace admin only. Settings → Agent runners → Register runner.
Fields:
- Name: what you call this runner, e.g. "Claude Code on ops laptop".
- Webhook URL: HTTPS endpoint the dispatch payload POSTs to.
- HMAC secret: at least 16 characters. Click Generate for a 48-hex
random value. Store the value in your runner as
ITHURA_RUNNER_SECRET. The SPA never reads the secret back after creation; a leaked bearer token cannot exfiltrate it. - Timeout: seconds Ithura waits for the outbound POST to succeed (default 900, cap 3600).
Dispatch a task
Any workspace member with member+ access can dispatch. On any task, call the API (v1 has no UI action yet):
POST /workspaces/{slug}/projects/{projectID}/issues/{issueID}/dispatch-agent/
{
"runner_id": "<uuid from the Agent runners page>",
"acceptance_criteria": "Ship a PR that closes this; tests must be green"
}
The response returns the dispatch run id and status queued. The
worker picks it up, signs the payload, POSTs to the runner. On any
2xx, the run moves to in_progress and waits for the callback.
Runner-side integration
Ithura POSTs a JSON payload with the task snapshot plus a callback URL:
{
"issue": {
"id": "…",
"project_identifier": "PAR",
"sequence_id": 482,
"name": "SAML JIT provisioning gate for verified domains",
"description": "…",
"priority": "high",
"state": "In progress"
},
"acceptance_criteria": "…",
"callback": {
"url": "https://api-staging.ithura.com/agent-runs/<token>/callback",
"signature_header": "X-Ithura-Signature",
"signature_scheme": "hex_hmac_sha256(secret + callback_token, body)"
}
}
The runner:
- Verifies the outbound
X-Ithura-Signature:hex(hmac_sha256(ITHURA_RUNNER_SECRET, request_body)). If mismatched, the request came from someone else. Reject. - Does its work. It can POST intermediate
status: in_progresscallbacks as often as it wants. - On completion, POSTs a terminal status:
{
"status": "completed",
"result_url": "https://github.com/acme/repo/pull/301",
"result_summary": "Added SAML JIT gate, 3 tests, all green"
}
The callback must include X-Ithura-Signature computed as:
hex(hmac_sha256(ITHURA_RUNNER_SECRET + callback_token, callback_body))
The per-run callback_token extra ensures a leaked runner secret alone
cannot forge callbacks for other runs.
Statuses
| Status | Meaning |
|---|---|
queued | The dispatch job is enqueued in Ithura's worker pool. |
in_progress | The outbound POST returned 2xx, waiting for the terminal callback. |
completed | Runner posted a completed callback. result_url is the PR / MR. |
failed | Runner posted a failed callback OR the outbound POST failed. error_text explains. |
cancelled | A workspace admin manually cancelled the run. |
Terminal states (completed, failed, cancelled) are idempotent:
subsequent callbacks are audited (workspace.agent_dispatch.callback_ignored)
but do not mutate the row.
Audit
Every dispatch, every callback, every runner change writes an entry to
audit_logs:
workspace.agent_runner.created/.deletedworkspace.agent_dispatch.queued/.in_progress/.completed/.failed/.callback_ignored
Enterprise workspaces can export the whole trail as CSV via the audit log export endpoint (see Sovereign > Audit CSV).
Follow-ups on the roadmap
- UI dispatch action on the task detail page (v1 is API-only for operators; the UI is Register-runner + read history).
- Auto-dispatch rules ("every task labelled
agent-eligiblegoes to runner X on state change to Ready"). - Cost tracking: runner reports tokens / seconds in the callback; we surface the totals per runner and per workspace.
- Retry policy (v1 requires a fresh dispatch call after a failed run to keep the audit trail linear).