Skip to main content

regulator-portal-service — Event Schemas

Version: 1.0 Status: Draft Owner: Regulator-facing + Legal Last Updated: 2026-04-21 Companion: DOMAIN_MODEL · SYNC_CONTRACT · SECURITY_MODEL

All events are published via the transactional outbox pattern. Every event carries schemaVersion, eventId (UUIDv4), traceId, and at (RFC 3339). Event payloads are PII-safe: no raw message body, no unmasked target MSISDN outside tightly scoped LI events (which are also marked CONFIDENTIAL-LEGAL).


1. Streams, Subjects, Retention

StreamSubjectsRetentionReplicasDedup window
REGULATOR_LIregulator.li.received.v1, regulator.li.transitioned.v1, regulator.li.delivered.v1, regulator.li.rejected.v1, regulator.li.sla.breached.v17 years (object-lock mirrored)35 min
REGULATOR_COMPLAINTSregulator.complaint.received.v1, regulator.complaint.resolved.v1, regulator.complaint.closed.v17 years35 min
REGULATOR_REPORTSregulator.report.submitted.v1, regulator.report.ready.v1, regulator.report.acked.v1, regulator.report.rejected.v17 years32 min
REGULATOR_SIEMregulator.siem.forwarded.v1, regulator.siem.destination.failed.v130 days3
REGULATOR_ATTESTATIONregulator.attestation.evidence.collected.v1, regulator.attestation.evidence.stale.v1, regulator.attestation.bundle.generated.v1Permanent3
REGULATOR_AUTHregulator.auth.login.v1, regulator.auth.failed.v1, regulator.cert.revoked.v1, regulator.auditor.access.granted.v1, regulator.auditor.access.revoked.v113 months3

All streams have a corresponding .deadletter subject. Consumers are durable with explicit ACK.


2. Produced Events

2.1 regulator.li.received.v1

Emitted when an LI request is first submitted.

interface RegulatorLiReceived {
schemaVersion: '1';
eventId: string;
liRequestId: string;
regulatorUserId: string;
orgName: string; // e.g. "ATRA"
targetMsisdnMasked: string; // +CCNNN***
scope: 'IRI' | 'CC' | 'FULL';
legalRef: string;
dateRange: { from: string; to: string };
ackBy: string;
inProgressBy: string;
deliverBy: string;
warrantObjectRef: string; // s3:// URI
warrantHashSha256: string; // hex
traceId: string;
at: string;
}

Classification: CONFIDENTIAL-LEGAL — only consumed by SIEM forwarder, admin-dashboard LI workbench, and analytics long-term archive.

2.2 regulator.li.transitioned.v1

interface RegulatorLiTransitioned {
schemaVersion: '1';
eventId: string;
liRequestId: string;
fromState: 'RECEIVED' | 'ACK' | 'IN_PROGRESS' | 'DELIVERED';
toState: 'ACK' | 'IN_PROGRESS' | 'DELIVERED' | 'CLOSED' | 'REJECTED';
initiatorUserId: string;
approverUserId: string;
rationale: string | null;
hashPrev: string; // hex
hashSelf: string; // hex
traceId: string;
at: string;
}

2.3 regulator.li.delivered.v1

interface RegulatorLiDelivered {
schemaVersion: '1';
eventId: string;
liRequestId: string;
deliveryPackageRef: string;
manifestHashSha256: string;
signatureRef: string;
sftpReceiptAt: string;
traceId: string;
at: string;
}

2.4 regulator.complaint.received.v1

interface RegulatorComplaintReceived {
schemaVersion: '1';
eventId: string;
complaintId: string;
regulatorRef: string;
orgName: string;
complaintType: 'UNSOLICITED_SMS' | 'FRAUD' | 'PHISHING' | 'BILLING_DISPUTE'
| 'DATA_PRIVACY' | 'SENDER_ID_ABUSE' | 'OTHER';
citizenMsisdnMasked: string;
receivedAt: string;
slaDueAt: string;
triageAiCategory?: string;
triageAiConfidence?: number;
traceId: string;
at: string;
}

2.5 regulator.complaint.resolved.v1

