archal run at a repo-local harness and also
pass --docker. Repo-local harnesses otherwise run locally by default.
In Docker harness mode, Archal builds and runs your repo in Docker, injects
environment variables that describe the scenario and the live twins, and
captures your container’s stdout as the agent response. This page describes
that contract so you can build your own harness from scratch.
How it works
- Archal resolves the repo-local harness you passed to
--harness. - Archal builds your image from the repo-root
Dockerfile(or a generated one if none exists). - Archal starts a local TLS-intercepting proxy that transparently forwards twin API traffic to the hosted cloud twins and adds the session auth header.
- Archal runs the container, passing all env vars described below and mounting a config directory at
/archal-out/. - Your container exits. Archal reads stdout as the agent response, stderr is logged, and non-zero exit code marks the run as failed.
Environment variables
The following env vars are set in every container. All are strings.Required — always present
| Variable | Contains |
|---|---|
ARCHAL_ENGINE_TASK | The scenario task text the agent must complete. This is the full natural-language instruction derived from the scenario’s setup and expected behavior. |
ARCHAL_ENGINE_MODE | Always "local". Identifies this as a local harness run. |
ARCHAL_TOKEN | Bearer token for twin REST calls. Pass as Authorization: Bearer $ARCHAL_TOKEN on every request. |
ARCHAL_TWIN_URLS | JSON object mapping twin names to their base URLs, e.g. {"github":"https://…","slack":"https://…"}. |
ARCHAL_TWIN_NAMES | Comma-separated list of twin names, e.g. github,slack. Use this to enumerate which ARCHAL_<TWIN>_URL vars are present. |
ARCHAL_REST_CONFIG | Absolute path to /archal-out/rest-config.json inside the container. |
ARCHAL_MCP_CONFIG | Absolute path to /archal-out/mcp-config.json inside the container. |
ARCHAL_MCP_SERVERS | Inline JSON string — same content as mcp-servers.json (see Mounted files). Useful if you do not want to read a file. |
ARCHAL_METRICS_FILE | Absolute path to /archal-out/metrics.json. Write a JSON metrics payload here before exiting (see Metrics below). |
ARCHAL_AGENT_TRACE_FILE | Absolute path to /archal-out/agent-trace.json. Write a trace payload here before exiting (optional). |
ARCHAL_API_PROXY_URL | URL of the local TLS proxy, e.g. http://host.docker.internal:PORT. All HTTPS traffic to twin domains routes through this proxy automatically. |
Per-twin URL vars
For each twin named inARCHAL_TWIN_NAMES, a dedicated URL var is also injected:
ARCHAL_<TWIN_NAME_UPPERCASE>_URL.
Optional — present when set by the caller
| Variable | Contains |
|---|---|
ARCHAL_ENGINE_MODEL | Model identifier the harness should use, e.g. claude-sonnet-4-6. Set by --agent-model or -m. |
ARCHAL_SESSION_ID | Archal session ID for the current run. |
ARCHAL_ENGINE_API_KEY | Generic API key forwarded from the host. |
ANTHROPIC_API_KEY | Forwarded from the host environment if set. |
OPENAI_API_KEY | Forwarded from the host environment if set. |
GEMINI_API_KEY | Forwarded from the host environment if set. |
NODE_ENV | Forwarded from the host environment if set. |
TLS proxy vars
These are injected automatically so standard HTTP clients trust the intercepting proxy without any code changes:| Variable | Value |
|---|---|
HTTP_PROXY / http_proxy | Proxy URL |
HTTPS_PROXY / https_proxy | Proxy URL |
NO_PROXY / no_proxy | 127.0.0.1,localhost,host.docker.internal |
NODE_EXTRA_CA_CERTS | /archal-out/ca.crt |
SSL_CERT_FILE | /archal-out/ca.crt |
REQUESTS_CA_BUNDLE | /archal-out/ca.crt (for Python requests) |
CURL_CA_BUNDLE | /archal-out/ca.crt |
requests, and curl all respect these vars out of the box. Other runtimes may need explicit CA configuration pointing to /archal-out/ca.crt.
Mounted files
The directory/archal-out/ is bind-mounted into the container. It contains:
| File | Contents |
|---|---|
rest-config.json | { "restEndpoints": { "<twin>": "<baseUrl>", … } } — a map of twin names to their REST base URLs. |
mcp-config.json | { "mcpServers": { "<twin>": { "url": "<mcpUrl>", "headers": { "Authorization": "Bearer …" } }, … } } — ready-to-use MCP server config. |
mcp-servers.json | Same content as mcp-config.json’s mcpServers key, without the wrapper object. |
ca.crt | PEM-encoded CA certificate for the intercepting proxy. Trust this cert in any runtime that does not read the standard env vars above. |
Tool discovery
CallGET {twinBaseUrl}/tools to retrieve all tools a twin exposes. No request body is required. The Authorization header is required.
name— tool identifier used in the call endpoint.description— human-readable description.inputSchema— JSON Schema object describing accepted parameters.
ARCHAL_TWIN_NAMES and prefix each tool name with mcp__<twinName>__:
Always pass a signal: AbortSignal.timeout(...) to every twin fetch. Without it, a hung twin worker leaves your harness blocking forever (#2169). 15s is a sensible default for /tools and /tools/call.
Tool execution
CallPOST {twinBaseUrl}/tools/call to execute a tool. The Authorization header is required.
name— the original (non-namespaced) tool name as returned by/tools.arguments— object matching the tool’sinputSchema. Pass{}for tools with no required parameters.
message field.
Output contract
| Stream / code | Meaning |
|---|---|
| stdout | Captured as the agent response text. Write your final answer or summary here. |
| stderr | Logged by Archal for debugging. Write progress, tool call results, and diagnostics here. |
| exit 0 | Run succeeded. Archal proceeds to evaluation. |
| exit non-zero | Run failed. Archal marks the run as an error and skips evaluation for this run. |
Metrics file (optional)
Write a JSON payload to$ARCHAL_METRICS_FILE before exiting to surface token usage in the run report:
exitReason should be one of: completed, max_steps, no_tool_calls, consecutive_errors, llm_error.
Minimal harness example
Using the bundled REST client
The bundled harnesses share a helper library atcli/harnesses/_lib/rest-client.mjs in the Archal repo. You can copy or vendor this file — it exports collectTwinUrls, discoverAllTools, and callToolRest, which implement the full discovery and execution protocol described above including namespacing.