Skip to main content

Evaluator and executor

The service that scores an agent cannot release a hold on it. That is not a code review convention. It is two services, two database roles, and a test that tries the forbidden write and asserts failure.

Two services

ServiceWhat it doesDatabase roleKey scope it accepts
vigil-apiScores events, evaluates the gate, appends event ledger records, reads review statevigil_apievents:write, read, admin, platform
vigil-gateLists holds; releases, rejects or escalates them; appends review ledger recordsvigil_gatereview (or admin)

vigil-api has no route that writes reviewed_by, reviewed_at, review_outcome or review_note. A CI test walks its openapi.json and fails on any non-GET method under /enforcement or /holds.

vigil-gate runs as its own deployment with its own database URL. It never receives the API key, the API's database URL, or any model key.

Two database roles, proven at the database

RoleMayMay not
vigil_apiINSERT events, scores, actions, ledger records; UPDATE agents, keys, policiesUPDATE enforcement_actions; DELETE anything
vigil_gateUPDATE enforcement_actions and ledger_head; INSERT ledger_records and webhook_deliveriesINSERT behavioral_events; write any other table
vigil_roSELECTWrite anything

The grants live in an Alembic migration. A CI test connects as each role: vigil_api UPDATE on enforcement_actions is denied, vigil_gate UPDATE succeeds, vigil_gate INSERT into behavioral_events is denied, vigil_ro cannot write. The denial was reproduced against production before the database cutover. This is decision rule 8 in the repo.

What the agents can reach

A governed agent holds a key scoped to events:write and usually read. It can post events and read scores. It cannot create keys, change thresholds, reach the gate, or mark itself authorized in the registries. VIGIL's own agents (the Investigator) get read tools and one propose tool; a CI test fails on any tool named release, reject, escalate, patch, delete, update or insert.

The review contract

GET /holds scope review pending actions in your org
POST /holds/{action_id}/release scope review body { "reviewer": "JAS", "note": "..." }
POST /holds/{action_id}/reject
POST /holds/{action_id}/escalate

reviewer is required. A decision is terminal: the second call on the same action is 409. Every decision appends a review record under the org's ledger_head lock and queues a review.<outcome> webhook. The gate's base URL is its own domain, published in the repo's operations guide; the console reaches it with a separate review key.

See the separation from the outside

The API exposes only reads of review state. This call lists what is waiting for a human:

curl -sf -H "Authorization: Bearer $VIGIL_KEY" "$VIGIL_URL/enforcement/pending"

And this one, sent to the API instead of the gate, must fail with 404 or 405 because the route does not exist there:

code=$(curl -s -o /dev/null -w "%{http_code}" -X POST -H "Authorization: Bearer $VIGIL_KEY" "$VIGIL_URL/holds/1/release")
test "$code" = "404" -o "$code" = "405" && echo "api has no release route ($code)"