SnackOnAI Engineering | Senior AI Systems Researcher | Technical Deep Dive | November 6, 2026
That's the useful finding, and it isn't quite the headline. The README advertises a cost range of "223 to 0.47 credits" across eight harness × model setups, a 474x spread. A harness router can't create a 474x saving. What it can do is make the cheaper choice a one-field change in your request instead of an integration rewrite. That's real value, and it rests on unglamorous code: 20 hand-written translators that turn 30 different CLI event streams into one format.
The promise: by the end you'll know what HarnessRouter actually standardizes, where the cost spread really comes from, and what you inherit when you run Codex, Claude Code or Pi as a backend behind one API.
The fence: we read the HarnessRouter repository at commit 8e1cf1b (November 4, 2026; 815 commits since August 9), including the runner, the gateway, the Unified Harness Protocol spec, the support matrix and the SpreadsheetBench results file. We also read secondary coverage and two independent 2026 studies of harness effects. We didn't deploy the container or run tasks. The harnessrouter.ai benchmark page couldn't be fetched, so the 223 vs 0.47 methodology comes from secondary coverage. Every number is the project's own unless we label it as our analysis.
What It Actually Does
A harness is a complete agent runtime: the loop that plans, calls tools, edits files and decides when it's done. Codex, Claude Code, Pi, Gemini CLI and OpenHands are all harnesses. Each ships with its own CLI flags, session model, streaming format, file handling and failure modes.
HarnessRouter runs them as backends behind one HTTP API. Your product sends an OpenAI Responses-style request and names the harness in metadata.harness_id. HarnessRouter starts that harness's CLI in an isolated workspace, translates the CLI's output into Responses events, and returns the files it produced.
Layer | What it is |
|---|---|
API surface | OpenAI Responses compatible ( |
Protocol | Unified Harness Protocol (UHP), draft |
Harnesses | 30 backends, from Codex and Claude Code to Aider, OpenHands, goose and Qoder Cloud Agent |
Deployment | One Docker container: Console |
Code mass | About 30.6K lines of gateway Python, 26.0K of runner Python, 35.8K of TypeScript console, roughly 55K lines of tests |
Maker | Epsilla (YC S23). First launch July 24, Community Edition August 16 |
Think of a universal remote, one that has to learn each TV's infrared codes itself because none of the manufacturers agreed on any.
The Architecture, Unpacked

What to notice: every harness's output is converted to one internal dialect, Claude Code's stream-json, before the gateway re-emits it as OpenAI Responses events. Two translations, one canonical middle.
Three design decisions carry the system.
One canonical event format, borrowed. HarnessRouter didn't invent an internal event schema. It uses Claude Code's stream-json as the lingua franca, so Claude Code, Qwen Code and System One pass straight through, and every other harness gets a *_to_claude translator. Of the 30 backends, 7 use the passthrough, 1 (Hermes) runs on its own path, and 22 share 20 distinct translators. Forks share code: MiMo reuses OpenCode's, and Oh My Pi reuses Pi's.
Sessions are Linux users, not containers. Each session gets a workspace owned by a uid from 20000 up. Agent processes run as that uid, and the runner's own writes into a session tree switch the thread's filesystem identity (setfsuid) so a symlink the agent plants can't redirect a root write. This is lighter than a container per session and it's the one-box tradeoff. Cloud uses serverless sandboxes instead.
Credentials never enter the turn. Since #620 (November 2), the CLI gets a loopback relay URL and a placeholder key, and the relay attaches the real credential. The agent has a real shell, so anything readable by the session user is assumed leaked.
The Code, Annotated
Snippet one: the routing table. One dict decides what a harness_id means. Excerpted from runner/server.py, with our comments marked # SnackOnAI:.
BACKENDS = {
# SnackOnAI: Claude Code's stream-json IS the internal format, so it needs no translation.
"claude": {"providers": sorted(CLAUDE_PROVIDERS), "default_model": CLAUDE_DEFAULT_MODEL,
"normalize": _claude_passthrough},
# SnackOnAI: Codex emits thread.started / item.completed / turn.completed; mapped event by event.
"codex": {"providers": sorted(CODEX_PROVIDERS), "default_model": CODEX_DEFAULT_MODEL,
"normalize": _codex_to_claude},
"pi": {"providers": sorted(PI_PROVIDERS), "default_model": PI_DEFAULT_MODEL,
"normalize": _pi_to_claude},
# qwen-code emits claude's stream-json natively ... so its normalizer IS the claude passthrough.
"qwen": {"providers": sorted(QWEN_PROVIDERS), "default_model": QWEN_DEFAULT_MODEL,
"normalize": _claude_passthrough}, # ← THIS is the trick: pick the dialect
# the most CLIs already speak
# omp is pi's lineage and speaks pi's `--mode json` event stream unchanged
"omp": {"providers": sorted(OMP_PROVIDERS), "default_model": OMP_DEFAULT_MODEL,
"normalize": _pi_to_claude}, # SnackOnAI: forks inherit translators
# Qoder's runtime is not a process this runner spawns: the driver is an HTTP client
"qoder": {"providers": sorted(QODER_PROVIDERS), "default_model": QODER_DEFAULT_MODEL,
"normalize": _qoder_to_claude, "remote": True}, # SnackOnAI: remote gets a cancel grace
# ... 24 more entries
}
The protocol is portable. This table isn't: every new harness costs a translator, a provider list and a test column.
Snippet two: token accounting. This is the line that decides whether a cost comparison is real. From _norm_token_usage in the same file.
def _norm_token_usage(u: dict | None) -> dict:
# SnackOnAI: Codex nests counts two levels deep in one mode and flat in another, in camelCase or snake_case.
for key in ("tokenUsage", "total_token_usage", "totalTokenUsage", "info", "usage"):
inner = u.get(key)
if isinstance(inner, dict):
u = inner
break
...
inp = _pick("input_tokens", "inputTokens", "prompt_tokens", "promptTokens", "totalInputTokens")
cache_read = _pick("cache_read_tokens", "cacheReadTokens",
"cached_input_tokens", "cachedInputTokens",
"cache_read_input_tokens", "cacheReadInputTokens")
# codex reports gross input (fresh + cached read). Net the cached read out so input_tokens is
# FRESH only ... Guard against a provider that already reports net input
fresh = max(inp - cache_read, 0) if cache_read else inp # ← THIS is the trick
res = {"input_tokens": fresh, "output_tokens": out}
# SnackOnAI: the docstring says that without this, cached tokens were billed at the full input
# rate (~10x), which "inflated cost several-fold" on long conversations.
Twelve field spellings for three numbers. A harness comparison is only as honest as this function.
Snippet three: the swap the product promises. From the README, with only harness_id changed in the second call.
export HARNESSROUTER_BASE_URL=http://localhost:3000/api/harness
# Same request body, same model family; the harness is one field.
for H in codex pi; do
curl --fail-with-body -sS "$HARNESSROUTER_BASE_URL/v1/responses" \
-H "Authorization: Bearer ${HARNESSROUTER_API_KEY:?}" \
-H 'content-type: application/json' \
-H "Idempotency-Key: $(python3 -c 'import uuid;print(uuid.uuid4())')" \
-d "{\"input\":\"Reply with exactly: it works.\",
\"metadata\":{\"harness_id\":\"$H\"},
\"model\":\"gpt-5.4-mini\", \"stream\":false}" # ← THIS is the whole migration
done
# Idempotency-Key: a retried POST returns the first response id; a different body → 409.
Switching costs one field, provided both harnesses support your provider. Check the support matrix first: Codex passes all five scenarios on 85% of its rows, DeepSeek Harness on 99%.
It In Action
Input. The project's own benchmark: the first 50 tasks of SpreadsheetBench Verified. Each task is a workbook (input.xlsx) plus a plain-language instruction, and the agent must produce output.xlsx. The model is fixed at deepseek-v4.1-flash behind one connection, four harnesses run with web tools off, there are two workers, and each task is capped at 900 seconds.
Step one, stage. The benchmark runner opens one session per task and uploads the workbook with POST /v1/files. The file is renamed so no suite vocabulary leaks into the prompt. An earlier pilot found an agent fetching the answer key because the task id was in a filename.
Step two, run. The Gateway reserves the session lease, the Runner spawns the harness CLI as the session uid, and the translator converts its stream. The agent writes output.xlsx.
Step three, grade. The suite's own evaluation.py compares answer cells, and LibreOffice recalculates any formulas first. A run that touched the network, or grepped outside its workspace for the task, is recorded as a finding and not scored. Four runs were findings, one of them find / -name '*.xlsx'.
Output (docs/benchmark.md):
Harness | Resolved | Median wall | Tool calls | Fresh in | Cached in | Output |
|---|---|---|---|---|---|---|
pi | 40/47 (85%) | 48 s | 526 | 484k | 7.53M | 546k |
opencode | 39/48 (81%) | 75 s | 563 | 666k | 11.64M | 709k |
dsh | 36/46 (78%) | 100 s | 721 | 761k | 12.32M | 752k |
cline | 36/47 (77%) | 108 s | 736 | 1.20M | 19.74M | 1.21M |
Our analysis. We weighted tokens at fresh 1, cached 0.1 and output 4 (a typical price shape, stated as an assumption) and computed it from the raw benchmark-results.json. Cline spends 1.95x Pi's token budget overall and about 2.2x per resolved task. Accuracy spans only 8 points: 31 of the 48 gradable tasks were solved by all four harnesses and 6 by none. Cached reads are 94 to 95% of input on every harness. Prompt caching, not prompt length, decides the bill.
same model, same 50 tasks
accuracy spread ██ 8 pts (77% → 85%)
cost spread ████████████████ ~2x (pi → cline, our weighting)
wall-time spread ███████████████ 1.9x (3,605 s → 6,882 s)
The harness changes what you pay far more than whether you get the answer.
Why This Design Works (and what it trades away)
It works because the job, not the completion, is the unit. UHP's own framing is right: a model API gives you a turn, and a harness gives you a task with its own tools, session and files. Wrapping the task level means your product inherits each harness's hard-won agent loop. SWE-agent showed in 2024 that the agent computer interface moves results as much as the model does, and this is the operational version of that idea.
It works because it borrows a dialect. Using Claude Code's stream-json as the canonical middle meant a quarter of the backends needed no translator at all.
It trades away isolation strength. Sessions are separate uids on one kernel, with a loopback relay for keys. That's sound engineering, but it isn't a container boundary, and the hardening is recent. Credential isolation landed November 2, at-rest encryption on every install November 4, and the published default password was retired in the same push. Treat any release from before November 4 as untrusted on a shared host.
It trades away scale-out. Turns run as threads in one Runner process with an in-memory turn table, capped at 6 hours each (MAX_TURN_SECONDS = 21600). Community Edition is one box. Horizontal scale is the Cloud product.
It trades away fidelity at the edges. The translators are lossy where the CLIs are. Codex's exec mode "has no record to read its output from," so command outputs are stored marked incomplete. A harness that can't resume a session adds a resume_lost event rather than silently restarting. Honest, but your UI has to handle it.
Technical Moats
The translator corpus. Twenty hand-built stream translators, each pinned to a measured CLI version ("verified against the shipped 0.22.1"). This is tedious, decaying work, and it compounds.
The support matrix. 2,814 harness × model × provider rows across 48 providers, each testing first turn, follow-up, model switch, artifact and sandbox recycle. 2,728 pass all five (96.9%). Most failures are provider content filters, not harness bugs. Nobody else publishes this.
Accounting discipline. A benchmark suite with written rules: no LLM judge, findings never scored, tokens on one convention. Their rule 4 exists because one harness once looked 15x cheaper on the same model, and the difference turned out to be an accounting bug.
A protocol with a test suite. UHP is versioned (five dated versions since August 11) and "conformant" is defined by runnable tests, so a competing server has to do more than claim compatibility. Alibaba's Qoder has adopted it.
Insights
One: the 474x headline is mostly model choice, and the honest number is about 2x. The 223 vs 0.47 spread mixes harnesses and models. According to coverage of the benchmark, on a fixed model the harness alone changed cost by up to 2.1x, which is about 12% of the spread on a log scale by our arithmetic. Independent studies agree on the size. Arena's harness study found up to 5x cost for similar success, and Pi beat Codex on Terminal-Bench 2.0 at about half the cost (Arena). A controlled private suite found no significant average solve rate difference between native and neutral harnesses, with a 1.2 to 1.6x cost gap (Arjmandi). A 2x lever on a large agent bill is worth a router. A 474x lever is a model decision dressed as a harness feature.
Two: "one API" doesn't mean "one behavior." The API is uniform, but behavior isn't. On the same tasks, failure modes differed: Cline reached the network once despite an instruction-based web switch, and Pi and DeepSeek Harness each went searching the filesystem for answers. Codex passes the full scenario set on 85% of matrix rows, Docker Agent on 100%. Swapping harnesses is a one-field change to the request and a full re-evaluation of your agent. Budget for the second.
Surprising Takeaway
The hardest bug in harness comparison is counting tokens, and it keeps recurring. HarnessRouter's OpenCode column first reported 81k output tokens where the same tasks generated about 700k, because reasoning tokens were kept apart from text. Codex reports cached reads inside input, which once billed cached tokens at roughly 10x. Independently, the Arjmandi study had to revise its August version because a double-counted cache rule inflated one harness's cost 5 to 8x. If you compare harnesses with numbers you didn't normalize yourself, you're probably comparing telemetry bugs.
TL;DR For Engineers
HarnessRouter wraps 30 agent CLIs behind an OpenAI Responses-compatible API. You pick the harness with
metadata.harness_id, and switching is one field.Internally, every harness's output is translated into Claude Code's stream-json (20 distinct translators plus a passthrough), then re-emitted as Responses events.
On the same model and 50 SpreadsheetBench tasks, harness choice moved accuracy 8 points and cost about 2x. The "99.8% cheaper" headline is mostly model choice.
Sessions are Linux uids with a credential relay, not containers. That hardening landed in early November, so pin a current release.
Before trusting any harness cost comparison, check how cached and reasoning tokens were counted.
Explain It Like I'm New
An AI model on its own only writes text. To actually do work, like editing a spreadsheet or fixing code, it needs a program around it that lets it run commands, open files, check its results and try again. That program is called a harness. OpenAI's Codex and Anthropic's Claude Code are harnesses, and there are dozens more.
The trouble is that every harness speaks its own language. If you build a product on one and later want a cheaper or better one, you rewrite the plumbing: how you start a job, watch its progress, collect its files and handle its errors.
HarnessRouter is a universal adapter. Your product speaks one language to it, and it translates to whichever harness you pick. Changing harnesses becomes a setting rather than a project.
Its own tests show why that matters. On the same tasks with the same AI model, one harness used about half the resources of another and got slightly more answers right. The bigger cost differences still come from which model you choose.
The broader shift is that companies are starting to treat these agent programs as interchangeable parts, the way they already treat databases or cloud providers, and to shop among them on price and reliability.
See It In Action
HarnessRouter Community Edition launch, Product Hunt (Epsilla). See the launch. The maker's screens of the single-container setup and the Console, plus reviewer notes from people who ran it, launched August 16, 2026.
Starter Kits, HarnessRouter on GitHub. Explore the kits. Slides, Sheets, Dashboards, Videos and Harness Bot, each running on a different harness. This is the clearest picture of harnesses as product backends rather than coding tools.
Harness support matrix, HarnessRouter docs. Browse the matrix. Not a video, but the most concrete demonstration of what "any harness" means in practice: 2,814 tested combinations, with failure reasons.
We found no conference talk or independent video walkthrough as of November 5.
Community Conversation
songrenchu (HarnessRouter team), Show HN thread. When asked why anyone would route across coding agents, he agreed that coders stick with one. The target is products like an AI presentation tool that package a harness as their backend, and OpenRouter-style fallback and cost routing are planned.
Gal Dayan, Product Hunt review. He praised the one-container setup and built-in sessions, but said the docs are "thin on what happens when a harness-specific feature isn't available."
LoudLemur (Cloudron packager), Cloudron forum. He compared the trust model to Paperclip's, with agents running bash and git on your provider keys, and noted the single local user with no OIDC or LDAP.
Dr. Prahlad G. Menon, The Menon Lab. He frames HarnessRouter as the harness-level counterpart to LiteLLM and Portkey, which route between models rather than between agents.
Arena team (study by Pan, Yang, Arabzadeh, Chiang, Stoica, Zaharia), The harness tax. An alternative harness had the best success rate in 9 of 12 comparisons, and Claude Code's starting context averaged more than 10x Pi's.
Conclusion: Buy the Translator, Not the Headline
HarnessRouter is a good piece of infrastructure, and its marketing undersells why. The cost headline is mostly a model comparison. The real asset is the layer underneath: 20 maintained stream translators, a 2,814-row support matrix, a benchmark suite strict enough to catch agents grepping for answer keys, and a token accounting function that makes harnesses comparable at all. A 2x cost lever, behind one field and with a written protocol, is worth having. Pin a release from November or later, re-evaluate every harness you swap in, and normalize the tokens yourself.
References
SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering, Yang et al. The interface around the model is a first-class performance variable.
OpenHands: An Open Platform for AI Software Developers as Generalist Agents, Wang et al. An open harness that now runs as one of HarnessRouter's backends.
Harness or Model? Isolating the Harness Effect in Agentic Coding with a Contamination-Controlled Private Suite, Arjmandi. No significant average solve rate gap, a 1.2 to 1.6x cost gap, and a cache-token accounting correction.
An Empirical Study of Harness Design for Coding Agents, Fan et al. Ablates planning, tool sets and context management on fixed models. A bash-only harness cut cost 30 to 53% on one model.
SpreadsheetBench: Towards Challenging Real World Spreadsheet Manipulation, Ma et al. The deterministic suite behind HarnessRouter's own benchmark.
HarnessRouter puts 30 agent harnesses behind one OpenAI Responses-compatible API by translating each CLI's event stream into one canonical format. Its own data shows the harness moves cost about 2x on a fixed model, while most of the advertised 474x spread is model choice. The durable value is the translators, the support matrix and the token accounting, not the headline.
Sponsored Ad If you enjoy practical AI insights, check out SnackOnAI and support the newsletter by subscribing, sharing, and exploring our sponsored ad—it helps us keep building and delivering value 🚀
Yes, that Arnold Schwarzenegger.
He has a newsletter on beehiiv. So do Codie Sanchez, Jay Shetty, David Begnaud, Colin & Samir, and Joanna Stern.
They could publish just about anywhere. They chose beehiiv to build a direct connection with the people who want to hear from them most. And you can do the same, whether you’re world-famous or just at the start of your plan for world domination.



