Skip to main content
A harness is the small program Archal runs instead of your app UI. It reads one task, calls your real agent code, and prints the final answer. npx archal init generates one for you at ./.archal/harness.mjs (run with node, no extra tooling). Prefer TypeScript? Put it at ./.archal/harness.ts and run it with tsx instead - the runner accepts harness.{mjs,js,ts,cjs}.

Template

./.archal/harness.ts
Send logs to stderr. Keep stdout for the final answer.

What Archal reads from stdout

Archal extracts the final answer from stdout in this order:
  1. One JSON object. Archal reads payloads[] or text. Recommended:
  2. JSON object in mixed stdout. Works, but logs can make this brittle.
  3. NDJSON. Useful if your harness streams events.
  4. Plain text. Archal joins non-empty stdout lines outside JSON blocks.
For most harnesses, print exactly one JSON object.

Environment variables

Archal sets these on the harness process before it spawns: Archal does not inject your model keys. Put OPENAI_API_KEY, ANTHROPIC_API_KEY, and similar secrets in .env or CI secrets.

Exit-code contract

How to route service traffic

Use the same SDKs, service domains, and normal credential env vars your agent would use in production. For example, use Octokit against api.github.com. Transparently routing unmodified SDKs that call real service domains (like Octokit against api.github.com) requires Docker or sandbox mode, so Archal can control DNS and TLS trust. A harness that instead calls the clone URL from AGENT_CLONE_URLS directly is scored without either (see the minimal example below). For a local harness that calls the clone directly instead of using Docker or sandbox routing, point the SDK at the matching URL in AGENT_CLONE_URLS and merge AGENT_ROUTE_HEADERS into each clone request. Keep the normal service authorization header too, for example Authorization: Bearer <test-token> for GitHub REST.

Minimal clone-calling harness (no agent framework)

If you do not have an agent yet, the fastest path to a first score is a harness that calls the clone’s REST API directly. Archal injects AGENT_CLONE_URLS and AGENT_ROUTE_HEADERS when it starts the run, so the harness needs no Archal SDK. This one matches the scenarios/first-run.md that archal init scaffolds
  • list and summarize GitHub issues, read-only:
./.archal/harness.mjs
Those two headers do different jobs: AGENT_ROUTE_HEADERS (x-route-authorization) authenticates you to Archal, while the normal service Authorization header is what the clone validates - keep both. The default bootstrap token for each service is listed in Authentication. Once you have a real agent, swap this for a call into it and let it use normal SDKs.

Check it locally

Run the entrypoint directly without provisioning clones:
That should print a final answer and exit 0. (The clone-calling example above prints No GitHub clone URL was provided. here - that is expected; the clone URLs only exist during archal run.) If it hangs or crashes, archal run will too.