Deployment¶
HarnessWeaver is a library and a spec, not a service you must run. Most apps deploy it in process; apps in other languages add the harnessd sidecar; apps with no SDK at all get controls through gateway configs. All three load the same spec and the same org baseline.
The short version:
- Run models behind a gateway (LiteLLM Proxy) that holds the provider keys and owns retries.
- Keep the org baseline in one versioned place every app extends, and lock what must not change.
- Give each environment an overlay (
harness.prod.yaml), selected byHARNESSWEAVER_ENV. - Use a durable state store when runs wait for approval.
- Send traces to an OpenTelemetry collector, and keep content capture off.
- Gate every change in CI:
spec validate, tests,eval run,policy test, andspec diffin the pull request.
| Page | Covers |
|---|---|
| Versioning | How contracts, packages and profiles are versioned, and how to upgrade safely |
| Configuration | Environment variables, overlays per environment, files the harness reads and writes |
| Gateways and the dev stack | LiteLLM and MCP gateway configs generated from the spec, and a local stack |
| Security | The threat model and its controls, the invariants an operator can rely on, and hardening |
Install¶
HarnessWeaver is a set of packages: a small Python core, a CLI, a sidecar, plugins, and a TypeScript core with an AI SDK adapter. Install the core and only the plugins your spec uses. Installing a plugin enables nothing on its own: only the names your harness.yaml refers to are imported (RULES S8).
| For | You need |
|---|---|
| Python apps, the CLI and the sidecar | Python 3.12 or later, and uv |
| TypeScript apps | Node 22 or later |
The local dev stack (harnessweaver dev --up) and the sidecar image |
Docker with Compose |
uv add harnessweaver # the core: pydantic, opentelemetry-api, pyyaml, anyio, loguru
uv add "harnessweaver[openai]" # plus one first-party plugin
uv add --dev harnessweaver-cli # the harnessweaver command
uv add "harnessweaver[all]" # every first-party plugin, the CLI and the sidecar
| Package | Extra | What it adds |
|---|---|---|
harnessweaver |
- | The core: spec loader, hook bus, envelope and budgets, runners, the built-in modules, the test kit. import harnessweaver as hw |
harnessweaver-cli |
cli |
The harnessweaver command (CLI) |
harnessweaver-sidecar |
sidecar |
harnessd, the HTTP sidecar (harnessd) |
harnessweaver-openai |
openai |
openai.chat, for any OpenAI-compatible endpoint, and instrument() for OpenAI clients |
harnessweaver-anthropic |
anthropic |
anthropic.messages for Claude, and instrument() for Anthropic clients |
harnessweaver-pydantic-ai |
pydantic-ai |
The pydantic_ai.agent runner and wrap() for Pydantic AI agents |
harnessweaver-monty |
monty |
monty.sandbox, which runs code-executing tools away from the host process |
harnessweaver-otel |
otel |
otel.sdk, which sets up the OpenTelemetry SDK and an exporter |
harnessweaver-cel |
cel |
cel.policy, full CEL for policy rules |
An extra only installs the package. Your spec still decides what is used, and an org baseline can restrict which plugins apps may use (plugins.allow).
For TypeScript:
npm install @harnessweaver/core # the TypeScript core
npm install @harnessweaver/ai-sdk ai @ai-sdk/provider # plus wrapModel for the Vercel AI SDK (v7)
From a clone, pnpm -C typescript install && pnpm -C typescript build builds both packages; Example 04 uses them from source. The sidecar image builds with docker build -f deploy/harnessd/Dockerfile -t harnessd . (harnessd).
Deployment shapes¶
flowchart LR
subgraph app1[Python app]
a1[app code] --> h1[harnessweaver]
end
subgraph app2[TypeScript app]
a2[app code] --> h2["@harnessweaver/core"]
end
subgraph pod[Go / Java app + sidecar]
a3[app code] -- "HTTP, 127.0.0.1" --> d3[harnessd]
end
gw[LiteLLM Proxy<br/>provider keys, retries,<br/>tenant budgets]
col[OTel collector] --> obs[Langfuse, Phoenix,<br/>your backend]
st[(State store)]
h1 --> gw
h2 --> gw
d3 --> gw
h1 --> col
d3 --> col
h1 --> st
d3 --> st
base[("org baseline<br/>(profile path or package)")] -.-> h1
base -.-> h2
base -.-> d3
In process (Python, TypeScript)¶
The app depends on harnessweaver (or @harnessweaver/core) and the plugins its spec uses, and ships its harness.yaml, overlays, prompts, manifests and eval sets with its code.
- Models: point
modelsat a gateway (openai.chatwithbase_url), or at a provider directly (anthropic.messages). Keys come from the environment, never the spec (RULES S1). - Org baseline: put the org's profiles on
HARNESSWEAVER_PROFILE_PATH(a mounted directory, a shared volume), or install them as a plugin package and extendprofile://<namespace>.<name>@^N. - State:
memoryis fine for stateless services; usesqlite(or a durable plugin) when runs wait for approval or you rely on idempotency across restarts. - Telemetry: let the app's own OpenTelemetry setup collect HarnessWeaver's spans, or set
observability.configure_sdk: truewithharnessweaver-oteland an OTLP endpoint.
Sidecar (any language)¶
Run harnessd next to the app (a sidecar container in the same pod, or a process on the same host), serving the app's spec:
docker build -f deploy/harnessd/Dockerfile -t harnessd .
docker run -p 127.0.0.1:7070:7070 -e HARNESSD_TOKEN -e HARNESSWEAVER_ENV=prod -v /srv/app:/app:ro harnessd
- Keep it local: harnessd binds 127.0.0.1 by default; inside a container it listens on the container's interfaces, so publish the port on 127.0.0.1 or keep it inside the pod (S6).
- Give it a token from your secret store; it refuses to start without one.
- The image includes the sidecar and the
openai,anthropic,otelandcelplugins; build with--build-arg PACKAGES="..."for others, including your own plugins. - Give it the same state store choice and telemetry settings as an in-process app.
See harnessd.
Gateway only¶
Apps that call models and tools over the network with no SDK get the controls a gateway can enforce: model aliases, retries, timeouts, no message logging, and a tool allow-list from the manifests. Generate the configs in CI from the spec (harnessweaver gateway litellm, gateway mcp) and deploy them with the gateway; tools that need a run (approvals, egress, tenants, rate limits) are withheld and must go through the harness or harnessd. See Gateways.
The services around it¶
| Service | Role | Notes |
|---|---|---|
| LiteLLM Proxy | Model egress: provider keys, retries, fallbacks, tenant budgets, cost reporting | One owner for retries (RULES R5): SDK retries stay off behind it. Its x-litellm-response-cost header gives the harness exact costs |
| OpenTelemetry collector | Receives spans and metrics | Forward to Langfuse, Phoenix or your own backend |
| State store | Runs, checkpoints, idempotency, events, recordings, samples | sqlite today; Postgres and Redis planned |
| MCP gateway | Shared tools over MCP | Configure it from harnessweaver gateway mcp |
| Approval channel endpoint | Receives approval requests | A webhook, your own callback, or polling memory |
harnessweaver dev --up runs LiteLLM, a collector, Langfuse and harnessd locally, wired to a spec (The local dev stack).
CI¶
uv run harnessweaver spec validate harness.yaml -e prod --profile-dir org-profiles
uv run harnessweaver doctor harness.yaml -e prod --strict
uv run harnessweaver test
uv run harnessweaver eval run harness.yaml -e offline
uv run harnessweaver policy test harness.yaml
uv run harnessweaver spec diff git:origin/main:harness.yaml harness.yaml > spec-diff.md # post as a PR comment
Validate every overlay you deploy (-e prod, -e staging): an overlay can break a lock or bound that the base spec respects.
Production checklist¶
- The spec extends the org baseline at a pinned range (
org://acme-baseline@^1), anddoctorreports no unpinned profiles. -
spec validate -e <env>passes for every deployed environment. - Models go through a gateway, with SDK retries off and keys only in the gateway's environment.
-
observability.capture_contentis false (and locked by the baseline). - Every tool has a pinned manifest;
doctorreports no unpinned or mismatched digests. - Every task has a budget with a deadline, within the baseline's bounds.
- Runs that can wait for approval use a durable state store and a real approval channel.
- New guards started in shadow mode and were promoted on eval evidence.
-
eval runandpolicy testgate the build, with committed baselines. - harnessd, if used, binds locally and takes its token from a secret store.
- Logs are enabled where you want them (
logger.enable("harnessweaver")), going to a sink you chose.