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.

Harness Core is the only service on the public network. It is an Astro SSR application that acts as the institutional Backend-for-Frontend (BFF): it serves the administrative portal, exposes the internal API consumed by the UI, resolves actor identity on every inbound request, and forwards validated work to the Runtime Python engine over the private Docker network. Nothing else in the harness is publicly reachable. Every human interaction — through the web portal, the chat interface, the approvals screen, or any external webhook — must pass through Harness Core before reaching any downstream service.

Role and Responsibilities

Harness Core serves a strictly bounded role: it governs, routes, and records — it never executes heavy work itself.

Portal & UI

Serves the administrative portal, chat interface, execution viewer, audit screen, approvals queue, and document registry at their respective routes.

API Façade

Exposes internal REST endpoints consumed by the portal UI and external webhooks. Validates every request body with Zod before any downstream call.

Actor Resolution

Calls mcp-identity-connector on every request to resolve the bearer to an ActorContext. Degrades gracefully to a system actor when the connector is unavailable.

Audit Delegation

Delegates all execution creation and audit event writing to Runtime Python. The BFF never opens a direct Postgres connection.

Actor Identity Resolution

Every request that reaches Harness Core flows through the Astro SSR middleware (src/middleware.ts) before hitting any route handler. The middleware is responsible for populating Astro.locals.actor with a resolved ActorContext.
1

Extract the Bearer

The middleware reads the Authorization: Bearer <token> header. A missing, malformed, or oversized (>512 chars) header causes an immediate degradation to actor=system with a warn-once log entry — the middleware never throws.
2

Check Connector URL

If MCP_IDENTITY_CONNECTOR_URL is empty (Phase 0 four-service acceptance mode), the middleware degrades to actor=system and emits a warn-once. No HTTP call is made.
3

Call mcp-identity-connector

The middleware calls POST {connector_url}/validate_session with { bearer } as the request body, enforcing a hard ceiling of ≤ 3 s via AbortController. A timeout, network error, or any non-200 response all degrade to actor=system.
4

Populate locals.actor

A successful 200 response is parsed and validated against the ActorContext shape. Invalid fields (wrong types, oversized values) also degrade to actor=system. On success, Astro.locals.actor holds the fully resolved identity.
The middleware never peeks at the request body — that responsibility belongs exclusively to the route handler that owns the Zod schema.
// src/middleware.ts — core resolution logic
export const onRequest = defineMiddleware(async (context, next) => {
  if (context.url.pathname === HEALTHZ_PATH) {
    context.locals.actor = SYSTEM_ACTOR;
    return next();
  }

  const bearer = extractBearer(context.request);
  if (bearer === null) {
    warnBearerMissingOnce();
    context.locals.actor = SYSTEM_ACTOR;
    return next();
  }

  const url = connectorUrl();
  if (url === "") {
    warnConnectorDisabledOnce();
    context.locals.actor = SYSTEM_ACTOR;
    return next();
  }

  const timeoutMs = connectorTimeoutMs();
  const actor = await callValidateSession(url, bearer, timeoutMs);
  if (actor === null) {
    warnConnectorErrorOnce();
    context.locals.actor = SYSTEM_ACTOR;
    return next();
  }

  context.locals.actor = actor;
  return next();
});

The ActorContext Shape

// src/lib/auth/index.ts
export interface ActorContext {
  actor_type: ActorType;   // "system" | "user" | "service"
  actor_id: string | null; // mirrors user_id for actor_type="user"
  user_id: string | null;
  department: string | null;
  roles: string[];
}

export const SYSTEM_ACTOR: ActorContext = {
  actor_type: "system",
  actor_id: null,
  user_id: null,
  department: null,
  roles: [],
};

Execution Creation Flow

POST /api/executions is the most security-sensitive endpoint on the BFF surface. It enforces a strict ordering that prevents any invalid or unauthenticated execution from reaching the runtime.
1

JSON Parse

The raw body is parsed as JSON. A malformed body returns 400 invalid_json immediately — Zod is never invoked on garbage input.
2

Zod Validation

The parsed body is validated against the ExecutionCreateBodySchema. A schema failure returns 400 invalid_payload with structured issues. The runtime is never called on rejection.
const ExecutionCreateBodySchema = z.object({
  source_type: z.string().min(1).max(128),
  intent:      z.string().min(1).max(128),
  payload:     z.record(z.string(), z.unknown()).default({}),
  constraints: z.object({
    allow_cloud:   z.boolean().optional(),
    require_audit: z.boolean().optional(),
    risk_level:    z.enum(["low", "medium", "high", "critical"]).optional(),
  }).strict().optional(),
}).strict();
3

Actor Resolution

getActor({ locals }) reads Astro.locals.actor populated by the middleware. If the middleware did not run, this falls back to SYSTEM_ACTOR with a warn-once.
4

Fail-Closed Gate (AD-5)

