SOX — Sarbanes-Oxley Act (US Public Company Accounting Reform)
Scope: financial reporting controls of US publicly-traded companies. Centres on section 302 (officer certifications), section 404 (internal-control attestation), section 409 (material-change disclosure), section 802 (records retention), section 906 (criminal penalties for fraudulent certification).
The annotation is compliance: [SOX] on type, axonstore,
axonendpoint, shield, flow. SOX is almost always combined
with SOC2 and (in banking) PCI DSS.
Declaring SOX
type GeneralLedgerEntry compliance [SOX] {
period: AccountingPeriod,
account: ChartAccount,
debit: Money,
credit: Money,
counterparty: Counterparty,
approver: ApproverRef
}
axonstore GeneralLedger
compliance: [SOX, SOC2]
backend: postgresql
isolation: serializable
encryption: at_rest
retention: 7y # section 802 — 7-year retention for accounting records
on_breach: raise
axonendpoint PostJournalEntry {
flow: PostJournalFlow
method: POST
route: "/v1/ledger/journal-entries"
compliance: [SOX]
}
What the compiler enforces statically
| SOX section | AXON enforcement |
|---|---|
| section 302 — management certification | Out of scope (process). |
| section 404(a) — internal control structure | A SOX-tagged axonendpoint whose method: is POST/PUT/DELETE requires a bound mandate: (the typed approval primitive) referencing an approval authority. |
| section 404(b) — segregation of duties | A flow whose body posts a SOX-tagged ledger entry requires the mandate: approver to differ from the flow's invoking persona (statically checked: the approver identifier must not be the same as the as: persona). |
| section 409 — material change disclosure | Out of scope (process). |
| section 802 — records retention | A SOX-tagged axonstore requires retention: ≥ 7y (retention: 7y, 10y, permanent). The type checker rejects shorter retention. |
| section 906 — criminal penalty for false certification | Out of scope (process). |
What the runtime enforces
| SOX section | AXON runtime enforcement |
|---|---|
| section 404(a) — control evidence | Every mutation to a SOX-tagged store emits an audit row with (actor, role, action, target, before_hash, after_hash, approver, timestamp). |
| section 404(a) — control effectiveness | The audit chain is hash-linked + signed; any post-hoc modification breaks the chain head — the v1.19.1 FIPS-friendly hash makes tampering detectable. |
| section 802 — records retention | The runtime refuses purge on a SOX-tagged store before the declared retention has elapsed; even authorised purges emit a structured retention_break: event for review. |
| section 404(b) — SoD enforcement | A mandate: whose approver matches the requesting persona at runtime is rejected with sod_violation:; this catches the case where static identifier equality wasn't enough (e.g. role-bound principals). |
What you still attest manually
- Officer certifications (section 302, section 906) — quarterly + annual.
- Material weakness assessment — auditor judgment.
- Walkthroughs of significant controls.
- PCAOB / external audit opinion.
- Whistleblower channels (section 301) — operational.
- Change-management sign-offs around code that touches a SOX-tagged store.
Segregation of duties — the AXON model
section 404(b) demands that the person initiating a financial transaction
cannot be the same person who approves it. AXON's mandate: field
on a SOX-tagged endpoint encodes this:
mandate ManagerApproval
requires: capability("ledger.approve")
excludes_requester: true # AXON enforces requester ≠ approver
on_breach: raise
axonendpoint PostJournalEntry {
flow: PostJournalFlow
method: POST
route: "/v1/ledger/journal-entries"
compliance: [SOX]
mandate: ManagerApproval
}
The runtime resolves the requester's subject from the OIDC token,
the approver's subject from the mandate carrier, and rejects the
posting if they match — emitting an audit row tagged
sod_violation_blocked.
Common patterns
Pattern 1 — Audit-trail query (section 404 evidence pack)
flow QueryAuditForSOX(period: AccountingPeriod) -> List<AuditRow> {
step Query {
given: period
retrieve: AuditLog where tag = "SOX" AND period = period
output: List<AuditRow>
}
return Query.output
}
The result feeds the section 404 management attestation evidence pack.
Pattern 2 — Retention-aware purge
flow CleanupOldEntries(cutoff: Date) {
step Inventory {
given: cutoff
retrieve: GeneralLedger where posted_at < cutoff
output: List<RowRef>
}
for row in Inventory.output {
step CheckRetention {
given: row
ask: "Has the section 802 7-year retention elapsed for this row?"
output: Bool
}
if CheckRetention.output {
step Purge {
given: row
purge: GeneralLedger
}
}
}
}
The runtime double-checks the retention even if the flow's logic gets it wrong — defence in depth.
Pattern 3 — Quarterly close lockdown
mandate QuarterlyCloseLock
requires: capability("ledger.posting")
period_lock_after: quarter_close
on_breach: raise
axonendpoint PostJournalEntry {
flow: PostJournalFlow
method: POST
route: "/v1/ledger/journal-entries"
compliance: [SOX]
mandate: [ManagerApproval, QuarterlyCloseLock]
}
Once the quarter closes, posting requires an explicit override approval at a higher capability tier.
When NOT to use SOX
- Private companies not subject to SEC reporting. The audit trail still has value, but the SOX-specific retention + SoD constraints can be relaxed.
- Operational metrics not part of the financial reporting
scope.
compliance: [SOC2]alone is usually sufficient. - Subsidiary entities wholly inside a non-public holding company. Check with counsel.
For combined SOX + PCI scenarios (banking, fintech), declare both:
compliance: [PCI_DSS, SOX, SOC2]. Each framework's static and
runtime checks compose independently.