Raxx · internal docs

internal · gated

SOC-2 Quarterly Attestation Runbook

Owner: operator (compliance program owner) System scope: Raptor audit system (customer_audit_events, HMAC chain, archiver) Card: #1496 Phase: Audit Phase 3 — usable after SC-A11 + SC-A13 land Last reviewed: 2026-05-17 UTC Related: - SC-A11 — HMAC integrity check job (jobs/audit_integrity_check.py) - SC-A13 — archiver manifest (audit_archival_runs) - RV-13 — compliance role + raxx-compliance-auditors group - SC-A15 — departing-employee deprovisioning runbook


1. Scope

This runbook covers the SOC-2 controls attested to by Raxx's audit system in the pre-customer phase. Controls outside this scope (vendor management, change management, incident response) are out of scope until a formal SOC-2 engagement begins.

Control AICPA ref What Raxx attests
Logical access — least privilege CC6.1 raptor-audit-compliance role assigned only to raxx-compliance-auditors group; no direct user grants
Logical access — access review CC6.2 raxx-compliance-auditors group membership reviewed quarterly; provisioned empty per OQ-3 decision
Logical access — termination CC6.3 Departing-employee deprovisioning runbook (SC-A15) executed within 24 h of notice
Change management — audit trail integrity CC8.1 HMAC-SHA-256 + AWS KMS chain verified monthly; no gaps in audit_integrity_log
Data retention A1.2 Archiver manifest confirms retention-policy exports within 90 days; 7-year cold storage SLA in effect
Monitoring — log completeness CC7.2 pg_audit external sink shows DML events for last 90 days with no unexplained gaps
Access change control CC6.7 rbac_grants_audit shows no out-of-window grants to raptor-audit-compliance or raptor-audit-admin

2. Quarterly schedule

Attestation must be completed within ±5 days of each quarter-end. Target window opens on the last business day of the quarter and must close no later than five calendar days after the UTC dates below.

Quarter Target date (UTC) Open window Hard close
Q1 2026-01-01 10:00 UTC 2025-12-27 2026-01-06
Q2 2026-04-01 10:00 UTC 2026-03-27 2026-04-06
Q3 2026-07-01 10:00 UTC 2026-06-26 2026-07-06
Q4 2026-10-01 10:00 UTC 2026-09-26 2026-10-06

The operator attestation commit (Step 8) must be timestamped within the hard close window. The commit timestamp is the evidence of timely completion.


3. Per-control evidence checklist

Step 1 — Compliance role assignment (CC6.1 / RV-13)

Confirm raptor-audit-compliance is assigned only to the raxx-compliance-auditors group and carries no direct user assignments.

Where to collect:

Run from a Postgres session authenticated as the owner credential (migration credential from Heroku; use heroku pg:psql --app raxx-api-prod):

-- Show all group members for raxx-compliance-auditors
SELECT u.email, gm.granted_at, gm.granted_by
FROM rbac_group_members gm
JOIN users u ON u.id = gm.user_id
JOIN rbac_groups g ON g.id = gm.group_id
WHERE g.name = 'raxx-compliance-auditors';

-- Confirm no direct user grants for raptor-audit-compliance
SELECT u.email, rg.granted_at, rg.granted_by
FROM rbac_grants rg
JOIN users u ON u.id = rg.user_id
JOIN rbac_roles r ON r.id = rg.role_id
WHERE r.name = 'raptor-audit-compliance'
  AND rg.group_id IS NULL;

Expected result: Group query returns zero rows (group is provisioned empty per OQ-3 decision until an external auditor is engaged). Direct-grant query returns zero rows.

Format: Screenshot of psql output, or copy-paste into evidence file. Storage: Private Google Drive — Raxx / Compliance / YYYY-QN / CC6.1-role-assignment.png


Step 2 — Auditor group membership review (CC6.2 / RV-13)

Confirm raxx-compliance-auditors contains only authorized auditors (or is empty). Cross-reference against the most recent authorization memo in docs/ops/attestation-log/.

Where to collect: Same psql query as Step 1 (group member query). If the group is non-empty, verify each email against the current authorization memo.

Format: Screenshot or text export. Storage: Private Google Drive — Raxx / Compliance / YYYY-QN / CC6.2-group-membership.txt


Step 3 — HMAC chain monthly verification (CC8.1 / SC-A11)

Run the monthly chain verification and confirm clean output:

# On the Raptor dyno or a one-off Heroku run:
heroku run --app raxx-api-prod \
  python jobs/audit_integrity_check.py --mode monthly

Confirm the job exits 0 and writes a PASS record to audit_integrity_log with no chain_error or kms_error fields set.

Verify in Postgres:

SELECT run_id, mode, started_at, completed_at, result, rows_verified, errors
FROM audit_integrity_log
WHERE mode = 'monthly'
ORDER BY started_at DESC
LIMIT 3;

Expected result: Most recent row shows result = 'pass', errors = 0, completed_at within the current quarter window.

Format: Terminal output screenshot + CSV export of the query above. Storage: Private Google Drive — Raxx / Compliance / YYYY-QN / CC8.1-hmac-chain-monthly.png


Step 4 — Archiver manifest completeness (A1.2 / SC-A13)

Confirm the archiver manifest in audit_archival_runs shows all retention-policy exports completed within the last 90 days.

SELECT run_id, started_at, completed_at, rows_archived, destination,
       retention_policy_label, status
FROM audit_archival_runs
WHERE started_at > NOW() - INTERVAL '90 days'
ORDER BY started_at DESC;

Expected result: At least one completed run per calendar month in the window. All rows show status = 'complete'. No status = 'failed' rows.

