Why governance is a first-class surface
A migration program that cannot answer "who accepted this risk, on what basis, and until when?" is not a program — it is a backlog. Enterprise separates three concerns that are usually tangled together: policies (the rules), exceptions (documented deviations with an expiry), and the audit chain (the tamper-evident record of both).
The policy engine
Policies are stored per project, compiled, and evaluated against a submitted summary of findings and exposures. The evaluation is synchronous and stateless — the CI endpoint persists nothing — which is what makes it safe to call on every pull request.
| Endpoint | Purpose |
|---|---|
GET/POST /api/v1/projects/{projectId}/policies | List and create project policies |
GET/PUT/DELETE /api/v1/policies/{policyId} | Read, replace, and delete a policy |
GET /api/v1/templates | List built-in policy templates |
POST /api/v1/projects/{projectId}/policies/from-template | Instantiate a policy from a template |
POST /api/v1/projects/{projectId}/policies/simulate | Dry-run policies against current findings before enabling them |
POST /api/v1/projects/{projectId}/policy/gate-decision | The synchronous CI gate decision |
GET /api/v1/projects/{projectId}/policy-decisions/aggregate | Aggregate gate outcomes — honest-empty until decisions exist |
The gate decision folds every matching policy into one verdict: allow maps to pass, warn and require-review map to warn, and block maps to fail. A governance-side score-regression block forces a fail regardless of the folded verdict.
Each submitted finding and exposure carries an isNew flag computed by the same base-versus-head fingerprint diff the CLI uses, so on_new: block / on_existing: allow policies do not fail your pull request on pre-existing debt.
The exception workflow
An exception is a documented, time-bounded acceptance of a real finding. It is not a way to hide one. Exceptions are requested against a finding, reviewed by a human, and expire on a date.
| Endpoint | Purpose |
|---|---|
POST /api/v1/findings/{findingId}/exceptions | Request an exception for a finding |
GET /api/v1/exceptions/{exceptionId} | Exception detail |
GET /api/v1/projects/{projectId}/exceptions | List a project exceptions |
GET /api/v1/projects/{projectId}/exceptions/active | List currently active exceptions — what the release gate reads |
POST /api/v1/exceptions/{exceptionId}/approve | Approve |
POST /api/v1/exceptions/{exceptionId}/reject | Reject |
POST /api/v1/exceptions/{exceptionId}/extend | Extend the expiry |
POST /api/v1/exceptions/{exceptionId}/revoke | Revoke an active exception |
In the release gate, an approved and non-expired exception matching every gating exposure downgrades that block to a warn, with the exception id cited in the decision reason. Expired exceptions are themselves a warn-level gate condition, so lapsed acceptances surface rather than silently reverting to a block.
The tamper-evident audit chain
Every triage decision, exception action, and policy change is written to a hash-chained audit trail, verifiable independently of the application.
| Endpoint | Purpose |
|---|---|
GET /api/v1/audit/verify-chain | Verify the integrity of the audit hash chain |
GET /api/v1/projects/{projectId}/audit | Governance audit trail for a project |
This composes with the immutability rule from the finding contract: findings are immutable facts, and triage state lives on separate rows. You never edit a finding, you supersede it — so the trail reconstructs what was known and decided at each point in time, not just the current state.
A workable operating model
- 01
Start in simulate mode
Dry-run a candidate policy against your current findings. You will see exactly what it would have blocked before it blocks anything.
- 02
Roll out in warn mode
Run the release gate with
--mode warn. The check run concludesneutraland can never block a merge, but the verdict and its reasons appear on every pull request. - 03
Adopt a baseline
Commit
.relixq-baseline.jsonso the existing backlog is accepted and only new debt gates. - 04
Flip to block, and add the check to branch protection
Only
--mode blockcan fail a merge, and only when the check is in your required status checks. - 05
Review exceptions on a cadence
Track the "expiring within 14 days" count on the Security dashboard. An exception that keeps getting extended is a migration decision nobody has made yet.
Related pages
Release gates, PR comments and the GitHub App
Three escalating enforcement layers, a copy-paste GitHub Actions workflow, baselines, the eight-condition QAST release gate, and what the GitHub App does today.
The RelixQ Score and the three dashboards
What the 0–100 readiness score means and deliberately does not mean, the five bands, the score hero anatomy, and the Engineering, Executive and Security dashboards built around it.
QAST: Quantum Exposure Assessment
The adversary-validation layer: HNDL exposure classification via the Mosca inequality, attack-path projection, consent-gated read-only probing, remediation lifecycle, PTES/NIST reports, retest, and the PQC compatibility lab.
REST API reference
The endpoint map for the Enterprise REST API: authentication, tenancy, per-service endpoint tables, honest-empty semantics, and the internal endpoints you should never call directly.