interface RegulatorComplaintResolved {
schemaVersion: '1';
eventId: string;
complaintId: string;
resolverTenantId: string | null; // null = platform-internal
resolutionSummaryHash: string; // sha256 of summary text (summary itself NOT in event)
resolvedAt: string;
traceId: string;
at: string;
}

2.6 regulator.report.submitted.v1

interface RegulatorReportSubmitted {
schemaVersion: '1';
eventId: string;
reportJobId: string;
reportType: 'DAILY_CDR_STATUS' | 'MONTHLY_COMPLIANCE_SUMMARY'
| 'QUARTERLY_PLATFORM_HEALTH' | 'AD_HOC';
filters: Record<string, unknown>;
requestedByUserId: string;
requestedByOrgName: string;
traceId: string;
at: string;
}

2.7 regulator.report.acked.v1

interface RegulatorReportAcked {
schemaVersion: '1';
eventId: string;
reportJobId: string;
ackedByUserId: string;
ackedAt: string;
traceId: string;
at: string;
}

2.8 regulator.report.rejected.v1

interface RegulatorReportRejected {
schemaVersion: '1';
eventId: string;
reportJobId: string;
rejectedByUserId: string;
reason: 'INTEGRITY_FAILURE' | 'CONTENT_DISPUTE' | 'OTHER';
rationale: string;
traceId: string;
at: string;
}

2.9 regulator.siem.forwarded.v1

interface RegulatorSiemForwarded {
schemaVersion: '1';
eventId: string;
sourceEventId: string; // the event being forwarded
sourceSubject: string; // e.g. firewall.audit.v1
destinationId: string;
destinationName: string;
format: 'CEF' | 'LEEF' | 'RAW_JSON';
attempts: number;
deliveryLatencyMs: number;
ackedAt: string;
traceId: string;
at: string;
}

2.10 regulator.siem.destination.failed.v1

interface RegulatorSiemDestinationFailed {
schemaVersion: '1';
eventId: string;
destinationId: string;
destinationName: string;
failureStreak: number;
lastErr: string;
backoffMs: number;
walMode: boolean; // true if WAL fallback engaged
traceId: string;
at: string;
}

2.11 regulator.attestation.evidence.collected.v1

interface RegulatorAttestationEvidenceCollected {
schemaVersion: '1';
eventId: string;
framework: 'ISO_27001' | 'ISO_27017' | 'ISO_27018'
| 'SOC2_TYPE_II' | 'GSMA_AA_18';
controlId: string;
evidenceId: string;
evidenceRef: string;
manifestHashSha256: string;
ownerService: string;
status: 'CURRENT'; // always CURRENT on collection
lastReviewAt: string;
nextReviewDueAt: string;
traceId: string;
at: string;
}

2.12 regulator.attestation.bundle.generated.v1

interface RegulatorAttestationBundleGenerated {
schemaVersion: '1';
eventId: string;
bundleId: string;
framework: string;
year: number;
bundleRef: string;
signatureRef: string;
controlsIncluded: number;
evidenceFiles: number;
generatedByUserId: string;
traceId: string;
at: string;
}

2.13 regulator.auth.login.v1 / .failed.v1

interface RegulatorAuthLogin {
schemaVersion: '1';
eventId: string;
userId: string; // null on .failed.v1
certSubjectDn: string;
issuerDn: string;
ipMasked: string; // /24 mask
userAgent: string;
result: 'SUCCESS' | 'CRL_HARD_FAIL' | 'OCSP_UNAVAILABLE' | 'REVOKED'
| 'UNKNOWN_SUBJECT' | 'EXPIRED';
traceId: string;
at: string;
}

2.14 regulator.cert.revoked.v1

interface RegulatorCertRevoked {
schemaVersion: '1';
eventId: string;
userId: string;
certSubjectDn: string;
issuerDn: string;
reason: 'CRL_REVOKED' | 'OCSP_REVOKED' | 'ADMIN_REVOKE';
detectedAt: string;
activeLiInFlight: number;
traceId: string;
at: string;
}

2.15 regulator.auditor.access.granted.v1 / .revoked.v1