Format: CSV export (\copy to local file from psql). Storage: Private Google Drive — Raxx / Compliance / YYYY-QN / A1.2-archiver-manifest.csv


Step 5 — 7-year retention bounds (A1.2 / SC-A13)

Verify retention policy bounds:

-- Oldest unarchived row in hot table (must be < 7 years old)
SELECT MIN(occurred_at) AS oldest_hot_row
FROM customer_audit_events;

-- Most recent S3 Glacier archive job (cold storage)
SELECT destination, rows_archived, completed_at
FROM audit_archival_runs
WHERE destination LIKE 's3://raxx-audit-glacier%'
ORDER BY completed_at DESC
LIMIT 1;

Confirm: oldest_hot_row is within the 7-year policy window. Glacier archive job completed within the last 90 days.

Format: Screenshot of psql output. Storage: Private Google Drive — Raxx / Compliance / YYYY-QN / A1.2-retention-bounds.png


Step 6 — pg_audit external log sink (CC7.2)

Confirm DML events are present in the external pg_audit sink for the last 90 days with no unexplained gaps.

Log drain is configured on raxx-api-prod and ships to the centralized log sink. Check via Heroku log drain or the configured APM (Sentry) log intake:

# Pull a sample of recent pg_audit lines from Heroku log drain (last 24 h)
heroku drains --app raxx-api-prod
# Confirm drain is active, then pull from your log aggregator for
# DML class events on customer_audit_events for the past 90 days.

Expected result: DML events present for every day in the 90-day window. Any gap exceeding 24 h that is not accounted for by a documented maintenance window is a finding — file a severity:high area:security issue before signing the attestation.

Format: Exported log sample (CSV or text), date-range annotated. Storage: Private Google Drive — Raxx / Compliance / YYYY-QN / CC7.2-pgaudit-sample.txt


Step 7 — Out-of-window RBAC grant review (CC6.7)

Confirm no raptor-audit-compliance or raptor-audit-admin grants occurred outside of documented change windows in the last quarter.

SELECT u.email, r.name AS role, rg.granted_at, rg.granted_by,
       rg.change_window_id
FROM rbac_grants_audit rg
JOIN users u ON u.id = rg.user_id
JOIN rbac_roles r ON r.id = rg.role_id
WHERE r.name IN ('raptor-audit-compliance', 'raptor-audit-admin')
  AND rg.granted_at > NOW() - INTERVAL '90 days';

Cross-reference any rows against the change management log (docs/ops/runbooks/deploy-freeze.md). Any grant with a null or unrecognized change_window_id is a finding — do not sign the attestation until resolved.

Format: CSV export of the query. Storage: Private Google Drive — Raxx / Compliance / YYYY-QN / CC6.7-rbac-grants-audit.csv


Step 8 — Operator attestation sign-off

Create a signed attestation commit in this repository:

  1. Copy docs/ops/attestation-log/YYYY-QN.md template (see 2026-Q2.md).
  2. Fill in the date, quarter, and pass/fail result for each of the seven steps.
  3. Note any open findings with linked GitHub issue numbers.
  4. Commit to main (via PR if a review is desired; direct commit is acceptable for operator-only sign-off):
git add docs/ops/attestation-log/YYYY-QN.md
git commit -m "ops(compliance): Q<N> YYYY SOC-2 attestation sign-off"
git push origin main

The commit SHA and UTC timestamp serve as the dated, signed statement of operator confirmation. The commit must fall within the hard close window defined in §2.

Storage: Git history is the authoritative record. A copy of the file is also stored in Private Google Drive — Raxx / Compliance / YYYY-QN / attestation.md


4. Operator action checklist (sequential)

Run these in order. Do not skip a step if the previous step produced a finding — document the finding and decide whether to proceed with a qualified attestation or delay sign-off.


5. Document retention

Evidence type Retention period Notes
Attestation log file (YYYY-QN.md) Minimum 7 years Retained in git history + Google Drive
RBAC grant audit export 7 years Supports SOC-2 Type II period-of-coverage requirement
HMAC chain verification log 7 years Same as above
Archiver manifest export 7 years Cold storage SLA evidence
pg_audit log samples 7 years Log drain retention may be shorter; operator must archive externally
Role + group membership screenshots 1 year minimum; 7 years recommended Extend to 7 years once SOC-2 Type II engagement begins

All evidence stored in Google Drive must be in the private Raxx / Compliance / folder with access restricted to operator + engaged auditors only.


6. Future automation candidates

These steps are manual in Phase 1. Post-launch automation targets:

Step Candidate automation Notes
Step 1–2 Script to query RBAC tables and emit pass/fail JSON Add to scripts/compliance/
Step 3 Nightly HMAC job already runs automatically; add quarterly summary email SC-A11 extension
Step 4–5 Archiver already produces audit_archival_runs rows; add a --report flag to emit a formatted evidence file SC-A13 extension
Step 6 Add pg_audit gap-detection query to the nightly integrity job SC-A11 extension
Step 7 Add a scheduled query that flags out-of-window grants and fires to #raxx-ops-alert-sev2 Queue RBAC service extension
Step 8 Auto-draft attestation file from script output; operator reviews and commits Phase 2 automation card

Reference documents

docs/architecture/customer-audit-unified/design.md
docs/architecture/customer-audit-unified/api-contract.md
docs/ops/runbooks/raptor-postgres-roles.md
docs/ops/attestation-log/2026-Q2.md

Privacy policy (evidence of user-facing data commitments):

docs/architecture/privacy-policy-v1.md