Compliance coverage matrix
Every regulatory and security control implemented in the platform, grouped by legal envelope. Each row lists the citation, the module(s) that carry the control, the audit review doc that specifies it, and the code + test paths that enforce it. Both the agent-mediated and ZFA-direct channels are covered on every row unless explicitly noted.
Legend:
- Statute column cites the primary source (Ugandan law, IRA/URA regulation, or industry standard).
- Module is the InsureLink module that owns enforcement.
- Review links to the audit doc that specifies scope, gaps, and acceptance criteria.
- Locked in by points at the tests / invariants that would fail if the control regressed.
Uganda Data Protection & Privacy Act 2019 (+ Regulations 2021)
| DPPA cite | Control | Module | Review | Locked in by |
|---|---|---|---|---|
| §8 lawful, fair, transparent processing | Every processing activity has a documented purpose in the DPIA folder; PII reads emit an audit event. | Notifications, Audit | Notifications review · Audit / SIEM review | audit-egress-snapshot.spec, pii-permission-scope.spec |
| §11 storage-limitation | Documents retention cascade physically unlinks blobs when the retention window closes; sanctions/fraud rows purged on schedule; warehouse exports carry a hard TTL. | Documents, Sanctions, Bordereau | Documents review · Sanctions/Fraud review · Bordereau review | document-storage.spec (retention path), sanctions-fraud retention job spec |
| §13(1) prior consent | Referral consent captured at referral creation (moved from client_accepted to create). Public inquiry captures consent as first field. Portal captures consent on first login. | Referrals, Client Portal, Client Inquiries | Referrals review · Client Portal review | data-transfer-consent.spec, referral consent invariant |
| §13(3) purpose-scoped consent | NotificationPreferences is per-category; the DPPA §16 cross-border kind (sms_kenya, whatsapp_us, sanctions_offshore, email_smtp) is a separate DataTransferConsent row per subject. | Notification Preferences, Notifications Dispatch | Notifications review | data-transfer-consent.spec, notification-preferences.spec |
| §13 consent-withdrawal path | Portal DSAR objection endpoint; unsubscribe token on every notification body; withdrawal downgrades DataTransferConsent.withdrawnAt. | Client Portal, Notifications, DSAR | Client Portal review · DSAR review | unsubscribe-token.spec, dsar-request-lifecycle.spec |
| §16 cross-border transfer | Africa's Talking (KE), WhatsApp Cloud (US), SMTP (varies), sanctions provider (UK/US) all gated by a per-subject DataTransferConsent row of the matching kind. Absent consent → dispatch dropped, audit event notification.blocked.no_transfer_consent. DPIA per provider in /docs/compliance/dpia/. | Notifications, Sanctions | Notifications review · Sanctions review | data-transfer-consent.spec (block path), webhook-signature-parity.spec |
| §17 data-subject rights (access, rectify, erase, object, restrict, port) | DsarRequest entity with 30-day SLA tracker, subject-portal + DPO-verified submission surfaces, structured export pack, tombstone erasure with dsarErasedAt stamp. | DSAR, Client Portal | DSAR review | dsar-request-lifecycle.spec, sar-scope.spec |
| §21 security safeguards | PII fields ciphered (PiiCipher) with HMAC search hash; documents AES-256-GCM at rest; app-layer encryption on webhook bodies; MFA gate on privileged roles; refresh-token reuse detection. | Auth, Documents, Notifications, Common Security | Auth review · Documents review | pii-cipher.spec, secrets-cipher.spec, document-storage.spec |
| §21 accountability — PII read visibility | Every bulk-PII decrypt requires a permission from the whitelisted set and emits an audit.pii_bulk_read event. Presigned-URL fetches emit document.download with subject id + IP. | Common Security, Documents | Documents review | pii-permission-scope.spec, document-storage.spec (download audit) |
| §33 72-h breach notification | ComplianceBreach entity, automated 72-h countdown, IRA/UPDPO notify-subjects endpoint, escalation event when SLA breached. | Compliance Breaches | Complaints / SARs / Breaches review | complaints-sars-breaches spec (72-h timer) |
| §33(1) records of processing | DPIA folder maintained per third-party processor (Africa's Talking, WhatsApp, SMTP, sanctions). | Notifications | Notifications review | Living docs at /docs/compliance/dpia/ |
Uganda Anti-Money Laundering Act 2013 (as amended 2017)
| AML cite | Control | Module | Review | Locked in by |
|---|---|---|---|---|
| §3 customer due diligence | Agent KYC required-doc policy per agent-type; client KYC field-completeness gate before payout. | Agent KYC, KYC/Identity | KYC/AML review | agent-kyc verify path, id-patterns.spec (NIN/TIN) |
| §6 pre-transaction sanctions screening | Every payout / commission / policy-issue calls SanctionsProviderRegistry. Refuses to boot in prod on console fallback. Circuit-breaker on the external provider. Inbound-collection payer screen shipped in Collections Phase 1 (see Collections review gap 1). | Sanctions, Payments, Collections | Sanctions/Fraud review · Collections review | prod-startup-gate.spec, sanctions integration spec |
| §12–14 record retention | 7-year retention on transaction + KYC records; document retention job enforces the floor. | Documents, Audit | Documents review · Audit review | Document retention job spec |
| §17 STR (suspicious-transaction reporting) | RegulatorRequest entity + POST /reports/regulator/fia/sweep + GET /reports/regulator/fia/goaml.xml for FIA goAML XML export. Tipping-off banner in compliance UI. | Reports, Compliance | Complaints / SARs / Breaches review | FIA XML export snapshot |
| FIA CTR (Cash Threshold Reporting) ≥ UGX 20M | FiaThresholdService.sweep() walks payment + claimPayment above FIA_CTR_THRESHOLD_UGX; goAML XML export at GET /reports/regulator/fia/goaml.xml. Inbound collection CTR coverage shipped in Collections Phase 1 (see Collections review gap 4). | Regulator Reports, Collections | Collections review · Complaints review | fia-threshold sweep spec |
Uganda Insurance Act 2017 & IRA regulations
| Statute cite | Control | Module | Review | Locked in by |
|---|---|---|---|---|
| §60–75 policy lifecycle | 8-state policy machine (issued → active → endorsed → cancelled → reinstated → renewed), IRA-schema policy schedule PDF, product-approval reference on every quote. | Policies | Policies review | policies state-transition tests |
| §88–95 claims handling | Claims workflow with acknowledgement SLA, reserve booking, IRA-compliant decision-reason enum, disbursement audit trail. | Claims | Claims review | Claims state-transition spec, claim.disburse permission gate |
| §102 IRA quarterly return | POST /reports/regulator/ira/quarterly builds + snapshots the quarterly return, GET .../:id/xml emits the IRA-schema XML. | Reports | Bordereau/Warehouse review | Regulator report snapshot spec |
| §112 commission caps | Commission preview endpoint validates against per-product / per-insurer cap; over-cap triggers commission.cap.exceeded audit event and requires super-admin override. | Commissions | Commissions/Wallets review | commission-calculator.spec |
| §128 monthly bordereau | Bordereau warehouse export runs on schedule, produces the IRA-schema file, records checksum + retention timestamp. | Bordereau | Bordereau review | Bordereau export + retention spec |
| IRA IT Controls Guidance — segregation of duties | Claim pay requires a different actor than claim approve; withdrawal approve cannot be the same actor as the requester. | Claims, Wallets | Auth review · Commissions review | endpoint-coverage.spec, policy.guard.spec |
| IRA market-conduct — cooling-off | Consumer Protection §37 cooling-off period exposed on policy schedule + client portal; cancellation inside the window bypasses cancellation-fee. | Policies, Client Portal | Policies review | Policy cancel path spec |
Uganda tax law (URA)
| Cite | Control | Module | Review | Locked in by |
|---|---|---|---|---|
| Income Tax Act §80 — 6% WHT on commissions | URA_WHT deduction row (versioned, beneficiaryTypes=["agent","zfa_house"]) applies to every commission approval. IRP5-equivalent export at GET /reports/regulator/ura/irp5/:agentId/:year. | Commissions | Commissions review | deduction-stacking.spec (WHT invariant), IRP5 export snapshot |
| URA monthly remittance by 15th | POST /reports/regulator/ura/remittance/compute computes the amount; POST .../:id/submitted records submission. | Reports | Commissions review | Regulator remittance report spec |
| 7-year record retention (tax) | Documents retention floor honours the max of AML §14 and URA §41 → effectively 7 years. | Documents | Documents review | Document retention job spec |
Uganda payments & telecoms
| Cite | Control | Module | Review | Locked in by |
|---|---|---|---|---|
| National Payment Systems Act 2020 (payouts) | Payout-provider registry gates prod-boot on real-provider config; webhook HMAC verified per-provider; idempotency key on every dispatch. | Payment Providers | Payment gateway review | providers.spec, webhook-verifier.spec, idempotency.service.spec |
| National Payment Systems Act 2020 (collections) | Four inbound rails (MTN MoMo, Airtel, Flutterwave, Pesapal, direct bank). Per-provider webhook signature strategies (HMAC-timestamp, Airtel body-hash, Pesapal GetTransactionStatus follow-up). Idempotent webhook ingest via (provider, eventId) unique. Prod-boot gate on collection providers shipped in Collections Phase 1; initiate-side idempotency + rate-limit remain Phase 2 (Collections review gaps 3, 6, 7). | Collections, Payment Providers | Collections review | webhook-signature-parity.spec, provider-webhook-sig.spec |
| BOU cybersecurity guidelines — MFA on privileged accounts | MFA required for ZFA staff / super-admin / finance; hardware-passkey enrolment path; recovery-code hashing. | Auth | Auth review | auth-security-runbook procedures; policy-guard spec |
| BOU cybersecurity — 90-day log retention floor | Audit chain + SIEM cursor retention >= 90 days; expired sweep on schedule. | Audit / SIEM | Audit / SIEM review | audit-chain.spec, audit-egress-snapshot.spec |
| Uganda Communications Act 2013 §5 + UCC Guidelines | Notification unsubscribe token on every SMS/email/WhatsApp; opt-out honoured on next dispatch attempt. | Notifications | Notifications review | unsubscribe-token.spec |
| Computer Misuse Act 2011 | Every state-changing endpoint hits audit.record(); hash-chained AuditEvent; regulator-scope filter prevents cross-tenant peek. | Audit | Audit review | audit-chain.spec, regulator-scope.spec |
Uganda Consumer Protection Act 2011
| Cite | Control | Module | Review | Locked in by |
|---|---|---|---|---|
| §8–13 unfair practices — clear pricing | Deductions preview endpoint returns net-of-deductions gross so agents / clients see the actual take-home before commitment. | Commissions, Client Portal | Commissions review · Client Portal review | deduction-stacking.spec (preview parity) |
| §8–13 unfair practices — unsubscribe | Every SMS/email/WhatsApp carries an HMAC-signed unsubscribe token; single click opts out at category granularity. | Notifications | Notifications review | unsubscribe-token.spec |
| §37–39 cooling-off | Policy schedule exposes cooling-off period, portal cancellation endpoint honours the window fee-free. | Policies, Client Portal | Policies review | Policy cancel spec |
| Complaints handling | 3-state complaint machine (opened → acknowledged → resolved), SLA timer, IRA §102 escalation event. | Complaints | Complaints review | Complaints SLA spec |
OWASP Top 10 & ASVS L2
| OWASP cite | Control | Module | Review | Locked in by |
|---|---|---|---|---|
| A01 broken access control | Object-authorization service scopes every referral / policy / claim / wallet fetch by (a) agent, (b) sponsor, (c) ZFA-direct handler, (d) portal client, (e) insurer. | Common Policies | Auth review · Referrals review | object-authorization.spec, endpoint-coverage.spec |
| A02 cryptographic failures | PII/documents/secrets each keyed separately (PII_HASH_KEY, SECRETS_KEY, DOCUMENT_KEY), AES-256-GCM, HMAC-SHA256 for search hashes, Argon2id for passwords. | Common Security, Documents | Auth review · Documents review | pii-cipher.spec, secrets-cipher.spec, document-storage.spec |
| A03 injection | Every DB call is parametric via Prisma; DTO validation with class-validator; no raw string concatenation into queries. | (platform-wide) | Auth review | Repository-wide grep audit; DTO validation tests per controller |
| A05 security misconfiguration | prod-startup-gate refuses boot on console fallback for sanctions + notification providers; boot fails loudly rather than silently degrading. | Sanctions, Notifications | Sanctions review · Notifications review | prod-startup-gate.spec |
| A07 identification & authentication failures | Refresh-token reuse detection, session revocation across devices, MFA on privileged accounts, passkey enrolment, magic-link + OTP for portal. | Auth, Client Portal | Auth review · Client Portal review | auth refresh-rotate spec, MFA spec |
| A08 software / data integrity | Webhook HMAC signature verification uses timing-safe comparison; audit chain uses SHA-256 forward-hash. Bordereau + Sanctions webhook parity spec locks the pattern. | Common Security, Audit, Bordereau | Audit review | webhook-signature-parity.spec, audit-chain.spec |
| A09 logging & monitoring failures | Every state change → audit.record(); every audit → Track-3 structured log line → SIEM push (with circuit-breaker on collector). Metrics + tracing on every job. | Audit, SIEM, Observability | Audit review | audit-egress-snapshot.spec, siem-export.spec, metrics-timing.spec |
| A10 SSRF | SIEM export host allowlist (SIEM_TARGET_HOSTS), sanctions provider URL allowlist, webhook URL egress denylist. | SIEM, Sanctions | Audit review · Sanctions review | siem-export.spec (host-check invariant) |
Cross-cutting invariants
Three spec files enforce that the whole platform respects patterns established in the module audits. If ANY module regresses one of these, CI turns red:
| Invariant | Enforced by | What it locks in |
|---|---|---|
| PII permission whitelist | pii-permission-scope.spec | Only the enumerated permissions may return bulk PII from a controller. Adding a permission to the whitelist requires a corresponding legitimate-use entry — accidental widening is caught in review. |
| Webhook signature parity | webhook-signature-parity.spec | Every inbound HMAC verification uses timing-safe compare, the correct header, and rejects on malformed / stale timestamps. Bordereau, Sanctions, and Payment webhooks all pass the same shape. |
| Prod startup gate parity | prod-startup-gate.spec | Sanctions + Notifications + Warehouse each refuse to boot in production when their real-provider config is missing, unless a matching escape-hatch env var (SANCTIONS_ALLOW_CONSOLE_IN_PROD, NOTIFICATION_ALLOW_CONSOLE_IN_PROD, WAREHOUSE_ALLOW_...) is set. Prevents silent degradation to console-fallback in prod. |
Channel parity
Every control above applies on both the agent-mediated and ZFA-direct channels. Verifications you can run to prove parity:
| Control area | Agent-mediated path | ZFA-direct path | Same code path? |
|---|---|---|---|
| Referral state machine | POST /referrals | POST /admin/zfa-direct/inquiries/:id/convert-to-referral | Yes — one ReferralsService.transition() |
| POP verify | Agent uploads | Portal / staff uploads via /client-portal/referrals/:id/pop | Yes — one PopService.verify() |
| Consent | Agent-collected at referral create | Client-signed at inquiry / portal login | Same DataTransferConsentService |
| DSAR | Subject via DPO or portal | Subject via portal | Same DsarRequestService |
| Sanctions screen | Pre-payout | Pre-payout | Same SanctionsProviderRegistry |
| Commission WHT | Applied on agent wallet credit | Applied on house wallet credit | Same deductions engine, different beneficiaryTypes |
The only intentional differences are:
- Commission payee: agent wallet vs house wallet — enforced by
beneficiaryTypeson each deduction row. - Consent capture surface: agent-facing DTO vs public / portal form — same table, different UI.
- Object-authorization scope: agent scopes by
agentId; ZFA-direct scopes byzfaDirectHandlerId; portal client scopes byclientId.
Where to go next
- Business flow: how the two channels interact end-to-end
- DPIAs: per-processor data-transfer records
- Design docs: the 15 module audits — one per module, each with a full gap-analysis and per-phase implementation record
- Operational runbooks: incident response, key rotation, onboarding