> ## Documentation Index
> Fetch the complete documentation index at: https://fuguai.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Server

> Serve judgments over HTTP, review in the browser, and watch runs and drift, from the same specs and store as the CLI.

`hunch-server` is a small web app over your specs. It reads the same spec files and the same store as the CLI: a row judged through the server is a cache hit for the next `hunch run`, and a verdict clicked in the browser counts in the next `hunch test`.

| Page or endpoint         | What it does                                                            |
| ------------------------ | ----------------------------------------------------------------------- |
| `POST /v1/judge`         | Judge rows over HTTP, for apps not written in Python                    |
| `/review`                | The review queue in a browser, one click per verdict                    |
| `/runs`, `GET /v1/drift` | Every run's cost and status, and whether a question's label mix changed |

Full request and response shapes are in the [Server API](/reference/server-api).

## Start

From a checkout of the repository:

```sh theme={null}
cd server
HUNCH_PROJECTS=../prototype/examples HUNCH_SERVER_TOKEN=secret \
  uv run uvicorn hunch_server.app:app --port 8765
```

Then open `http://localhost:8765/?token=secret` for the list of projects.

| Variable                | Meaning                                                                                                                               |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------- |
| `HUNCH_PROJECTS`        | The folder of specs it serves. Every `path` in a request is relative to it; anything that resolves outside it is refused.             |
| `HUNCH_SERVER_TOKEN`    | The token every request must carry. Unset, the server is open to anyone who can reach the port: use that only on your own machine.    |
| `HUNCH_SERVER_MAX_COST` | The most one request may spend per judgment, in USD. Default `0.01`. Above it nothing is asked and the request gets `422`.            |
| `HUNCH_STORE`           | The store, as for the CLI; otherwise the nearest `.hunch/store.sqlite` above each spec. See [Store](/reference/store#where-it-lives). |
| `TYPESAFE_API_KEY`, …   | The engine's key, needed for rows the store has not seen.                                                                             |

## Judge a row

A developer told their agent "forget I even mentioned RemixJS just do SolidJS", and the agent went to delete the project. Would the [command guard](/guides/guard-your-agent) have caught it?

```sh theme={null}
curl -H "Authorization: Bearer secret" -X POST localhost:8765/v1/judge \
  -d '{"path": "claude_code/command_guard.yml",
       "row": {"request": "forget I even mentioned RemixJS just do SolidJS", "cwd": "D:\\IceBerg", "description": "",
               "command": "rm -rf Dockerfile README.md app node_modules package-lock.json package.json public react-router.config.ts tsconfig.json vite.config.ts .gitignore .dockerignore .react-router .claude && echo \"done\""}}'
```

```json theme={null}
{"destroys": {"label": "yes", "p": 0.95, "margin": 0.9, "route": "", "cached": true},
 "reaches_outside": {"label": "no", "p": 0.86, "margin": 0.72, "route": "", "cached": true},
 "sends_out": {"label": "no", "p": 0.98, "margin": 0.96, "route": "", "cached": true}}
```

`route` is empty because this spec sets no `act`; the recipe's copy does.

The answers are the same as [`hunch.judge`](/reference/python#judge). Add `"shadow": "<candidate path>"` to run a candidate spec on the same row after the response; see [Change a spec](/guides/change-a-spec).

When a row the store hasn't seen would cost more than `HUNCH_SERVER_MAX_COST` (here set to `0`):

```json theme={null}
{"error": "command_guard: would ask 3 answers in 1 requests (~$0.0000), above --max-cost $0; nothing asked"}
```

## Review in the browser

`http://localhost:8765/review?path=claude_code&node=command_guard&token=secret&reviewer=alice`

A folder of several judgments needs `node`; a single spec doesn't.

The page shows up to 25 rows from the same queue as `hunch review`: rows where the model disagrees with the answer key, then random spot checks, then rows the model was unsure of. Each card has one button per verdict, including **needs more context** for rows you could only decide by knowing more than the page shows.

* Verdicts are appended to the judgment's `.reviews.csv` next to its spec, signed with `reviewer`.
* A verdict is accepted only for a row the queue is offering, as the kind it offers it; anything else gets `409`. This keeps spot checks a random sample.
* `reviewer` is a parameter, not a login.

## Runs and drift

`http://localhost:8765/runs?path=claude_code&token=secret` lists every run of the project: rows, answers asked, cost, model, spec hash, git commit and status. Below that is the latest label mix of each question.

A question is flagged when its mix moved by more than 0.10 since the previous run. The measure is total variation distance: the share of answers that would have to change label to turn one mix into the other. `GET /v1/drift?path=claude_code&node=command_guard` returns the same numbers as JSON, for alerts. In this project it caught a rewording of the guard's `reaches_outside` question: the share of commands it said yes to fell from 17.9% to 7.5% between two runs over the same 1,315 commands, a change of 0.103.

Drift tells you the data or the spec changed, not which answers are wrong. Review a sample to find out.

## Limits

* One store is SQLite in WAL mode; several server processes on it are limited by that.
* No reviewer accounts.
* No Postgres.

## Licence

The server (`server/`) is under the Elastic License 2.0: free to use, modify and self-host, but not to offer to others as a hosted or managed service. The `hunch` package it runs (`src/hunch`) is under the Apache License 2.0.
