Skip to main content

Running the harnessd Daemon

harnessd is the HTTP daemon at the center of go-code. It boots a complete agent runtime — LLM provider, tool registry, memory, cron scheduler, MCP client, skills, and workflow engines — and exposes everything over a REST + SSE (Server-Sent Events) API on a single TCP port (:8080 by default). Clients like harnesscli, the BubbleTea TUI, or any HTTP client can submit agent runs, stream live events, steer running agents, and manage conversations without coupling directly to the Go runtime.

If you need a daemon that stays running while you iterate in other terminals, or you want to connect Claude Desktop and other MCP hosts to your local agent runtime, harnessd is where you start.


Starting the server

harnessd can be started four ways depending on your workflow.

Build the binary, then export your API key and workspace path:

go build -o harnessd ./cmd/harnessd
OPENAI_API_KEY=sk-... HARNESS_WORKSPACE=$(pwd) ./harnessd

When the daemon is ready it prints:

harness server listening on :8080

You can then POST runs to http://localhost:8080/v1/runs and stream events from http://localhost:8080/v1/runs/{id}/events.


Flags and listen address

harnessd has three CLI flags:

FlagDefaultDescription
--profile <name>""Load a named profile from ~/.harness/profiles/<name>.toml or <workspace>/.harness/profiles/<name>.toml. Profile names must not contain /, \, .., or be absolute paths.
--mcpfalseStart in MCP stdio mode instead of HTTP. See MCP stdio mode below.
--mcp-workspace <path>""Workspace root for MCP stdio mode.

Listen address resolution

The address harnessd binds to is resolved in five layers, lowest to highest priority:

PrioritySourceExample
1 — lowestBuilt-in default:8080
2~/.harness/config.toml (addr field):9000
3<workspace>/.harness/config.toml (addr field):9000
4Named profile (via --profile):9090
5 — highestHARNESS_ADDR env var:8888

The resolved address is passed directly to net/http.Server.Addr. Use the socket form (:8080), not a full URL.

HTTP server timeout constants

These values are hardcoded and are not configurable via environment variables:

SettingValue
ReadTimeout60 s
ReadHeaderTimeout10 s
IdleTimeout120 s
MaxHeaderBytes1 MiB (1 << 20)

Non-streaming request handler timeout (for routes that do not end in events, stream, or wait) is 30 seconds. Streaming paths bypass this timeout entirely.


Bootstrap and provider resolution

20-step boot sequence

When harnessd starts in HTTP mode, runWithSignalsWithDeps performs these steps in order:

  1. Load layered config

    Calls config.Load, applying the six-layer cascade: built-in defaults → user global TOML → project TOML → named profile → HARNESS_* env vars → cloud/team constraints (stub, not yet active).

  2. Resolve HARNESS_WORKSPACE

    Defaults to "." if not set. All relative paths for DB files and content directories are anchored here.

  3. Build catalog bootstrap

    Loads catalog/models.json (auto-detected or via HARNESS_MODEL_CATALOG_PATH), builds the ProviderRegistry, and sets up the pricing resolver.

  4. Resolve default provider

    Runs resolveDefaultProvider — see the priority order immediately below.

  5. Boot prompt engine

    Initializes systemprompt.NewFileEngine from the prompts directory — HARNESS_PROMPTS_DIR when set, otherwise an auto-detected prompts/ directory (found by walking up for prompts/catalog.yaml).

  6. Open memory manager

    Opens SQLite or Postgres based on HARNESS_MEMORY_MODE and HARNESS_MEMORY_DB_DRIVER.

  7. Open SQLite stores

    Opens checkpoint, workflow, and working-memory stores — all at orchestrationDBPath inside the workspace.

  8. Load workflow and network definitions

    Reads YAML files from HARNESS_WORKFLOWS_DIR and HARNESS_NETWORKS_DIR if set.

  9. Boot skills system

    Loads and registers SKILL.md files from the global and workspace skills directories.

  10. Boot cron

    Starts the embedded SQLite scheduler (default) or connects to an external service if HARNESS_CRON_URL is set.

  11. Boot MCP client manager

    Registers MCP servers from TOML config layers (TOML takes precedence), then from HARNESS_MCP_SERVERS env var (skipping duplicate names).

  12. Boot persistence stores

    Opens the run store, conversation store, and relay worker store — only when HARNESS_RUN_DB, HARNESS_CONVERSATION_DB, and HARNESS_RELAY_DB are set respectively.

  13. Wire brokers and activations

    Sets up the ask-user broker, tool-approval broker, and activation registry.

  14. Build RunnerConfig and harness.Runner

    Assembles the core run execution engine with all wired dependencies.

  15. Start hot-reload watcher

    When HARNESS_WATCH_ENABLED=true (default), starts a poll-based watcher on the global and workspace skills/workflows directories.

  16. Build HTTP runtime

    Initializes the workflows.Engine, networks.Engine, subagents.Manager, and script workflow engine.

  17. Mount handlers

    Mounts mainHandler at / and the MCP HTTP server at /mcp — both on the same TCP port.

  18. Start http.Server

    Calls ListenAndServe in a goroutine.

  19. Log ready

    Prints harness server listening on <addr> to stdout.

  20. Block on signal

    Waits for SIGINT or SIGTERM, then shuts down gracefully with a 10-second timeout.

Provider resolution order

