Height 2.0 tried a hosted, autonomous, in-SaaS agent product and shut down in September 2025. The lesson was not that agents are useless. It was that a tracker is the wrong place to run the model, and autonomy-by-default is the wrong posture. What a team actually wants from its tracker is a queue with provenance: I hand a task off, the work happens on my infrastructure, and the trail lands somewhere I can audit next quarter.
Ithura's Agent Dispatch Queue is exactly that shape. Register a runner (a webhook URL to your box running Claude Code, Cursor CLI, a local model, whatever you like), dispatch a task with a runner id and acceptance criteria, and Ithura signs the payload and POSTs it out. Your runner does the work, signs a callback, and Ithura moves the run to completed with a result URL. Every hop lands in the audit log.
Why not a chat surface
The interesting agent products are agents talking to other agents. A tracker is not that surface. A tracker is where the work is defined, the reviewer signs off, and the deliverable lives. If the tracker also streams a chat, two things happen: the chat becomes the source of truth (bad), and every message multiplies the blast radius of a leaked bearer token (worse).
So dispatch is a one-shot handoff. The runner may post intermediate in_progress callbacks as often as it wants (they land in the audit log, not the UI), and posts one terminal callback with either completed and a result_url (the PR / MR link) or failed and an error_text. That is it. No back-and-forth.
How the signing works
Two directions, two signatures, one shared secret plus a per-run token so a leaked secret cannot forge callbacks for an unrelated run.
Outbound (Ithura, to your runner):
X-Ithura-Signature: hex(hmac_sha256(runner_secret, request_body)) # The runner verifies: want = hex_hmac_sha256(os.environ["ITHURA_RUNNER_SECRET"], body) assert hmac.compare_digest(want, request.headers["X-Ithura-Signature"])
Inbound (your runner, back to Ithura):
X-Ithura-Signature: hex(hmac_sha256(runner_secret + callback_token, callback_body)) # Ithura verifies: want = hex_hmac_sha256(runner_secret + run.callback_token, body) assert hmac.compare_digest(want, header)
The callback_token addition on the return trip is deliberate. A runner that gets compromised could otherwise forge callbacks against every past and future run for that runner. With the per-run token, a leaked secret still needs the token for the specific run to forge that run's terminal state, and the token lives in Ithura's DB and on the outbound POST only.
What the audit log gets
Every operation writes a structured audit entry:
workspace.agent_runner.createdandworkspace.agent_runner.deletedfor lifecycle. Runners are soft-deleted; the audit trail for their runs stays intact.workspace.agent_dispatch.queued, then.in_progress, then.completedor.failed. If the runner posts a callback for a run that has already reached a terminal state,.callback_ignoredfires and the request body is preserved for investigation.
Enterprise workspaces stream the same trail as CSV via the audit CSV export endpoint. The SIEM ingest recipes on the audit export page cover Splunk, Elastic, and Datadog.
What is not in v1
- UI dispatch action. v1 is API-first: the Settings tab registers runners and reads history; the dispatch call is a
curl. A per-task "Dispatch to runner..." action is next. - Auto-dispatch rules, e.g. "every task labelled
agent-eligiblegoes to runner X when it moves to Ready". The primitive is there (labels, states, hooks), the wiring is next. - Cost tracking. The callback can already carry any extra fields; a first-class
tokens/secondspair rolled up per runner and per workspace is the next reporting surface. - Retry in place. A
failedrun today requires a fresh dispatch to keep the audit trail linear. A one-click retry that opens a new run linked to the failed one is the ergonomics fix.
Where dispatch fits with the rest of Ithura
The outbound signing pattern is deliberately the same as workspace webhooks. A team that already has a webhook subscriber hardened against replay and signature failures can reuse the same verification code path for a runner. The audit-log surface is the same one that catches SAML SSO events, SCIM provisioning changes, and every write to the workspace's settings.
The tracker is still the tracker. The agent runs somewhere you control. Provenance is not a bolt-on.
Related
- docs/features/agent-dispatch, the full runner integration contract with the exact request and callback payloads.
- docs/tutorials/agent-dispatch-first-runner, a 15-minute walkthrough that stands up a Python receiver and dispatches a task end to end.
- docs/sovereign. The runner runs on your infrastructure, the model weights never touch Ithura, and the tracker is the audit surface.