Before touching the runtime, the BFF evaluates isSensitiveRequest. If the request is sensitive and the actor is degraded (actor_type === "system"), the call is blocked with 401 identity_unavailable.
5

Build Actor Forward Headers

buildActorForwardHeaders(actor) produces the X-Harness-Actor-Id and X-Harness-Actor-Department header pair, or null for a system actor. Oversized values throw ActorFieldTooLongError, returning 400 actor_id_too_long before the runtime is called.
6

Forward to Runtime

The validated body and actor headers are forwarded via fetch() to POST /internal/executions on the runtime container. The BFF proxies the runtime’s response verbatim — 201 { id, status: "queued" } on success.
// src/pages/api/executions/index.ts — POST handler (abridged)
const actor = await getActor({ locals });

if (isSensitiveRequest({ request, url, body }) && actor.actor_type === "system") {
  return json(401, {
    error: "identity_unavailable",
    message: "Sensitive execution requests require a resolved identity. …",
  });
}

let actorHeaders: ReturnType<typeof buildActorForwardHeaders>;
try {
  actorHeaders = buildActorForwardHeaders(actor);
} catch (err: unknown) {
  if (err instanceof ActorFieldTooLongError) {
    return json(400, {
      error: err.code,
      message: `Actor field ${err.field} exceeds the 128-char limit.`,
      field: err.field,
    });
  }
  throw err;
}

upstreamRes = await fetch(`${runtimeBaseUrl()}/internal/executions`, {
  method: "POST",
  headers: { "Content-Type": "application/json", ...(actorHeaders ?? {}) },
  body: JSON.stringify(body),
});

The isSensitiveRequest Predicate

isSensitiveRequest is the single source of truth for the fail-closed contract. It returns true only when all three of the following hold simultaneously:
ConditionValue
HTTP methodPOST
Pathnamestarts with /api/executions
constraints.risk_level"high" or "critical"
Any other combination — GET reads, low/medium risk writes, or an absent constraints block — is treated as non-sensitive and degrades gracefully.
// src/lib/auth/index.ts
export function isSensitiveRequest(ctx: SensitiveRequestContext): boolean {
  if (ctx.request.method !== "POST") return false;
  if (!ctx.url.pathname.startsWith("/api/executions")) return false;

  const body = ctx.body;
  if (body === null || body === undefined) return false;
  if (typeof body !== "object") return false;

  const constraints = (body as { constraints?: unknown }).constraints;
  if (constraints === null || constraints === undefined) return false;
  if (typeof constraints !== "object") return false;

  const riskLevel = (constraints as { risk_level?: unknown }).risk_level;
  return riskLevel === "high" || riskLevel === "critical";
}

ActorFieldTooLongError and the 128-Character Ceiling

The BFF enforces a 128-character ceiling on both X-Harness-Actor-Id and X-Harness-Actor-Department before forwarding to the runtime. This mirrors the VARCHAR(128) columns on the executions table. Values are trimmed before the check; oversized trimmed values throw ActorFieldTooLongError, which the route handler maps to 400 actor_id_too_long.
// src/lib/auth/index.ts
export const ACTOR_FIELD_MAX_LENGTH = 128;

export class ActorFieldTooLongError extends Error {
  public readonly code: "actor_id_too_long";
  public readonly field: "user_id" | "department";

  constructor(code: "actor_id_too_long", field: "user_id" | "department") {
    super(`${field} exceeds ${ACTOR_FIELD_MAX_LENGTH} characters`);
    this.code = code;
    this.field = field;
    this.name = "ActorFieldTooLongError";
  }
}

Hard Rules

These constraints are architectural, not configurable. A PR that violates any of them is rejected at review.
Harness Core MUST NOT write to harness_db directly. It MUST NOT import rq, Redis, ioredis, asyncpg, pg, or SQLAlchemy. The only outbound network call from any execution handler is a single fetch() to the runtime container over the private Docker network.
RuleRationale
No direct Postgres writesThe runtime is the sole writer of executions and audit_events. The BFF proxying through the runtime enforces that invariant across every code path.
No Redis/RQ importsJob enqueueing is the runtime’s responsibility. The BFF only calls POST /internal/executions and receives the execution id back.
fetch() onlyKeeping outbound calls to a single fetch() makes network topology auditable and prevents silent coupling to internal services.
Fail-closed on sensitive writesA degraded identity (actor=system) must never result in a high or critical execution being accepted — even if the connector is temporarily down.

API Endpoints

For the full endpoint reference, see the BFF API Reference.

POST /api/executions

Create a new execution. Validates body, resolves actor, enforces fail-closed gate, forwards to runtime.

GET /api/executions

List recent executions with limit and offset. Non-sensitive — no actor gate applied.

GET /api/executions/:id

Fetch a single execution by UUID. Returns 404 verbatim from runtime for unknown ids.

GET /api/executions/:id/events

Fetch audit events for an execution ordered by created_at ascending. Returns 200 [] for unknown ids.

Build docs developers (and LLMs) love