interface RegulatorAuditorAccessGranted {
schemaVersion: '1';
eventId: string;
auditorId: string;
firmName: string;
certSubjectDn: string;
grantedFrameworks: string[];
accessExpiresAt: string;
grantedByUserId: string;
traceId: string;
at: string;
}

3. Consumed Events

SubjectProducerPurpose within regulator-portal
auth.events.login.v1auth-serviceSIEM forwarding (CEF/LEEF)
auth.events.mfa.v1auth-serviceSIEM forwarding
auth.events.idp.changed.v1auth-serviceSIEM forwarding
compliance.audit.v1compliance-engineSIEM forwarding + monthly report aggregation
compliance.rule.changed.v1compliance-engineSIEM forwarding
compliance.tenant.tier.changed.v1compliance-engineSIEM forwarding
consent.granted.v1consent-ledger-serviceSIEM forwarding + consent-rate aggregation
consent.revoked.v1consent-ledger-serviceSIEM forwarding + revocation-rate aggregation
sender.id.issued.v1sender-id-registry-serviceSIEM + sender-ID activity aggregation
sender.id.suspended.v1sender-id-registry-serviceSIEM + monthly report
cbc.audit.v1cbc-serviceSIEM forwarding
firewall.audit.v1sms-firewall-serviceSIEM forwarding
fraud.detected.sim_box.v1, fraud.detected.ait.v1, fraud.detected.smishing.v1fraud-intel-serviceSIEM forwarding
cdr.exported.v1, cdr.submission.acked.v1, cdr.submission.rejected.v1cdr-mediation-serviceDaily CDR status report
auth.user.erased.v1auth-serviceGDPR-like erasure of PII in complaints

4. SIEM Payload Formats

4.1 CEF (Splunk / ArcSight)

Per ArcSight Common Event Format v25 specification.

CEF:0|Ghasi|regulator-portal|1.0|firewall.blocked|Outbound message blocked|8|rt=Apr 21 2026 10:14:32 UTC src=10.0.4.21 cs1Label=ruleId cs1=rl_01HV9 cs2Label=tenantId cs2=t_01HV... cs3Label=verdict cs3=BLOCK cs4Label=reason cs4=CONTENT_FORBIDDEN msg=Message blocked by firewall rule

Fields:

  • Header: Device Vendor | Product | Version | Signature ID | Name | Severity
  • Extensions: rt (receipt time), src, cs1..csN (custom strings with labels), msg

4.2 LEEF (QRadar)

Per IBM LEEF 2.0 specification.

LEEF:2.0|Ghasi|regulator-portal|1.0|compliance.audit|cat=compliance devTime=2026-04-21T10:14:32Z devTimeFormat=yyyy-MM-dd'T'HH:mm:ss'Z' verdict=BLOCK tenantId=t_01HV... ruleId=rl_01HV... src=10.0.4.21 dst=+93701*** msg=Compliance block verdict

4.3 Raw JSON

Original event payload, unmodified, sent to Logstash for flexible downstream parsing.


5. PII & Security Rules

  • Raw message body is forbidden in any forwarded event; already enforced upstream by compliance-engine and firewall-service.
  • Target MSISDN on LI events appears as targetMsisdnMasked in regulator.li.* events; the full MSISDN is retained only in regulator.li_requests column (encrypted) and in the physical LI delivery package (encrypted + signed).
  • Event payloads signed with the platform outbox signing key (RS256); critical consumers (SIEM forwarder, admin-dashboard) verify opportunistically.
  • SIEM forwarding filter per destination can further restrict subjects (e.g., ATRA only receives audit + compliance; internal SOC receives everything).

6. Schema Evolution

  • Additive fields with non-required defaults are non-breaking within the same schemaVersion.
  • Breaking changes bump to .v2. v1 and v2 coexist for ≥ 180 days (ATRA-coordinated) before v1 removal.
  • Enum values are additive; consumers treat unknown values as UNKNOWN and do not fail.
  • Schemas published via Schema Registry at /v1/regulator/schemas/{subject}.

7. Outbox Pattern

All state changes write an outbox row in the same Postgres transaction as the aggregate change. A relay publishes to NATS with retry and ACK. regulator.outbox is pruned 7 days post-publish for low-sensitivity events; LI and attestation events retained indefinitely mirrored to immutable S3.