Coreal.
Book a working session →
← RESOURCES·COMPLIANCEv1.1

Three lines of defense, written down as configuration.

How the 1L/2L/3L model becomes diffable, reviewable artefacts in the same git history as the code — including who can change what, who has to approve, and how the regulator reads it.

AUTHOR
I. Kovalchuk
Head of Regulatory · Coreal
READING TIME
17 min
UPDATED
2026-04-15
WRITTEN FOR
Risk officer, head of compliance, internal audit lead

The three-lines model is older than fintech. The discipline is well-known. The implementation, almost universally, is a slide deck. Three boxes, three teams, three reporting lines, an arrow pointing up to the board. The slide deck is correct. The system that the slide deck describes does not exist anywhere visible.

Independence has to be testable

The three lines are only meaningful if they are independent — different people, different reporting, different authority over different artefacts. A regulator does not take "we are independent" as an answer. They look at what 1L can change without 2L review, what 2L can change without 1L knowing, and whether 3L has read access to both without the ability to write.

In Coreal, independence is wired into the IAM and the repo. Each line has its own GitHub team, its own CODEOWNERS scope, its own approval policy on its config files. A 1L engineer cannot merge a change to 2L thresholds. A 2L compliance officer cannot edit the BPM workflows that 1L owns. A 3L auditor has read access to both, and write access to neither.

The artefacts each line owns

LineArtefactLives inApprover
1LBPM workflow definitionsconfig/bpm/1L tech lead + product
1LVelocity / exposure capsconfig/policy/velocity.yaml1L tech lead
1LIdempotency contractconfig/gateway/idempotency.yaml1L tech lead
2LSanctions list refreshconfig/screening/sanctions.lock2L head of compliance
2LKYT thresholdsconfig/screening/kyt-thresholds.yaml2L head of compliance
2LKYC tier policyconfig/policy/tiers.yaml2L + product VP (joint)
2LAML rule curationconfig/aml/rules/*.rule2L senior analyst
3LAudit findingsaudit/findings/*.md3L head of audit
3LRemediation trackingaudit/remediation.yaml3L head of audit + named 1L/2L owner

What a 2L change looks like

Compliance reviews EU sanctions list updates within 24 hours of publication. The change is a commit to `config/screening/sanctions.lock` (a hash-pinned reference to the source). The PR is opened by an analyst, approved by the head of compliance, and lands. CI re-validates the hash, regenerates the in-memory bloom filter, and the BPM workflows pick up the new list at the next refresh window.

A 1L engineer cannot touch this file — CODEOWNERS forbids it, and the CI permission check on merge will reject. A 1L engineer can read the file, see the hash, see what changed, when, and by whom. They have full visibility, no edit rights. That is what independence looks like in source control.

PR template for a 2L policy change

  • What is changing (file paths and one-line summary)
  • Why (regulatory reference, internal incident reference, or scheduled review)
  • Lawful basis if PII is involved (GDPR Art. 6 sub-clause)
  • Customer-facing impact (yes/no, with description)
  • 1L impact (does any 1L workflow need to change to remain consistent?)
  • 3L sign-off (required for material changes; logged in audit register)
  • Rollback plan (how to revert and the maximum time window safely allowed)

What 3L sees, and what they don't

Internal audit reads the journal. They do not read the dashboard. The dashboard is a derived view; the journal is the source. If an audit finding turns on what was decided, who decided it, on what input, in what time — the journal answers all four. The dashboard might lie (latency in derivation, caching, stale views); the journal does not.

3L can pull a sample of cases from any period in the last 7 years, walk the journal hop by hop, and verify that 2L policy was applied as written. If they find a deviation, the finding lands as a markdown file in `audit/findings/` with a named 1L or 2L owner and an SLA tied to severity. The remediation lands as a follow-up PR.

The point of three lines is not the slide deck. The point is that the regulator can pull a case, walk the journal, and verify that the policy in force at that time was the policy actually applied — not the policy claimed in marketing.

The case for diffability

Putting 2L policy in git is not a stylistic choice. It is the only way to give the regulator a meaningful answer to the question "what was your sanctions list refresh policy on March 12?" The git history shows the file at that commit. The file shows the source. The CI logs show when it was applied. The journal shows which transactions ran against it. Together, four artefacts answer the question completely. Without git, the same question takes weeks of email archaeology.

Where this design fails

The model assumes 2L policy can be expressed as configuration. Most of it can. Some cannot — qualitative judgement on a marginal case, cultural sensitivity in a jurisdiction where the rules are unwritten, escalation paths in a multi-jurisdiction incident. For those, the journal carries the human reasoning as a written analyst note attached to the case. The note is part of the audit trail. It is not configuration; it is testimony.

OTHER DEEP DIVES
← All deep divesReserve a 4-hour working session →