Communication Service — Security Model
Status: populated Owner: TBD Last updated: 2026-04-17 Companion: Service Template · 13 Security
1. RBAC / ABAC matrix
| Action | DOCTOR | NURSE | CARE_COORDINATOR | PATIENT | ADMIN | SERVICE |
|---|---|---|---|---|---|---|
| Create thread | ✓ | ✓ | ✓ | via portal BFF only | ✓ | ✗ |
| Send message in thread | ✓ if participant | ✓ if participant | ✓ if participant | ✓ if patient-linked participant | ✓ if participant | ✗ |
| Read thread (patient-linked) | ✓ iff chart:read on patient | ✓ iff chart:read | ✓ iff chart:read | ✓ iff own patientId | ✓ (audit) | ✗ |
| Escalate thread | ✓ | ✓ | ✓ | ✗ | ✓ | ✗ |
| Submit notification intent | ✗ | ✗ | ✗ | ✗ | ✓ | ✓ |
| DLR callback | — | — | — | — | — | provider-secret |
| Admit virtual participant | ✓ (provider role) | ✓ | ✗ | ✗ | ✓ | ✗ |
| View virtual-session list | ✓ own sessions | ✓ own | ✓ own | ✓ own (identity-bound) | ✓ all | ✗ |
| End virtual session | ✓ (provider) | ✓ | ✗ | ✗ | ✓ | ✗ |
ABAC overlays:
- Patient-link: any thread operation on a patient-linked thread requires
chart:readon that patient (FR-COMMS-MSG-003 / BR-MSG-002). - Tenant scope: all operations scoped by
tidfrom JWT; cross-tenant thread participants forbidden. - Identity-bound PATIENT: PATIENT role can only query sessions / threads where they are a participant (FR-DCOM-018).
2. Encryption
| Data | At rest | In transit |
|---|---|---|
messages.body | pgcrypto AES-256 (column-level) or PG TDE; key via KMS | TLS 1.2+ |
message_attachments blobs | S3/MinIO SSE-KMS | TLS 1.2+; presigned URLs ≤ 15 min |
notification_intents.variables | At-rest; must not carry PHI | TLS 1.2+ |
virtual_sessions.room_ref | Plain (no PHI) | TLS 1.2+ |
| Join tokens | Signed with KMS private key (RS256); embed in URL for ≤ 5 min | HTTPS only |
| DLR callback secret | Tenant-scoped HMAC signing key | TLS 1.2+; HMAC validated |
3. Audit events
| Event | When |
|---|---|
audit.communication.thread.access | Read thread |
audit.communication.thread.patient_linked.access | Read a patient-linked thread (accounting of disclosures) |
audit.communication.message.sent | Send |
audit.communication.attachment.uploaded | Upload finalized |
audit.communication.notification.dispatched | Intent dispatched (metadata only) |
audit.communication.virtual_session.started | VC started |
audit.communication.virtual_session.recording.access | Recording viewed |
audit.communication.gdpr.thread.anonymized | GDPR anonymization |
4. GDPR / data subject participation
| Request | Behavior |
|---|---|
| Export | Return all threads where subject is participant + all intents with recipientRef hash matching subject |
| Erasure | Anonymize messages.sender_id and replace body with redaction marker; drop dispatch_records.recipient_ref; preserve structural audit per legal hold |
| Rectification | Participant metadata updates only; message bodies are immutable (correction via new message + retracted status on old) |
5. Data residency
- Messages and virtual-session metadata stored in the same region as the owning tenant's primary
patientsschema (Afghanistan / UAE / EU / US zones). - SMS dispatch via Ghasi-SMS-Gateway remains in-country for Afghan tenants. International fallback only with tenant opt-in.
- Email via regional SES where possible.
6. Rate-limiting & abuse prevention
- Per-user message send: 60/min (burst 120).
- Per-tenant intent submit: 10000/min.
- DLR callback endpoint: HMAC + IP allowlist per provider.
- Failed-send anomaly detection: alert if
failed / dispatched > 0.2over 5 min for any(tenant, channel)pair.
7. Threat model highlights
| Threat | Mitigation |
|---|---|
| PHI leak in push payload | Template system disallows free-form PHI variables; structured logs sanitized |
| Enumeration of patient threads | Tenant-scoped RLS + participant check; 404 not 403 on non-member |
| Replay of DLR callback | HMAC + timestamp window + idempotency on providerMessageId |
| Join-token theft | Short TTL, one-time use, bound to session + participant |
| Cross-tenant participant injection | Server validates every participantId belongs to same tenant |