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
| Stream | Subjects | Retention | Replicas | Dedup window |
|---|---|---|---|---|
REGULATOR_LI | regulator.li.received.v1, regulator.li.transitioned.v1, regulator.li.delivered.v1, regulator.li.rejected.v1, regulator.li.sla.breached.v1 | 7 years (object-lock mirrored) | 3 | 5 min |
REGULATOR_COMPLAINTS | regulator.complaint.received.v1, regulator.complaint.resolved.v1, regulator.complaint.closed.v1 | 7 years | 3 | 5 min |
REGULATOR_REPORTS | regulator.report.submitted.v1, regulator.report.ready.v1, regulator.report.acked.v1, regulator.report.rejected.v1 | 7 years | 3 | 2 min |
REGULATOR_SIEM | regulator.siem.forwarded.v1, regulator.siem.destination.failed.v1 | 30 days | 3 | — |
REGULATOR_ATTESTATION | regulator.attestation.evidence.collected.v1, regulator.attestation.evidence.stale.v1, regulator.attestation.bundle.generated.v1 | Permanent | 3 | — |
REGULATOR_AUTH | regulator.auth.login.v1, regulator.auth.failed.v1, regulator.cert.revoked.v1, regulator.auditor.access.granted.v1, regulator.auditor.access.revoked.v1 | 13 months | 3 | — |
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
| Subject | Producer | Purpose within regulator-portal |
|---|---|---|
auth.events.login.v1 | auth-service | SIEM forwarding (CEF/LEEF) |
auth.events.mfa.v1 | auth-service | SIEM forwarding |
auth.events.idp.changed.v1 | auth-service | SIEM forwarding |
compliance.audit.v1 | compliance-engine | SIEM forwarding + monthly report aggregation |
compliance.rule.changed.v1 | compliance-engine | SIEM forwarding |
compliance.tenant.tier.changed.v1 | compliance-engine | SIEM forwarding |
consent.granted.v1 | consent-ledger-service | SIEM forwarding + consent-rate aggregation |
consent.revoked.v1 | consent-ledger-service | SIEM forwarding + revocation-rate aggregation |
sender.id.issued.v1 | sender-id-registry-service | SIEM + sender-ID activity aggregation |
sender.id.suspended.v1 | sender-id-registry-service | SIEM + monthly report |
cbc.audit.v1 | cbc-service | SIEM forwarding |
firewall.audit.v1 | sms-firewall-service | SIEM forwarding |
fraud.detected.sim_box.v1, fraud.detected.ait.v1, fraud.detected.smishing.v1 | fraud-intel-service | SIEM forwarding |
cdr.exported.v1, cdr.submission.acked.v1, cdr.submission.rejected.v1 | cdr-mediation-service | Daily CDR status report |
auth.user.erased.v1 | auth-service | GDPR-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
targetMsisdnMaskedinregulator.li.*events; the full MSISDN is retained only inregulator.li_requestscolumn (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
UNKNOWNand 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.