resolveDefaultProvider picks the default LLM provider at startup by trying these four paths in order:

  1. Fake — if HARNESS_PROVIDER=fake, use fakeprovider.New(turns). No API key needed.
  2. Catalog — if the model catalog is loaded and the configured default model (HARNESS_MODEL, default gpt-4.1-mini) resolves to a provider that has its API key set, use that catalog client. Supported catalog providers include openai, anthropic, deepseek, groq, xai, kimi, qwen, together, openrouter, and gemini.
  3. OpenAI legacy — if OPENAI_API_KEY is set, bootstrap OpenAI directly.
  4. Error — if none of the above applies, harnessd exits with an error message.

Key environment variables

The full set of HARNESS_* variables is documented on the Configuration page. The table below covers the most important ones for getting harnessd running.

Core startup

VariableDefaultDescription
HARNESS_ADDR:8080HTTP listen address in socket form.
HARNESS_WORKSPACE.Workspace root — anchors all relative paths.
HARNESS_MODELgpt-4.1-miniDefault LLM model for runs.
HARNESS_MAX_STEPS8Max tool-calling steps per run. Set to 0 to remove the cap.
HARNESS_PROVIDERSet to fake for key-free mode.
HARNESS_FAKE_TURNSPath to the JSON turns file when HARNESS_PROVIDER=fake. Required when using fake mode.
HARNESS_AUTH_DISABLEDSet to true to disable Bearer-token auth.

HARNESS_MAX_STEPS gotcha

Setting HARNESS_MAX_STEPS=0 in a TOML config file means "unlimited." However if the env var is absent and the config stack resolved to 0, harnessd resets it to 8 as a backward-compatible default. To truly remove the step cap at runtime, set HARNESS_MAX_STEPS=0 explicitly in your environment.

Persistence

All persistence stores are disabled unless their env var is set. When HARNESS_RUN_DB is not set, run records are kept only in memory and are lost on restart.

VariableDescription
HARNESS_RUN_DBSQLite path for run and event records. Required for GET /v1/runs (list).
HARNESS_CONVERSATION_DBSQLite path for conversation persistence.
HARNESS_RELAY_DBSQLite path for relay worker state.

Auth and persistence are linked

When HARNESS_RUN_DB is not set, there is no key store to validate against, so auth is implicitly disabled — the server will not return 401 even without HARNESS_AUTH_DISABLED=true. Similarly, GET /v1/runs (list all runs) returns 501 Not Implemented without a persistent store. Reads of individual runs by ID still work because runs are held in memory for the life of the process.

Provider API keys

Each provider reads its API key from a dedicated env var:

ProviderEnv var
OpenAIOPENAI_API_KEY
AnthropicANTHROPIC_API_KEY
DeepSeekDEEPSEEK_API_KEY
GroqGROQ_API_KEY
xAIXAI_API_KEY
Kimi / MoonshotMOONSHOT_API_KEY
Qwen / DashScopeDASHSCOPE_API_KEY
Together AITOGETHER_API_KEY
OpenRouterOPENROUTER_API_KEY
Google GeminiGOOGLE_API_KEY

You only need to set the keys for providers you actually want to use.


MCP stdio mode

In addition to its HTTP API, harnessd can run as an MCP (Model Context Protocol) server over stdin/stdout. This is useful when you want to drive harnessd from a host that speaks MCP natively — for example, from another agent or from a Claude Code session that uses harnessd as a tool provider.

# Start harnessd as an MCP stdio server
./harnessd --mcp

# Specify a workspace root explicitly
./harnessd --mcp --mcp-workspace /path/to/my-project

When --mcp is set, harnessd starts the stdio MCP server instead of the HTTP server. The workspace root is resolved from, in priority order:

  1. --mcp-workspace flag
  2. HARNESS_WORKSPACE env var
  3. . (current directory)

The stdio server exposes the full harness tool catalog as MCP tools under their native catalog names (e.g. read_file, bash, start_run, subscribe_run). The mcp_{server}_{tool} naming scheme applies in the reverse direction: it is how harnessd names tools it imports from external MCP servers it connects to as a client.

Proxy binary for Claude Desktop

If you want to connect Claude Desktop to a running harnessd HTTP instance (rather than spawning a new stdio process), build cmd/harness-mcp instead. It is a lightweight stdio proxy that bridges Claude Desktop's MCP host to any harnessd HTTP server at the address in HARNESS_ADDR.


Healthcheck

A simple healthcheck endpoint is available without authentication:

curl http://localhost:8080/healthz
# {"status":"ok"}

This is useful in Docker / Kubernetes liveness probes and in scripts that need to wait for the daemon to be ready.


Quick reference: common startup patterns

# Production: OpenAI, persistent runs DB, custom address
OPENAI_API_KEY=sk-... \
HARNESS_ADDR=:9090 \
HARNESS_WORKSPACE=/srv/myapp \
HARNESS_RUN_DB=/var/lib/harness/runs.db \
./harnessd

# Development: fake provider, no auth, default address
HARNESS_PROVIDER=fake \
HARNESS_FAKE_TURNS=./turns.json \
HARNESS_AUTH_DISABLED=true \
./harnessd

# MCP stdio mode: expose harness tools to a parent MCP host
./harnessd --mcp --mcp-workspace $(pwd)

# Multi-provider: Anthropic as default model
ANTHROPIC_API_KEY=sk-ant-... \
HARNESS_MODEL=claude-sonnet \
HARNESS_WORKSPACE=$(pwd) \
./harnessd

Next steps

  • Configuration — Full reference for the six-layer TOML + env var cascade and every HARNESS_* variable.
  • HTTP API Reference — Every route, request body shape, and response schema.
  • SSE Event Stream — The event types emitted during a run and how to subscribe to them.
  • MCP Integration — Connect external MCP tool servers to your agents, or drive harnessd from Claude Desktop.
  • Key-Free Testing — More depth on the fake provider and the TestRunSmoke in-process smoke test.