Audit CI Gates
Status: Live — SC-A17 gate active as of #1497 Owner: devops / security Date: 2026-05-17 UTC
Overview
The audit CI gates enforce that every audit event emitted by the application is documented and registered before it reaches production. This is the CI companion to SC-A3's runtime allowlist enforcement.
SC-A17 — Audit action namespace allowlist gate
Purpose
Every action= string literal passed to write_audit() or
write_audit_strict() in application code must appear in the registered
allowlist before the PR can merge. This prevents undocumented audit events
from silently entering the audit log where they would break the HMAC chain
integrity (SC-A11) or violate the SOC-2 auditability invariants.
Scope
The gate scans the following source trees on every PR that touches them:
backend_v2/console/velvet/reasonator/
Files matching test_*.py and the argparse entry-point files (cli.py,
conftest.py) are excluded.
Allowlist config file
docs/architecture/audit/action-allowlist.txt
This is the same file consulted by SC-A3's runtime writer for field-level
allowlists. The CI gate reads only the action name lines; the runtime uses
the full per-action before_state/after_state field sets defined separately
in the writer service.
Format: one action string per non-comment line. Lines beginning with # are
comments. Blank lines are ignored. Action strings use the convention:
domain.noun.verb
Examples:
console.flag.flip
session.revoke
vault.rotation.completed
What the gate checks
scripts/ci/check_audit_action_allowlist.py is run by the CI job
audit-action-allowlist in .github/workflows/ci-pr.yml.
- Scans all
.pysource files under the configured directories. - Finds every occurrence of
action="<value>"oraction='<value>'that is NOT on a pure-comment line. - Looks up each
<value>indocs/architecture/audit/action-allowlist.txt. - Fails the pipeline with an error message naming the unregistered string and the exact file + line number.
The gate passes if all found action strings are registered.
Registering a new action
- Add the action string to
docs/architecture/audit/action-allowlist.txtunder the appropriate category comment. - Document the action's purpose, actor class (
dimension), and permittedbefore_state/after_statefields in the writer service's schema documentation (ask the security-agent or open a design card if uncertain). - Open a PR with both the source change and the allowlist entry. The CI gate on that PR will then pass.
- Get sign-off from the code reviewer that the action string is appropriate for the audit dimension and field allowlist.
Escape hatch
In rare cases where a line must use a dynamic or transitional action string that cannot yet be registered (e.g., a migration helper that is removed in the same PR), add the escape-hatch comment to the same source line:
write_audit(action="temp.migration.helper") # audit-action-ok
Requirements for using the escape hatch:
- The line must have
# audit-action-okas a comment on the same line as theaction=call. - The code reviewer must explicitly sign off in their review comment.
- A follow-up card must be filed to either register the action or remove the call within one sprint.
The escape hatch is visible in the CI output:
NOTE: 1 action string(s) suppressed via # audit-action-ok:
console/app/services/migration_helper.py:42 action='temp.migration.helper' [suppressed]
The --strict flag on the script treats suppressed lines as failures (used in
full audits, not normal PR CI).
CI job name
audit-action-allowlist — required status check in branch protection for
main. The job is path-filtered: it only runs when a PR touches files in
backend_v2/**, console/**, velvet/**, or reasonator/**.
Future gates (planned)
| Gate | Status | Card |
|---|---|---|
| SC-A17 (this gate) | Live | #1497 |
| iOS companion app action namespace lint | Post-launch | Deferred |