Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/joseluis-dev/harness-ai/llms.txt

Use this file to discover all available pages before exploring further.

This guide walks you through cloning the repository, configuring your environment, and running the full local stack smoke test. By the end you will have four healthy Docker services running locally, a completed execution record in the database, and a full audit event trail you can inspect via the BFF API. The steps below reflect the Phase 0 acceptance criteria — everything here is tested and verified against the live stack.
1

Prerequisites

Ensure the following tools are installed and meet the minimum versions before proceeding.
ToolMinimum versionNotes
Docker24+Required for multi-stage builds and Compose v2
Docker Composev2Bundled with Docker Desktop; verify with docker compose version
Node.js20+Required by harness-core (Astro SSR)
Python3.12+Required by runtime-python (FastAPI + RQ)
pnpm9+Resolved by Corepack — do not install globally
The pnpm version is not installed globally. It is resolved by Corepack from the packageManager field in the root package.json. You activate Corepack once per host; CI, Docker builds, and local development all inherit the same pinned toolchain automatically.
2

Clone and configure the environment

Clone the repository and copy the environment template:
git clone <repo-url> harness-ai
cd harness-ai
cp .env.example .env
Open .env and review the required variables before starting the stack. The core Phase 0 variables are:
# BFF bind address and port (published to the host as :4321)
SERVER_HOST=0.0.0.0
SERVER_PORT=4321

# Internal Docker DNS alias — do not change unless you rename the service
SERVER_RUNTIME_BASE_URL=http://runtime-python:8000

# Runtime Python (FastAPI + in-process RQ worker) bind address
API_HOST=0.0.0.0
API_PORT=8000

# Name of the RQ queue consumed by the in-process worker
API_EXECUTION_QUEUE=executions

# PostgreSQL connection for harness_db (business + audit data)
HARNESS_DATABASE_URL=postgresql+asyncpg://harness:harness@postgres:5432/harness_db

# Redis used as the RQ broker and operational cache
REDIS_URL=redis://redis:6379/0

# Logging (info | debug | warning | error — comma-separated levels)
LOG_LEVEL=info
# Set to "1" to pretty-print JSON logs in development terminals
LOG_PRETTY=1
Never commit a real .env file. The .env.example template contains only safe placeholder values. In production, inject secrets through Dokploy’s secrets manager or your organization’s vault — never hardcode credentials in the repository.
The file also contains # PHASE 0.b and # PHASE 1 / # PHASE 2 sections with variables for mcp-auth-service, mcp-identity-connector, and model-gateway. Leave these at their defaults for a Phase 0 local run; they are either inert seams or already set to safe fallback values.
3

Enable Corepack and install JavaScript dependencies

Activate Corepack once for the host, then install workspace dependencies using the pinned pnpm version:
corepack enable
pnpm install
Corepack reads the packageManager field in package.json (pnpm@10.33.0) and ensures every invocation — locally, in CI, and inside Docker — uses exactly that version. The pnpm install command materialises node_modules for all workspace packages under apps/.
4

Start the local stack

Bring up all services in detached mode. Use either the convenience script defined in package.json or the full Docker Compose command directly:
# Via the package.json script (recommended for local dev)
pnpm run compose:local:up
# Or directly with Docker Compose (equivalent)
docker compose -f infra/compose/docker-compose.local.yml up -d
Docker starts all services in dependency order, pulling images for postgres and redis and using previously built images for harness-core and runtime-python. Add --build to the direct docker compose invocation if you want to force a rebuild from the current source.
runtime-python runs both the FastAPI server (uvicorn) and the RQ worker (rq worker executions) inside the same container, supervised by infra/docker/entrypoint.runtime-python.sh. There is no separate worker compose service in Phase 0 — splitting the worker is a future Phase 0.b / Phase 5 change.
5

Verify stack health

