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:
| Flag | Default | Description |
|---|---|---|
--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. |
--mcp | false | Start 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:
| Priority | Source | Example |
|---|---|---|
| 1 — lowest | Built-in default | :8080 |
| 2 | ~/.harness/config.toml (addr field) | :9000 |
| 3 | <workspace>/.harness/config.toml (addr field) | :9000 |
| 4 | Named profile (via --profile) | :9090 |
| 5 — highest | HARNESS_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:
| Setting | Value |
|---|---|
ReadTimeout | 60 s |
ReadHeaderTimeout | 10 s |
IdleTimeout | 120 s |
MaxHeaderBytes | 1 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:
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).Resolve HARNESS_WORKSPACE
Defaults to
"."if not set. All relative paths for DB files and content directories are anchored here.Build catalog bootstrap
Loads
catalog/models.json(auto-detected or viaHARNESS_MODEL_CATALOG_PATH), builds theProviderRegistry, and sets up the pricing resolver.Resolve default provider
Runs
resolveDefaultProvider— see the priority order immediately below.Boot prompt engine
Initializes
systemprompt.NewFileEnginefrom the prompts directory —HARNESS_PROMPTS_DIRwhen set, otherwise an auto-detectedprompts/directory (found by walking up forprompts/catalog.yaml).Open memory manager
Opens SQLite or Postgres based on
HARNESS_MEMORY_MODEandHARNESS_MEMORY_DB_DRIVER.Open SQLite stores
Opens checkpoint, workflow, and working-memory stores — all at
orchestrationDBPathinside the workspace.Load workflow and network definitions
Reads YAML files from
HARNESS_WORKFLOWS_DIRandHARNESS_NETWORKS_DIRif set.Boot skills system
Loads and registers
SKILL.mdfiles from the global and workspace skills directories.Boot cron
Starts the embedded SQLite scheduler (default) or connects to an external service if
HARNESS_CRON_URLis set.Boot MCP client manager
Registers MCP servers from TOML config layers (TOML takes precedence), then from
HARNESS_MCP_SERVERSenv var (skipping duplicate names).Boot persistence stores
Opens the run store, conversation store, and relay worker store — only when
HARNESS_RUN_DB,HARNESS_CONVERSATION_DB, andHARNESS_RELAY_DBare set respectively.Wire brokers and activations
Sets up the ask-user broker, tool-approval broker, and activation registry.
Build RunnerConfig and harness.Runner
Assembles the core run execution engine with all wired dependencies.
Start hot-reload watcher
When
HARNESS_WATCH_ENABLED=true(default), starts a poll-based watcher on the global and workspace skills/workflows directories.Build HTTP runtime
Initializes the
workflows.Engine,networks.Engine,subagents.Manager, and script workflow engine.Mount handlers
Mounts
mainHandlerat/and the MCP HTTP server at/mcp— both on the same TCP port.Start http.Server
Calls
ListenAndServein a goroutine.Log ready
Prints
harness server listening on <addr>to stdout.Block on signal
Waits for
SIGINTorSIGTERM, 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:
- Fake — if
HARNESS_PROVIDER=fake, usefakeprovider.New(turns). No API key needed. - Catalog — if the model catalog is loaded and the configured default model (
HARNESS_MODEL, defaultgpt-4.1-mini) resolves to a provider that has its API key set, use that catalog client. Supported catalog providers includeopenai,anthropic,deepseek,groq,xai,kimi,qwen,together,openrouter, andgemini. - OpenAI legacy — if
OPENAI_API_KEYis set, bootstrap OpenAI directly. - Error — if none of the above applies,
harnessdexits 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
| Variable | Default | Description |
|---|---|---|
HARNESS_ADDR | :8080 | HTTP listen address in socket form. |
HARNESS_WORKSPACE | . | Workspace root — anchors all relative paths. |
HARNESS_MODEL | gpt-4.1-mini | Default LLM model for runs. |
HARNESS_MAX_STEPS | 8 | Max tool-calling steps per run. Set to 0 to remove the cap. |
HARNESS_PROVIDER | — | Set to fake for key-free mode. |
HARNESS_FAKE_TURNS | — | Path to the JSON turns file when HARNESS_PROVIDER=fake. Required when using fake mode. |
HARNESS_AUTH_DISABLED | — | Set 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.
| Variable | Description |
|---|---|
HARNESS_RUN_DB | SQLite path for run and event records. Required for GET /v1/runs (list). |
HARNESS_CONVERSATION_DB | SQLite path for conversation persistence. |
HARNESS_RELAY_DB | SQLite 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:
| Provider | Env var |
|---|---|
| OpenAI | OPENAI_API_KEY |
| Anthropic | ANTHROPIC_API_KEY |
| DeepSeek | DEEPSEEK_API_KEY |
| Groq | GROQ_API_KEY |
| xAI | XAI_API_KEY |
| Kimi / Moonshot | MOONSHOT_API_KEY |
| Qwen / DashScope | DASHSCOPE_API_KEY |
| Together AI | TOGETHER_API_KEY |
| OpenRouter | OPENROUTER_API_KEY |
| Google Gemini | GOOGLE_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:
--mcp-workspaceflagHARNESS_WORKSPACEenv var.(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
harnessdfrom Claude Desktop. - Key-Free Testing — More depth on the fake provider and the
TestRunSmokein-process smoke test.