regulator-portal-service — Domain Model
Version: 1.0 Status: Draft Owner: Regulator-facing + Legal Last Updated: 2026-04-21 Companion: SERVICE_OVERVIEW · APPLICATION_LOGIC · API_CONTRACTS · DATA_MODEL · EVENT_SCHEMAS · SECURITY_MODEL
1. Bounded Context
Regulatory & Lawful-Intercept Interface. The regulator-portal-service owns the ATRA-facing and external-auditor-facing surface of the platform: Lawful Intercept (LI) request workflow per ETSI TS 102 232, citizen-complaint ingest, scheduled and ad-hoc regulator reports, SIEM forwarding, and periodic compliance attestations (ISO 27001 / SOC 2 Type II / GSMA AA.18).
Inside the boundary:
RegulatorUser,AuditorUseridentities (national-PKI-mapped, mTLS-authenticated — no passwords)LiRequest,LiAuditEntry(ETSI TS 102 232 IRI/CC/FULL) with dual-control state machineComplaint(ATRA-forwarded citizen complaint) + triage recordReportJob,GeneratedReport(signed PDF artifacts) andScheduledReportdefinitionsAttestationControlcatalog,AttestationEvidencefreshness,AttestationBundleannual packagesSiemDestination(Splunk HEC / Logstash / QRadar LEEF) +SiemDeliveryLogAuditorAccessGrant(time-boxed, mTLS via auditor PKI)
Outside the boundary (read-through only — read-only against upstream):
- Audit content lives in
compliance-engine(compliance.audit.v1,compliance.audit_log) - Consent evidence lives in
consent-ledger-service(consent.granted.v1,consent.revoked.v1) - Sender-ID registry state lives in
sender-id-registry-service - CDR submission state lives in
cdr-mediation-service - Historical ClickHouse queries served by
analytics-service - Actual traffic interception is performed by MNOs /
cdr-mediation-service— regulator-portal only orchestrates the workflow
Per ADR-0004 §3, regulator-portal-service is a distinct bounded context; it never mutates upstream data. It assembles, signs, and serves.
2. Aggregates
2.1 RegulatorUser
Identity of an authenticated ATRA officer. Mapped from mTLS client-certificate subject DN (CN, O, OU, SERIALNUMBER).
| Field | Type | Notes |
|---|---|---|
userId | UUIDv4 | Platform identity |
certSubjectDn | string | RFC 4514 canonical DN; used as stable mapping key |
certFingerprintSha256 | bytes(32) | Verified at every handshake |
issuerDn | string | Must match one of the national-PKI trusted issuers |
orgName | string | Typically ATRA or accredited delegated body |
role | RegulatorRole enum | regulator-read · regulator-li · regulator-auditor · external-auditor |
allowedRegions | string[] | ISO 3166-2 subdivisions (e.g. AF-KAB) |
status | UserStatus | ACTIVE · SUSPENDED · REVOKED |
createdAt, lastLoginAt, revokedAt | Instant |
Invariants
certSubjectDn+issuerDnform a unique key (one identity per issued cert).role = regulator-lirequiresdualControl = trueon all LI transitions the user initiates.status = REVOKEDis terminal; CRL/OCSP revocation cascades here automatically (see SECURITY_MODEL §1.1).- No password, API key, or shared secret is ever associated with a
RegulatorUser.
2.2 AuditorUser
Special case of a regulator-like identity for a third-party external auditor (e.g., an ISO 27001 or SOC 2 audit firm). mTLS via the auditor's own PKI; separate trust anchor from national PKI.
| Field | Type | Notes |
|---|---|---|
auditorId | UUIDv4 | |
firmName | string | e.g. BigFour LLP |
certSubjectDn, certFingerprintSha256, issuerDn | as above | Issuer must be in auditor_ca_trust_store |
grantedFrameworks | ComplianceFramework[] | Per-engagement scope (ISO 27001 / SOC 2 / GSMA AA.18) |
accessGrantedAt, accessExpiresAt | Instant | Time-boxed; default 30 days (extendable by CISO + Legal) |
grantedBy | userId | Platform admin who issued the grant |
Invariants
accessExpiresAtis load-bearing — backend enforces TTL independently of JWTexp, so a stolen cert cannot outlive the grant.- An auditor user is read-only; all state-changing endpoints return
403 INSUFFICIENT_SCOPE. - Auditor reads themselves are audit-logged (meta-audit — the audit trail records who inspected it).
2.3 LiRequest
A lawful intercept request submitted by an authorised ATRA LI officer per ETSI TS 102 232-1/-2/-3 (IRI = intercept-related information; CC = communication content; FULL = both).
| Field | Type | Notes |
|---|---|---|
liRequestId | UUIDv4 (externally li_…) | |
regulatorUserId | UUIDv4 | Submitter |
targetMsisdn | E.164 | Single target per request (ETSI alignment) |
dateRangeFrom, dateRangeTo | Instant | Inclusive window |
scope | LiScope VO | IRI · CC · FULL |
legalRef | string | Court order reference / warrant number |
signedWarrantHash | bytes(32) | SHA-256 of the signed PDF warrant; compared against uploaded document |
warrantObjectRef | string | s3://ghasi-regulator/li/warrants/{liRequestId}.pdf (object-lock 7y) |
state | LiState VO | RECEIVED → ACK → IN_PROGRESS → DELIVERED → CLOSED (terminal) or REJECTED (terminal) |
ackBy, inProgressBy, deliverBy, closeBy | Instant | Per-state SLA deadlines |
initiator, approver | userId | Dual-control pair (see invariants) |
deliveryPackageRef | string | null | s3://ghasi-regulator/li/deliveries/{liRequestId}/ |
deliverySftpReceiptAt | Instant | null | Confirmed regulator receipt |
createdAt, updatedAt | Instant |
Invariants
- Dual-control. Every state transition requires both
initiator(Ghasi Legal) andapprover(Ghasi Security) to co-sign. Same user cannot fill both roles. Persisted atomically viaBEGIN; SELECT FOR UPDATE; INSERT INTO li_audit; UPDATE li_requests; COMMIT;. - Append-only audit.
LiAuditEntryrows are Postgres-rule protected against UPDATE/DELETE (see DATA_MODEL §3). - SLA hard-coded.
ackBy = createdAt + 1h;inProgressBy = createdAt + 4h;deliverBy = createdAt + 18h; alert fires 15 min before each deadline. - State monotonicity. Transitions only advance (
RECEIVED → ACK → IN_PROGRESS → DELIVERED → CLOSED).REJECTEDreachable only fromRECEIVEDorACK.REJECTEDandCLOSEDare terminal. - Warrant integrity.
signedWarrantHashMUST equal SHA-256 of the uploaded PDF at ingest; mismatch → 422 and no row created. - Cert-revocation cascade. If
regulatorUserId.status → REVOKEDduring an active workflow, state freezes and an URGENT alert fires; see FAILURE_MODES §FM-12.
2.4 LiAuditEntry
Append-only state-transition log. One row per transition; one row per user action within a workflow.
| Field | Notes |
|---|---|
auditId | UUIDv4 |
liRequestId | FK → LiRequest |
fromState, toState | LiState |
action | SUBMIT · ACK · START · DELIVER · CLOSE · REJECT · NOTE |
initiator, approver | userId (both NOT NULL except for NOTE) |
rationale | string (required on REJECT) |
hashPrev, hashSelf | bytes(32) |
occurredAt | Instant |
2.5 Complaint
Citizen complaint forwarded by ATRA. Regulator-portal-service does not resolve complaints itself — it ingests, routes to admin-dashboard workbench, tracks SLA, and reports resolution back.
| Field | Notes |
|---|---|
complaintId | UUIDv4 (externally comp_…) |
regulatorRef | string |
citizenMsisdnMasked | string |
complaintType | enum |
summary | string (≤ 4000 chars) |
receivedAt | Instant |
state | ComplaintState |
slaDueAt | Instant |
resolverTenantId | UUID | null |
resolutionSummary | string | null |
triageAiCategory | string | null |
Invariants
slaDueAt = receivedAt + 5 business days(business-day calendar scoped to Afghanistan working week).resolutionSummaryis mandatory whenstate = RESOLVEDorstate = CLOSED.- Citizen MSISDN is never returned to anyone except
platform.compliance.adminand the assigned resolver's tenant admin;citizen_msisdncolumn is encrypted per SECURITY_MODEL §3.
2.6 ScheduledReport and ReportJob
ScheduledReport defines the recurring contract (cron, type, recipients). ReportJob is one execution.
ScheduledReport Field | Notes |
|---|---|
scheduledReportId | UUIDv4 |
reportType | ReportType VO: DAILY_CDR_STATUS · MONTHLY_COMPLIANCE_SUMMARY · QUARTERLY_PLATFORM_HEALTH · AD_HOC |
cronExpr | CRON, timezone Asia/Kabul |
recipientEmails | email[] |
enabled | bool |
lastRunAt, lastStatus | Instant, PENDING/RUNNING/READY/FAILED |
ReportJob Field | Notes |
|---|---|
reportJobId | UUIDv4 (externally rpt_…) |
scheduledReportId | FK or null for ad-hoc |
filters | JSONB |
status | ReportStatus: PENDING · RUNNING · READY · FAILED |
outputRef | s3://ghasi-regulator/reports/{yyyy}/{mm}/{reportJobId}.pdf |
signatureRef | PKCS#7 detached signature alongside output |
signingKeyRef | Vault path for HSM-bound regulator-reports key |
requestedBy, requestedAt, completedAt |
Invariants
- Every
READYreport has a non-nullsignatureRef; a report without a signature is not downloadable. outputRefandsignatureRefare stored under object-lock retention = 7 years (regulatory evidence).- SLA: hot-window ad-hoc ≤ 30 min; cold-window (S3 Glacier restore) ≤ 24 h.
2.7 AttestationControl, AttestationEvidence, AttestationBundle
Catalog of compliance-framework controls and their evidence freshness.
AttestationControl Field | Notes |
|---|---|
framework | ComplianceFramework VO: ISO_27001 · ISO_27017 · ISO_27018 · SOC2_TYPE_II · GSMA_AA_18 |
controlId | e.g. A.12.4.1 for ISO 27001 (Event logging) |
title, description | Control text |
ownerService | e.g. compliance-engine, auth-service, infra |
evidenceType | AUTO_SBOM · AUTO_CI_RESULT · AUTO_ACCESS_REVIEW · AUTO_IMAGE_SIGNATURE · MANUAL_UPLOAD |
AttestationEvidence Field | Notes |
|---|---|
evidenceId | UUIDv4 |
framework, controlId | FK |
evidenceRef | s3://ghasi-regulator/evidence/{framework}/{controlId}/{hash} |
manifestHashSha256 | bytes(32) |
status | EvidenceStatus VO: CURRENT · STALE · MISSING |
lastReviewAt, nextReviewDueAt | Instant |
Invariants
status = STALEifflastReviewAt + 60 days < now().status = MISSINGiff no evidence row exists for that(framework, controlId).- Evidence files are immutable once referenced by a generated
AttestationBundle.
AttestationBundle Field | Notes |
|---|---|
bundleId | UUIDv4 |
framework, year | Composite natural key |
bundleRef | s3://ghasi-regulator/bundles/{framework}/{year}/bundle.zip (object-lock permanent) |
signatureRef | PKCS#7 detached (attestation-bundle signing key, HSM) |
controlMatrixRef, execSummaryRef | Sub-artifacts inside the ZIP |
generatedAt, generatedBy |
2.8 SiemDestination and SiemDeliveryLog
SiemDestination Field | Notes |
|---|---|
destinationId | UUIDv4 |
name | e.g. ATRA-Splunk-Prod, Ghasi-QRadar-SOC |
kind | SPLUNK_HEC · LOGSTASH_HTTP · QRADAR_SYSLOG |
endpointUrl | HTTPS URL or syslog://host:port |
format | CEF · LEEF · RAW_JSON |
authRef | Vault path to credential (HEC token / mTLS cert / syslog key) |
filter | JSONB |
enabled | bool |
SiemDeliveryLog Field | Notes |
|---|---|
logId | UUIDv4 |
destinationId | FK |
eventId | Matches NATS eventId |
subject | NATS subject |
status | ACKED · RETRYING · DEADLETTERED |
attempts | int |
lastErr | string | null |
ackedAt | Instant | null |
Invariants
- Delivery is at-least-once with destination ACK. An event is only
ACKEDafter the destination returns 2xx (HEC), a syslog-relay-ACK, or a Logstash ingest-ack. - Disk-WAL kicks in when NATS lag exceeds 1 h; see FAILURE_MODES §FM-02.
3. Value Objects
| VO | Shape | Invariants |
|---|---|---|
LiScope | IRI | CC | FULL | Per ETSI TS 102 232 |
LiState | RECEIVED | ACK | IN_PROGRESS | DELIVERED | CLOSED | REJECTED | Monotonic except REJECTED; dual-control required |
ComplaintState | RECEIVED | TRIAGED | ASSIGNED | RESOLVED | CLOSED | REJECTED | 5-business-day SLA |
ReportType | DAILY_CDR_STATUS | MONTHLY_COMPLIANCE_SUMMARY | QUARTERLY_PLATFORM_HEALTH | AD_HOC | Determines filter schema |
ReportStatus | PENDING | RUNNING | READY | FAILED | READY requires signature |
ComplianceFramework | ISO_27001 | ISO_27017 | ISO_27018 | SOC2_TYPE_II | GSMA_AA_18 | |
EvidenceStatus | CURRENT | STALE | MISSING | Derived from lastReviewAt |
RegulatorRole | regulator-read | regulator-li | regulator-auditor | external-auditor | Scope-bound |
UserStatus | ACTIVE | SUSPENDED | REVOKED | CRL/OCSP-driven |
SiemFormat | CEF | LEEF | RAW_JSON | CEF per ArcSight spec; LEEF per QRadar spec |
4. Domain Events (produced)
Full payloads in EVENT_SCHEMAS.md.
| Event | Trigger |
|---|---|
regulator.li.received.v1 | LI request submitted |
regulator.li.transitioned.v1 | Any LI state advance |
regulator.li.delivered.v1 | LI package SFTP receipt confirmed |
regulator.complaint.received.v1 | New complaint ingested |
regulator.complaint.resolved.v1 | Resolution written back |
regulator.report.submitted.v1 | Report job requested |
regulator.report.acked.v1 | Regulator acknowledges download |
regulator.report.rejected.v1 | Regulator raises integrity issue |
regulator.siem.forwarded.v1 | Per-destination delivery ACK observed |
regulator.attestation.evidence.collected.v1 | Auto-collection cron completed |
regulator.attestation.bundle.generated.v1 | Annual bundle signed and stored |
regulator.auditor.access.granted.v1 / .revoked.v1 | Auditor grant change |
Consumed events are listed in EVENT_SCHEMAS.md §3.
5. Global Invariants
- Read-only against upstream.
regulator-portal-servicenever mutates any table owned by another service. Reports and deliveries are assembled by read-through gRPC/REST. - mTLS-only. No endpoint is reachable with password, API key, or bearer token for regulator or auditor identities. Internal admin APIs use the platform JWT via Kong.
- CRL + OCSP enforced at every handshake. Stapled OCSP (RFC 6066) preferred; CRL fallback on OCSP outage. Hard-fail on no staple in regulator cert (soft-fail tolerated for auditor cert with Legal approval).
- Signed reports. Every downloadable artifact (report, LI delivery, attestation bundle) carries a detached PKCS#7 signature over the raw bytes, using an HSM-bound key (Vault Transit or PKCS#11 HSM; see ADR-0004 §11).
- Append-only audit.
li_audit,complaint_triage.audit_trail,attestation_evidence.audit_log, andauditor_access.audit_logare hash-chained and Postgres-rule protected. - Time-boxed auditor access. Every read by an
external-auditoris audit-logged. Access auto-expires ataccessExpiresAt; extensions require CISO + Legal dual-sign. - SIEM at-least-once. Events are re-delivered until destination ACK; disk-WAL backs NATS when regulator-side is degraded.
- Object-lock retention. LI packages + complaints: 7 years. Reports: 7 years. Attestation bundles: permanent. Enforced at the S3 bucket policy level, not application-layer (application cannot delete).
6. Cross-Reference Map
| Concept | Lives here | Also referenced by |
|---|---|---|
| LI workflow | regulator schema | ATRA SFTP drop-box; audit-dashboard workbench |
| Complaint triage | regulator.complaints | admin-dashboard EP-ADMDASH-10 workbench |
| Report source data | Read-through | compliance-engine, consent-ledger-service, sender-id-registry-service, cdr-mediation-service, analytics-service (ClickHouse) |
| SIEM source events | NATS JetStream | auth-service, compliance-engine, firewall-service, fraud-intel-service, cbc-service, consent-ledger |
| Attestation controls | regulator.attestations | All services contribute evidence |
| HSM signing keys | Vault Transit | Referenced via signing_keys_ref; HSM-held per ADR-0004 §11 |