Skip to main content

SMS Orchestrator — Security Model

Status: populated Owner: Platform Engineering + Security Last updated: 2026-04-18 Companion: 13 Security · ADR-0001 Kong

1. Authentication

Enforced at Kong:

  • jwt plugin (Firebase / auth-service JWKS) for user traffic.
  • key-auth (or custom ghasi-api-key-lookup) plugin for machine traffic.

Service re-validates X-Tenant-Id header against JWT tenant_id claim — rejects mismatch with 403.

2. Authorization (RBAC/ABAC)

ScopeActionRole(s)
sms:sendPOST /v1/sms/send, /v1/sms/bulkaccount.sender, account.admin
sms:readGET /v1/sms/{id}account.reader, account.sender, account.admin
Admin cross-tenant readAny messageplatform.admin (via admin-dashboard, audit-logged)

Role checks performed in service guards; scopes embedded in JWT.

3. Tenant Isolation

  • tenant_id column + RLS policy on every table.
  • Query always includes WHERE tenant_id = $1 AND RLS; double defense.
  • Integration test test/integration/tenant-isolation.spec.ts proves cross-tenant read returns 404.

4. Encryption

ClassMechanism
In-flightTLS 1.2+ (Kong + internal mesh mTLS)
At rest (PG)Transparent disk encryption (AWS EBS / GCP PD)
Sensitive column (body)Application-level field not required at current sensitivity class; revisit if regulated content categories are added
SecretsVault → env injected at pod start; no plaintext in env files

5. Audit

Events written to sms.events.status are the audit trail. Administrative reads via admin-dashboard emit audit.access.v1 (owned by audit/ops, not this service).

6. Threat Model

ThreatControl
Credential stuffing against API keysKong rate-limiting-advanced + account lockout in auth-service
Replay attacksIdempotency-Key window; replay returns same messageId
Spam / abuse (high-volume sender)Per-account + per-destination rate limits at Kong; future content classifier (AI)
PII leakage in logsBody and MSISDNs hashed in Pino logs; forbidden in Kong access logs
DB SQL injectionPrisma parameterization + ESLint rule
Tenant spoofingX-Tenant-Id re-checked against JWT claim; fail closed

7. Data Residency + GDPR

  • tenant_id → region mapping in tenant-service; this service runs per region.
  • GDPR erasure: on tenant_erased.v1, this service redacts body, to, from, metadata on matching rows (keeps messageId + timestamps + billing-required fields).