1. Capabilities
| Endpoint | Method | What it gives you |
|---|---|---|
/v1/health | GET | Service liveness and STOP state (no auth) |
/v1/queue | GET | Live per-queue counts of the whole estate |
/v1/issues/{owner}/{repo}/{n}/status | GET | Where any issue stands and why |
/v1/issues/{owner}/{repo}/{n}/evidence | GET | Recorded receipts and stored SHA-256 digests |
/v1/policies | GET | The published governance policy set |
/v1/models | POST GET | Store/list tenant-scoped provider metadata and a one-way key hash; no provider call |
/v1/jev | POST | The typed advisory classifier (JEV), raw form |
/v1/jev/classify | POST | work_kind / kind_confidence / risk / impact / spec_ready |
/v1/quality | GET | Per-model output-quality aggregates from receipts |
/v1/submissions | POST | Submit a real issue into the governance flow |
/v1/asks/{owner}/{repo}/{n} | POST | Answer a governance clarification on an issue |
/, /signup, /account | GET | Landing, signup, and server-rendered tenant dashboard |
2. Authentication
Personal, scoped, revocable tokens. Send as Authorization: Bearer agk_......
- read — all GET endpoints plus the JEV endpoints (60 requests/minute)
- submit — read plus submissions, answers and model registration (5 writes/hour)
- admin — submit plus eligible tenant administration; API tokens cannot invoke signed actions
Every token belongs to one tenant. Models, quality receipts, usage, abuse records, quotas and tokens are caller-tenant scoped. Health, queue, issue status/evidence and policies expose the shared governance estate. Tokens are stored only as SHA-256 hashes. Exceeding a limit returns 429.
Operator: issuing a token
sudo -u aicoeapi python3 -c "
import sys; sys.path.insert(0, '/opt/aicoe-governance-api')
import auth
out = auth.issue('/var/lib/aicoe-governance-api', 'name@example.com', 'read')
print(out['token']) # shown ONCE; only the hash is stored
"
Operator: revoking
sudo -u aicoeapi python3 -c "
import sys; sys.path.insert(0, '/opt/aicoe-governance-api')
import auth
print(auth.revoke('/var/lib/aicoe-governance-api', 'name@example.com', tenant_id=1))
"
3. Endpoint reference
GET /v1/health — no auth
Production reads the root-owned sanitized /var/lib/aicoe-governance-export/governance-health.json snapshot. The non-root API does not traverse canonical governance state.
curl -s http://127.0.0.1:18090/v1/health
{"ok": true, "now": 1790137187.7, "stages": {}, "stopped": false}
GET /v1/queue
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:18090/v1/queue
{"queues": {"implement": 47, "needs-spec": 34, "blocked-policy": 514},
"held_for_authors": 43, "generated_at": 1790137187.8}
GET /v1/issues/{owner}/{repo}/{n}/status
The live row: queue, state, attempts, work_kind, risk, impact, spec_at (spec approved or not), the PR number if one exists, and the last outcome with its reason.
GET /v1/issues/{owner}/{repo}/{n}/evidence
Verifiable receipts only: per-attempt model identity, prompt/response SHA-256 digests, HTTP statuses, durations, outcome reasons. Nothing inferred, nothing secret.
POST /v1/models — register provider metadata submit scope
curl -s -X POST http://127.0.0.1:18090/v1/models \
-H "Authorization: Bearer $SUBMIT_TOKEN" -H "Content-Type: application/json" \
-d '{"name":"glm-5.3","provider":"zai","base_url":"https://api.z.ai/v1","api_key":"sk-your-key"}'
201 {"name": "glm-5.3", "provider": "zai", "base_url": "https://api.z.ai/v1"}
- All four fields required; a missing field returns 400 naming it.
- The api_key is stored only as a SHA-256 hash — no endpoint ever returns it, and the raw database file never contains it.
- Re-registering a name updates the connection (upsert).
POST /v1/jev/classify — JEV classification read scope
curl -s -X POST http://127.0.0.1:18090/v1/jev/classify \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"title":"API rejects valid model registrations: fields silently dropped",
"body":"## Problem\nPOST /v1/models ignores provider and base_url fields."}'
200 {"work_kind": "BACKEND", "kind_confidence": 1.0,
"risk": 2.0, "impact": "MAJOR", "spec_ready": 0.48}
The same advisory classifier the governance loop itself uses. JEV is read-only advisory — it classifies, it never gates anything.
POST /v1/jev — raw advisory ask
curl -s -X POST http://127.0.0.1:18090/v1/jev \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{"state":"ISSUE myorg/myrepo#42\nDATA: {\"title\":\"Crash on sort\"}",
"questions":{"spec_ready":{"type":"noul","instructions":"...","criteria":{"true":"...","false":"..."}}}}'
GET /v1/quality — per-model output quality
curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:18090/v1/quality
{"models": [{"model": "glm-5.3", "count": 1, "ok_count": 1,
"refusal_count": 0, "error_count": 0,
"median_latency_ms": 19.0,
"receipts_sha256": "adcf95ab..."}]}
Aggregates are recomputed from recorded receipts on every call. Registration receipts prove metadata storage only; they do not prove registered-provider execution.
POST /v1/submissions — submit a problem submit scope
curl -s -X POST http://127.0.0.1:18090/v1/submissions \
-H "Authorization: Bearer $SUBMIT_TOKEN" -H "Content-Type: application/json" \
-d '{"repo":"AI-Centre-of-Excellence/your-repo",
"title":"A real problem in the billing flow",
"sections":{"Problem":"What currently fails, concretely.",
"Expected behavior":"What should happen instead."}}'
201 {"created": true, "repo": "...", "issue": 42,
"note": "issue created for a repository present in the exported governed repository inventory; normal intake policy and future sweeps determine subsequent processing"}
Title 10–200 chars; sections require Problem and Expected behavior (optional: Scope, Acceptance criteria, Steps to reproduce, Environment). Before GitHub is called, the repository must already be present in the exported governed inventory; an absent repository is refused and no issue is created. A successful request creates an attributed issue. Subsequent intake, specification, implementation, and verification remain subject to normal policy and future sweeps; the API does not promise pickup or skip a gate.
POST /v1/asks/{owner}/{repo}/{n}
Posts your answer to a governance clarification as an attributed comment. The loop re-reads the issue on its next sweep (every few minutes).
Frontend and exact issue inspector
GET / serves the self-contained landing page. GET /signup, GET /signup/status/{id} and GET /account provide essential no-JavaScript flows. The status form submits its verification code only in the POST /signup/status/{id} body, never in a URL. The account dashboard shows the caller's tenant, token names/scopes/state, usage, quality receipts, admin-gated token controls, and an exact shared-estate issue inspector.
POST /account/inspect submits the token, owner, repo and issue in the form body and renders the existing status/evidence reader output after HTML escaping. Tokens are never accepted from account URL query strings or placed in hidden fields, JavaScript or browser storage.
Signed actions are not exposed by this HTTP service
The non-root API cannot directly and atomically inspect /root/fleet/STOP at an action append boundary. Signed-action HTTP routes are disabled, the production service loads no action roster or HMAC credential, and reusable internal modules are reserved for a future separate direct STOP-aware authority broker.
Application response security
Every response is Cache-Control: no-store and carries nosniff, frame denial, no-referrer, permissions-policy and Content-Security-Policy headers. Signup/account pages allow no script; the landing health enhancement is admitted by a response-computed SHA-256 CSP hash.
4. Hosting on your own server
Stdlib-only Python 3.11+ (developed on 3.14). The production topology is one non-socket service on 127.0.0.1:18090, two root-run exporter services and timers, and the dedicated TLS origin https://governance-api.preview.aicoe.io. The first install and every upgrade use the same transactional revision installer; never populate /opt/aicoe-governance-api or install one unit by hand.
Exact prerequisites
The target needs Linux with systemd, Python 3.11+, nginx, and a local reviewed repository containing the exact 40-lowercase-hex commit. Normal healthy deployment requires an absent /root/fleet/STOP; the explicit stopped-runtime procedure below leaves it present and uses a separate deployment sentinel. Create these inputs before the transaction:
user/group: aicoeapi (system account, nologin)
/etc/aicoe-governance-api/ root:root 0700
/etc/aicoe-governance-api/environment root:root 0600 (optional)
/etc/nginx/ssl/aicoe-api/fullchain.pem certificate for governance-api.preview.aicoe.io
/etc/nginx/ssl/aicoe-api/privkey.pem matching private key
/etc/aicoe-governance-api/environment is optional. systemd reads this root-owned file before dropping to aicoeapi; use it only for host-required GH_TOKEN, GOV_API_GH_TOKEN, or TYPESAFE_API_KEY values. Requests never supply those credentials and checks never print them. DNS for governance-api.preview.aicoe.io must target the intended edge before activation.
Signed-action HTTP routes are disabled. The production unit loads no action roster, HMAC credential, or pinned signed-actions implementation; those internal modules remain reserved for a separate direct STOP-aware authority broker.
Transactional install or upgrade
sudo /path/to/reviewed-repo/deploy/install.py --revision \
<40-lowercase-hex-commit> --repo /path/to/reviewed-repo
When governance is intentionally stopped, leave /root/fleet/STOP present and use this exact command with the dedicated deployment sentinel absent:
sudo test -e /root/fleet/STOP
sudo test ! -e /root/fleet/API_DEPLOY_STOP
sudo /path/to/reviewed-repo/deploy/install.py --revision \
<40-lowercase-hex-commit> --repo /path/to/reviewed-repo \
--expect-governance-stopped \
--stop-sentinel /root/fleet/API_DEPLOY_STOP
The stopped mode is fail-closed. Both local HTTP and loopback HTTPS health checks must return exactly HTTP 503 with parseable governance health JSON where ok is false, stopped is true, and status is either unhealthy or unknown. unknown is accepted only because an intentionally stopped runtime may also have missing or stale worker progress; stopped: true remains mandatory. A false or null stopped value, malformed body, unreachable service, or unrelated status rolls back. The separate /root/fleet/API_DEPLOY_STOP remains the mandatory transaction guard; leaving the governance runtime STOP at /root/fleet/STOP present is intentional and does not weaken deployment mutation checks.
The transaction stages only committed allow-listed bytes, verifies the manifest and Python syntax, validates all five systemd units and nginx, checks the exact credential prerequisites, snapshots the prior application and API state, migrates state in isolation, and atomically installs the release.
It installs and runs governance-health-export.service and governance-state-export.service, enables governance-health-export.timer and governance-state-export.timer, verifies the root-owned /var/lib/aicoe-governance-export/governance-health.json and /var/lib/aicoe-governance-export/loop-snapshot handoff, and starts the non-root API only after both initial exports complete. The API reads those exported snapshots and never opens canonical governance state directly.
After smoke checks, the installer performs exact installed-byte, mode, link and service-state readback. Success writes a mode-0600 report.json and terminal journal under /var/lib/aicoe-governance-deploy/transactions/. Inspect that report and the installed manifest before treating the deployment as committed.
If a post-mutation check fails, the prepared journal drives rollback of the prior application, state, units, nginx bytes and exact service states. Rollback is certified only after readback matches the pre-transaction inventory; interrupted prepared journals are reconciled by the next installer invocation.
Operational logs remain in journalctl -u aicoe-governance-api.service. Path-only nginx logging omits query strings and Referer, and application logs never include authorization headers.
5. Guarantees and non-guarantees
Guaranteed:
- No endpoint can author or merge code, or append a signed action
- Model keys and tokens are hashed at rest; newly issued token values are shown once
- Tenant-owned stores are scoped to the calling token's tenant; operator content remains operator-gated
- Every quality claim recomputed from recorded receipts, never asserted
- Failures fail closed (502/503 with a reason), never silently
Not claimed: internet-grade hardening by itself (use the proxy layer); per-tenant partitioning of the shared governance estate; JEV verdicts as execution gates; model registration as proof of provider execution.