You don’t have to choose between Claude Code and Codex: HarnessRouter runs both, plus eight more harnesses, behind a single API in a Docker container with your own keys. It’s free software under Apache 2.0 and runs on your infrastructure.
I’ve seen a lot of infrastructure sold as an “open standard.” The reflex it triggered in me seems worth explaining, because here the engineering is better than the governance, and a CIO needs to evaluate both separately.
Claude Code or Codex?
The question is poorly framed, and that’s precisely the product’s thesis.
Claude Code and Codex aren’t interchangeable. Each has its own execution loop, tools, error semantics, and release calendar. If your product incorporates one, you inherit all of that. The usual answer is to pick the best one and live with the decision for years.
HarnessRouter proposes the opposite: run both and switch based on the task. The project publishes a benchmark of eight harness-and-model combinations on the same recorded task, with savings up to 99.8% in cost and a 3.2× speed improvement.
It’s worth reading those numbers carefully, and the project itself warns about it: they’re self-reported data, they come from a single recorded task, and the two figures are separate comparisons between extremes, not the same outcome. What they don’t show is that switching harnesses will save you 99.8%. What they do show is that the difference between the cheapest and most expensive way to run the same task is enormous, and that today you’re making that choice blind.
That’s the real argument. Not “which is better,” but “why are you paying the price of a choice you never measured.”
What is HarnessRouter?
A harness is the layer around the model that turns it into an agent: the loop, the tools, the workspace, and session state. Claude Code is a harness. Codex is another.
HarnessRouter puts a single interface in front of all of them. The Community Edition packages the Console, Gateway, and Runner into a single Docker deployment and implements the Unified Harness Protocol (UHP), whose task surface is deliberately compatible with OpenAI’s Responses API. That means the SDKs, streaming parsers, and UI components you already use with Responses work against it.
It comes from Epsilla (YC S23) and launched publicly on August 17, 2026.
How do you install it?
You need Docker, about 4 GB of disk space, and an API key from some model provider. It doesn’t come with a model or a trial key included.
docker run -d --name harnessrouter \
-p 127.0.0.1:3000:3000 \
-v harnessrouter:/data \
harnessrouter/harnessrouter
The first startup installs the harness CLIs into the volume. Wait to see [harnessrouter] ready on :3000 in docker logs -f harnessrouter, open http://localhost:3000, and log in with harnessrouter / harnessrouter.
Those default credentials are real, and the container logs a warning while they remain active. Change them from Profile, or set them at startup:
docker run -e HR_AUTH_USER=your-username -e HR_AUTH_PASSWORD=your-password ...
Keep the loopback link (127.0.0.1:3000:3000) until you do. A documented detail worth knowing before writing a deployment manifest: don’t add --user. The entrypoint and Runner need root to manage per-session users.
What harnesses does it actually run?
The default set is ten:
docker run -e HR_BACKENDS=claude,codex,hermes,pi,dsh,opencode,qwen,gemini,cline,omp ...
Backends are installed in the data volume, not in the image, so this is a runtime adjustment: -e HR_BACKENDS=opencode gives you a minimal instance. If a backend fails to install, it’s not fatal—it just doesn’t appear in the console’s catalog.
Each agent’s CLI is installed under its original license and isn’t redistributed within the image.
How do you connect it to your own backend?
This is the part that matters if you’re building a product and not just evaluating a tool. Create an API key in /keys and call the Responses-compatible endpoint, selecting the harness with metadata.harness_id:
export HARNESSROUTER_BASE_URL=http://localhost:3000/api/harness
curl --fail-with-body -sS "$HARNESSROUTER_BASE_URL/v1/responses" \
-H "Authorization: Bearer ${HARNESSROUTER_API_KEY:?}" \
-H 'content-type: application/json' \
-d '{
"input":"Reply with exactly: it works.",
"metadata":{"harness_id":"codex"},
"model":"gpt-5.4-mini",
"stream":false
}'
Continuations maintain the session with previous_response_id, and "stream": true returns server-sent events.
There are three credentials here with three different functions, and confusing them is the first mistake everyone will make: the Console password is for the person using the browser, the provider key is what HarnessRouter uses to call the model, and the HarnessRouter API key is what your backend presents.
How much does it cost?
HarnessRouter Community Edition costs nothing: it’s Apache 2.0 and you run it on your own machine.
What you do pay for is the models, and you pay the provider directly with your own key, per use. There’s no intermediation, no credits, no product subscription. This changes the spending structure more than it appears: you shift from a fixed per-seat fee to a variable cost you can measure by task, and that you can optimize by choosing the right harness-and-model combination for each job.
That’s where the benchmark we talked about at the beginning stops being marketing and starts being budget.
Is it really open source?
Mostly yes, and the exceptions deserve naming.
The Community Edition is Apache 2.0, and that covers the reference implementation, the machine-readable schemas, and the conformance suite. The Starter Kits—Slides, Sheets, Dashboards, and Videos—live in a separate repository with different license terms. HarnessRouter Cloud is the managed commercial offering, running the same API contract in isolated serverless sandboxes.
And then there’s the clause I’d want any architect to read before standardizing on this: Apache 2.0 grants rights over the code and the specification, but explicitly not over the protocol name. “Unified Harness Protocol” and “UHP” are HarnessRouter trademarks. You can say your product “works with” UHP; you can only claim to implement it if you pass its conformance suite.
Is UHP a standard or one company’s protocol?
The honest answer, at the time of publishing this note, is the latter, governed with uncommon discipline.
Governance is led by maintainers and always starts with a proposal. Changes enter as UHP Enhancement Proposals, maintainers respond within ten business days with accepted / needs work / rejected with reasons, and nothing merges unless the specification, reference implementation, and conformance test advance in the same pull request. That last rule—“a specification sentence that nothing enforces is just a wish”—is better discipline than many corporate standards bodies have.
There’s also no certification body, no membership fee, no logo program. The suite is in the repository and anyone can run it against anyone’s server, including yours:
pip install -e protocol/conformance
uhp-conformance --base-url https://your-server --api-key "$KEY" --class full
The conformance run recorded by the reference implementation itself, dated September 4, 2026, passed all 64 checks in Full class with no failures or omissions, against suite 2026.8.11.post1 and protocol version 2026-08-11. The project makes clear without hedging that this is a dated measurement, not a rerun with each later version.To summarize: single-vendor maintenance, trademark-registered name, and absence of an independent body, but with a public and versioned specification, an executable conformance suite, and an explicit statement that the standard can be implemented without HarnessRouter Cloud, because it’s an HTTP contract and nothing in it requires a managed service.
That combination is defensible. It’s not the same as a multi-stakeholder standard, and I’d want to see it written that way in any architecture decision record that adopts it.
What does self-hosting actually isolate?
Read this before concluding that self-hosted means contained.
Sessions get separate workspaces and separate operating system users, not separate containers. The Console is the only published port; the Gateway (8080) and Runner (8081) listen on loopback inside the container. The entrypoint sets HR_SANDBOX_TRUST=owner, whose documented justification is that you own the machine, the agent, and the key, so the key is delivered directly to the agent rather than being intermediated.
It’s a coherent design for a machine you own and a workload you trust. It’s not a multi-tenant isolation boundary, and you shouldn’t treat it as such.
Practical consequences:
- Set
HR_SECRET_KEYso that saved connection strings are encrypted at rest, and keep the same key across restarts. - Use a read-only database account for the Dashboards kit.
- Put TLS in front via reverse proxy before exposing it on a public URL.
- Idle workspaces are reclaimed according to
HR_WORKSPACE_TTL_HOURS, 72 by default, and rehydrated from their checkpoint.0keeps them forever.
The Community Edition also disables product telemetry from the Console entirely, which is a real and unusual concession.
Should you adopt it?
If you’re shipping agent features inside a product and don’t want to lock your backend to one provider’s CLI, this is the most credible abstraction I’ve seen so far, and the cost of evaluating it is a docker run.
If you’re an individual developer choosing a coding agent for yourself, it solves a problem you don’t have. The founders themselves said so on Hacker News when asked why anyone would route between multiple harnesses: the value shows up when you package agents as product infrastructure for your end users. That thread, at the time of writing this note, had accumulated a modest 10 points and 14 comments: traction is on GitHub and the YC launch, not yet on organic community pull.
What I’d watch is the open core boundary. A young company, with a commercial cloud tier, a registered protocol name, and solo maintenance has every incentive to move that line later. Nothing in their behavior so far suggests they intend to. Still, put it in writing which side of the line each dependency falls on.
Related: How to connect all your coding agents with OmniRoute