AI News HubLIVE
站內改寫6 分鐘閱讀

待翻譯:Show HN: HarnessRouter: Unified interface for agent harnesses

AI 服務暫時不可用,以下為來源摘要,待恢復後補全翻譯:Uh oh! There was an error while loading. Please reload this page. Notifications You must be signed in to change notification settings Fork 6 Star 95 BranchesTags Open more actions menu Folders and files NameName Last co…

來源Hacker News AI作者: songrenchu

AI 服務暫時不可用,以下為來源正文,待恢復後補全翻譯。

Uh oh! There was an error while loading. Please reload this page. Notifications You must be signed in to change notification settings Fork 6 Star 95 BranchesTags Open more actions menu Folders and files NameName Last commit message Last commit date Latest commit History 105 Commits 105 Commits .github .github docker docker docs/images docs/images gateway gateway protocol protocol runner runner scripts scripts ui ui .dockerignore .dockerignore .env.example .env.example .gitignore .gitignore CODE_OF_CONDUCT.md CODE_OF_CONDUCT.md CONTRIBUTING.md CONTRIBUTING.md Dockerfile Dockerfile LICENSE LICENSE NOTICE NOTICE README.md README.md SECURITY.md SECURITY.md docker-compose.yml docker-compose.yml Repository files navigation Run agent harnesses on your own machine. Run agent harnesses on your own machine. One container, your own API keys, your own data. Configure a harness, give it work, watch it run, with no account, no cloud, and no telemetry. Community Edition implements the Unified Harness Protocol (UHP), the open standard the hosted service implements too. Tip New here? Start with What it is, or read the protocol at unifiedharnessprotocol.org. Install Six steps, and at the end of them you have a running instance, a signed-in console, and an agent that has answered you. You need Docker, about 4 GB of disk, and an API key from a model provider. There is no account to create and nothing to sign up for. The provider key is the only credential in the story, and it never leaves the box except to call the provider it belongs to. 1. Pull the image docker pull harnessrouter/harnessrouter About 700 MB to download. Pinning a version instead of latest latest is the current release, and pulling it again is how you upgrade. Pin a version only when you need two machines to run the same bytes, by naming a version in a compose file you share with a team. Releases are listed on Docker Hub. 2. Run it Copy this as it is. Nothing in it is a placeholder — no provider key, no password. docker run -d --name harnessrouter \ -p 127.0.0.1:3000:3000 \ -v harnessrouter:/data \ harnessrouter/harnessrouter If port 3000 is already busy, change only the left-hand number (-p 127.0.0.1:3100:3000), because the container always listens on 3000 inside. What each part of that line does -p 127.0.0.1:3000:3000 keeps the console reachable only from this machine. That is what makes it safe to start on a default login and change it afterwards. -v harnessrouter:/data is where everything durable lives: the database, your files, and the agent CLIs installed on the first start. Keeping that volume is what makes every later start fast. No provider key, because you connect a provider from the console in step 5. That is the shorter road: a key pasted into a form cannot be misspelled into a shell history, and changing it later does not mean recreating the container. No password, because the instance starts on a default login that step 4 gives you and asks you to change. Choosing your own username and password at docker run Two optional variables. Set them and they replace the defaults; the console never shows you the default login again. docker run -d --name harnessrouter \ -p 127.0.0.1:3000:3000 \ -v harnessrouter:/data \ -e HR_AUTH_USER=you \ -e HR_AUTH_PASSWORD=the-password-you-chose \ harnessrouter/harnessrouter Whatever you put after HR_AUTH_PASSWORD= is the password — sign in with exactly that in step 4. You do not need this to get started, and changing the password from the profile page later works just as well; it exists for a box built by a script, where nobody is going to open a browser. Using Docker Compose instead cp .env.example .env, then docker compose up -d. Read docker-compose.yml first: it publishes 3000:3000 on every interface rather than on loopback. Change that one line and it behaves like the command above. 3. Wait for it to say it is ready Do not open the browser yet. docker run gives your prompt back in about a second, but the console needs roughly another half a minute, and until then http://localhost:3000 refuses the connection. That is the first start still working, not a broken container. docker logs -f harnessrouter Wait for ready on :3000, then open the browser: [harnessrouter] installing Claude Code (Anthropic's terms apply)… [harnessrouter] installing Codex (Apache-2.0)… [harnessrouter] installing Hermes (check its upstream license before use)… [harnessrouter] data=/data backends available: claude codex hermes [harnessrouter] ready on :3000 This wait happens once per volume. Every start after it takes a few seconds and prints no install lines at all. The other lines, and why the first start is the slow one backends available: lists what actually installed, so a backend that failed is named rather than silently missing, and the others still work. You will also see this line, and it comes back on every start until you change the password in step 4. On a loopback-only instance it is a reminder rather than a problem: [harnessrouter] WARNING: using the DEFAULT password. Set HR_AUTH_PASSWORD, or change it from the profile page, before exposing this instance. The agent CLIs are fetched on the first start rather than shipped in the image, and that is a licensing fact rather than a packaging preference. Claude Code is distributed under Anthropic's own terms and hermes-agent declares no license at all, so neither can be redistributed inside a public image. Installing them on first run means you install them yourself, from upstream, under those terms — which is also why you should read them before you use those two backends. Codex is Apache-2.0 and arrives the same way, so all three land in one place. 4. Sign in Open http://localhost:3000 and sign in with: Username harnessrouter Password harnessrouter Change the password now, from Profile in the account menu at the top right. Saving restarts the console, which takes about a second. If you set HR_AUTH_USER or HR_AUTH_PASSWORD at docker run, sign in with those instead — the defaults are then refused. Where the password lives, and what to do if you forget it Printing the defaults here is what makes them a placeholder rather than a secret, which is why the container warns about the password on every start until you change it. The profile page asks for the current password as well as the new one, so an unattended tab cannot be used to take over the instance. New credentials are stored on the data volume (/data/selfhost-auth.json: a username, a salt and a hash, never the password) and take precedence over the environment from then on — an HR_AUTH_PASSWORD set at docker run months ago cannot quietly undo a password change. After one, the start-up line changes to say where the real password came from: [harnessrouter] sign in as 'harnessrouter' (credentials set from the profile page) Saving also signs out every other browser. Yours stays signed in. Forgot it? There is no reset email to send, so delete /data/selfhost-auth.json and restart. The instance falls back to HR_AUTH_USER / HR_AUTH_PASSWORD. 5. Connect a model provider Nothing runs until you do this. There is no bundled model, no trial key, and no free tier hiding in the image. Open Integrations and press Add Integration. It asks three things: a name, the provider, and your API key. Which models that provider serves is not your problem to configure: the product keeps that list and adds to it as providers ship models. Pick the provider, paste the key, and the models it covers appear on the row. Running more than one provider The mappings underneath the integrations decide which one serves a given model. With a single integration there is nothing to set. Setting it from the environment instead, for a scripted deploy A connection names a provider and its credential; a policy says which connection a backend uses. Useful when the box is built by a script and nobody is going to open a browser: -e HR_SECRET_GLOBAL_HARNESS_CONN_ANTHROPIC='{"name":"anthropic","provider":"anthropic","api_key":"sk-ant-…"}' -e HR_SECRET_GLOBAL_HARNESS_POLICY_CLAUDE='{"chain":["anthropic"]}' There is one policy variable per backend: …POLICY_CLAUDE, …POLICY_CODEX, …POLICY_HERMES. An OpenAI-compatible endpoint of your own takes the same pair with a base_url added, and "provider":"openai" rather than the "openai-api" that .env.example still shows: -e HR_SECRET_GLOBAL_HARNESS_CONN_LOCAL='{"name":"local","provider":"openai","api_key":"…","base_url":"https://api.example.com/v1"}' -e HR_SECRET_GLOBAL_HARNESS_POLICY_CODEX='{"chain":["local"]}' Not every provider fits every backend, and a pairing that does not fit fails quietly: the turn comes back empty after a long wait rather than erroring. The Integrations page does not have this problem, because it only offers you providers that work. Connection provider Backends that can use it anthropic Claude Code, Hermes openai Codex, Hermes openrouter Codex, Hermes azure-foundry Codex, Hermes bedrock Claude Code, Hermes tokenrouter Claude Code, Codex, Hermes vercel Claude Code, Codex, Hermes What a backend with nothing connected says Forthcoming about it, which is what you get if you skip this step entirely: {"error":{"type":"invalid_request_error","code":"invalid_input","message":"no provider configured for backend 'codex'. Add an integration for a provider that serves 'gpt-5.4-mini', or configure a connection policy"}} 6. Give it something to do Tasks → New Task. Pick a harness in the switcher on the left, choose a model next to the message box, and type. The turn streams back as it happens: every command the agent runs, every file it touches, and the answer at the end. That is the whole install. State is SQLite and files on one Docker volume. Delete the volume and the instance is gone; copy it and you have moved the instance, harnesses, transcripts and all. What is happening in that screenshot That one asked for a small utility with tests. The agent wrote it, built a fixture tree with duplicates planted in it, ran the suite, and came back with 2 tests ran, OK, which is an answer you can check rather than one you have to trust. Everything it produced is on the transcript to take away, a file at a time or the lot as a zip. Starter kits Starter kits are worked examples, and they are here to show you what this can be pointed at. Each one is a whole agent product rather than a snippet: an app, an agent configured to drive it, and the skill that teaches that agent the format it writes. Use one, then read it: every kit is open source. More arrive over time; your instance lists the ones it has. Launching asks one question: what to run it on. What the launch dialog is telling you Each card names the base and the model it will run on before you launch it, so you can see what a kit is about to spend before it spends it. What it names depends on the keys you gave it in step 5: the screenshot above is an instance with three providers connected, and an instance with one will recommend that one on every card. The runtimes you have no key for are listed but disabled, with the reason on them: Hermes · deepseek-v4-pro · Not connected. Add a provider that serves this model to use it. What the dialog recommends is a suggestion you can overrule, not a default you have to accept. Slides A deck is one conversation. Ask for a presentation and the agent designs it: structure first, then a style system, then slide by slide. Slides appear while it works, so when the shape is wrong you can say so while there are two slides to change instead of twenty. The deck below came from one sentence: "A 5-slide deck explaining what a container image is, for new engineers." What you are looking at in the panel on the right That is the run, not a pr [truncated for AI cost control]