Skip to content

Setup

This page runs the whole system from a clone — proxy, harness, dashboard, fixtures. If you only want to use Gettier on your own repository, you want the quickstart instead: it needs neither Docker nor a server.

Prerequisites

  • Node 20+
  • pnpm 10
  • Docker (for the fixtures and the Postgres-backed test suite)
  • At least one upstream key: ANTHROPIC_API_KEY and/or OPENAI_API_KEY

Install and test

pnpm install
pnpm --filter @gettier/core test
pnpm --filter @gettier/core typecheck

The two customer surfaces

Neither needs a Gettier server, and neither talks to the proxy.

pnpm --filter gettier build                      # Gettier for Agents (the CLI)
node packages/cli/dist/cli.js --help

pnpm --filter @gettier/node build                # Gettier for Runtime (the SDK)

Full references: Gettier for Agents and Gettier for Runtime. Everything below is the rest of the system.

Run the GTR-001 fixture

cd fixtures/gtr-001
docker compose up -d --build

The fixture is a real docker-compose environment (the nginx / body-parser trap), not a mock — the seeded fault is exactly what the harness is meant to expose.

Start the proxy

ANTHROPIC_API_KEY=sk-ant-… \
GETTIER_API_KEY=replace-with-a-long-random-value \
GETTIER_REGISTRY=fixtures/gtr-001/sensors.local.yaml \
GETTIER_SENSOR_CWD=fixtures/gtr-001 \
pnpm --filter @gettier/proxy start

The proxy listens on http://localhost:7070 and serves POST /v1/messages (Anthropic and OpenAI wire formats). Adoption is a single base_url change in your client or coding agent.

Add the header x-gettier-consequential: 1 to force the full state machine on a turn, then watch the response headers:

Clients authenticate with Authorization: Bearer <GETTIER_API_KEY> (OpenAI format) or x-api-key: <GETTIER_API_KEY> (Anthropic format). The credential determines the tenant.

  • x-gettier-gate: held — a sensor could have settled a load-bearing belief and did not; the turn was blocked. A claim no sensor covers is a coverage gap, not a hold (v0.3).
  • x-gettier-gate: released — a sensor verdict backed at least one load-bearing claim.
  • x-gettier-gate: released_unverified — nothing held, and nothing measured either: the turn rests entirely on self-report. Not a clean bill of health.
  • x-gettier-miscalibrations: N — claims tagged with evidence the model did not have.
  • x-gettier-unverifiable: N — load-bearing claims no registered sensor covers. A sensor that covers a claim but could not run holds instead of landing here.
  • x-gettier-unjudged: N — load-bearing claims a sensor measured and no judgment rule could read. Write a sensor for unverifiable; widen a rule for this.

Models and provider routing

The proxy and the harness share one routing definition, so they can never disagree about where a model id goes. The provider is inferred from the id:

  • claude-* → Anthropic (Messages API).
  • gpt-*, o1o9, chatgpt-* → OpenAI. Within OpenAI, gpt-5.6+ and gpt-6+ route to the Responses API; everything else uses Chat Completions. (Those newer families reject function tools on Chat Completions at any reasoning effort but none — measured 2026-07-19 — and forced tool use is how the declaration is enforced, so they need the Responses surface.)
  • An unrecognised id throws rather than defaulting — a silent default would send the request to the wrong provider and surface as a confusing 404.

Keys and endpoints come from the environment (or a repo-local .env):

Variable Purpose
ANTHROPIC_API_KEY / OPENAI_API_KEY Upstream credentials. At least one is required.
GETTIER_API_KEY Client credential bound to GETTIER_TENANT. Required.
GETTIER_API_KEYS Optional JSON key-to-principal map for multi-tenant service.
ANTHROPIC_BASE_URL / OPENAI_BASE_URL Optional — point at a compatible gateway.
GETTIER_PROVIDER Optional override for non-standard ids (e.g. a gateway serving other names). The OpenAI surface (Responses vs Chat) is still inferred from the id.

Run the harness

See The measurement harness for the arms, the scoring taxonomy, and multi-run variance. The short version:

pnpm --filter @gettier/harness start -- \
  --scenario fixtures/gtr-001 \
  --models claude-opus-4-8,gpt-5.6-sol \
  --arms raw,gated --runs 1

Reports land in runs/; per-run ledger evidence under runs/ledgers/. Put your API key in a repo-local .env if you want it picked up automatically.

Open the dashboard

GETTIER_DATA_DIR=/path/to/ledger-dir GETTIER_TENANT=local \
GETTIER_DASHBOARD_TOKEN=replace-with-a-long-random-value \
pnpm --filter @gettier/web dev

Then visit http://localhost:3000.

Session signing uses GETTIER_SESSION_SECRET, falling back to GETTIER_DASHBOARD_TOKEN when no OIDC issuer is configured — which is why the single variable above is enough for local development. In production the secret is required and must be at least 32 characters, or the dashboard refuses to serve rather than signing sessions with something guessable. Set DATABASE_URL to use the hosted Postgres control plane instead of a SQLite ledger; see Hosted control plane and Dashboard identity.

Run the public landing site

The public site is isolated from the authenticated dashboard in packages/landing:

NEXT_PUBLIC_SITE_URL=https://gettier.example pnpm --filter @gettier/landing dev

It listens on http://localhost:3001 in development. Set the canonical origin before every production build so canonical, Open Graph, robots, sitemap, and JSON-LD URLs agree.

Build this docs site

pip install mkdocs-material
mkdocs serve            # http://localhost:8000
mkdocs build --strict   # -> ./site  (gitignored); --strict is what CI runs

--strict turns warnings into errors, so a broken internal link or a page missing from the nav fails the build rather than shipping. This site deploys to docs.gettier.io on Cloudflare Workers Static Assets from .github/workflows/deploy-docs.yml, on any push to main that touches docs/** or mkdocs.yml.