Confirm all four services reached a healthy state:
docker compose -f infra/compose/docker-compose.local.yml ps
Expected output for a healthy Phase 0 stack:
NAME                   IMAGE                          COMMAND                  SERVICE            CREATED          STATUS                    PORTS
harness-core           harness-ai-harness-core        "/bin/sh -c 'node ..."   harness-core       10 seconds ago   Up 9 seconds              0.0.0.0:4321->4321/tcp
harness-postgres       postgres:16-alpine             "docker-entrypoint..."   postgres           10 seconds ago   Up 9 seconds (healthy)
harness-redis          redis:7-alpine                 "docker-entrypoint..."   redis              10 seconds ago   Up 9 seconds (healthy)
runtime-python         harness-ai-runtime-python      "/bin/bash /app/in..."   runtime-python     10 seconds ago   Up 9 seconds (healthy)
Only harness-core publishes a host port (0.0.0.0:4321->4321/tcp). runtime-python is internal-only and exposes port 8000 on the private Docker network only — never on the host. Do not attempt to curl localhost:8000 directly; health must be observed through the BFF or via Docker’s own healthcheck status.
6

Check BFF health

Verify that the BFF is up and that it can reach runtime-python on the private network:
curl http://localhost:4321/api/healthz
A fully healthy response:
{
  "status": "ok",
  "runtime": "ok",
  "service": "harness-core",
  "version": "0.1.0"
}
If the runtime is still starting, you may see a degraded response:
{
  "status": "degraded",
  "runtime": "error",
  "service": "harness-core",
  "version": "0.1.0",
  "error": "..."
}
Wait a few seconds and retry. The runtime-python healthcheck has a start_period of 15 seconds and retries every 5 seconds up to 10 times. Note that the response does not include a postgres field — PostgreSQL health is observed via docker compose ps, not through the BFF.
7

Create your first execution

Submit a manual execution through the BFF API. The constraints object is part of every execution and controls the policy gate: allow_cloud: false ensures no data leaves the local network, and require_audit: true guarantees that audit events are written.
curl -X POST http://localhost:4321/api/executions \
  -H 'Content-Type: application/json' \
  -d '{
    "source_type": "manual",
    "intent": "smoke-test",
    "payload": {},
    "constraints": {
      "allow_cloud": false,
      "require_audit": true,
      "risk_level": "low"
    }
  }'
The BFF returns 201 Created with the new execution record:
{
  "id": "5b47cba7-...",
  "status": "queued"
}
Save the id value — you will use it in the next two steps.
8

Poll for completion

The in-process RQ worker inside runtime-python picks up the job from the Redis queue and transitions the execution through queued → running → completed. Poll until the status reaches completed:
curl http://localhost:4321/api/executions/{id}
Replace {id} with the UUID returned in the previous step. The response will show the current status:
{
  "id": "5b47cba7-...",
  "status": "completed"
}
Under normal conditions the transition completes within a few seconds. You can loop the call if you prefer:
while true; do
  STATUS=$(curl -fsS "http://localhost:4321/api/executions/{id}" | jq -r .status)
  echo "status=$STATUS"
  [ "$STATUS" = "completed" ] && break
  sleep 1
done
9

Inspect audit events

Retrieve the full event trail for the execution. The audit spec guarantees two events for every completed execution:
curl http://localhost:4321/api/executions/{id}/events
Expected response:
[
  {
    "event_type": "execution.running",
    "from_status": "queued",
    "to_status": "running"
  },
  {
    "event_type": "execution.completed",
    "from_status": "running",
    "to_status": "completed"
  }
]
These events are written by the runtime’s audit/event_builder.py — the runtime is the sole writer of audit_events. The BFF proxies the response without modification.
The executions list and detail pages are also available in the browser at http://localhost:4321/executions and http://localhost:4321/executions/{id}. The pages SSR-fetch from the BFF and do not query the database directly.
The host-port boundary is a hard Phase 0 rule: only harness-core publishes a port to the host (:4321). runtime-python is reachable exclusively on the private Docker network (harness-net). Never bind :8000 to the host or add any other service to the public network — this is a deliberate architectural constraint enforced at the compose level.
Ready to deploy to production? Harness AI uses Dokploy as its deployment target. The production compose file lives at infra/compose/docker-compose.prod.yml and follows the same four-service topology with TLS terminated at the Dokploy reverse proxy. See Production Deployment with Dokploy for the full bring-up sequence, secrets management, and rollback procedure.

Build docs developers (and LLMs) love