# MHI Gateway Security Proposal

## 2026-07-20 — RC1-4A dedicated message-status route correction

- Added the frozen `GET /api/v1/messages/{message_id}/status` runtime route and exact compact status representation omitted during RC1-4.
- Reused the existing tenant/application-scoped status query and public projector while preserving `GET /api/v1/messages/{message_id}` unchanged.
- Added focused contract, correlation, occurrence-time, authentication, tenant-isolation, error, route-inventory, and full-resource regression coverage. No API contract, OpenAPI, migration, DLR, webhook, idempotency, SMPP, `SessionExecutor`, or `RuntimeLoop` behavior changed.

## 2026-07-19 — RC1-8 staging and client integration package

- Added an operator-led deployment runbook for staging target `10.0.0.200` without performing deployment or inventing environment commands.
- Added generic API integration and quick-start guides, a layered staging smoke test, placeholder-only cURL/Postman requests, standalone PHP/Python/Node clients, and webhook verification receivers.
- Documented how to assemble the ignored `dist/client-integration/` artifact from versioned sources and listed every server fact still required before deployment.
- No API contract, production code, runtime behavior, test, migration, database schema, service configuration, commit, push, or deployment was performed.

## 2026-07-19 — RC1-7 release-candidate handover documentation

- Added the RC1 implemented-scope and public API integration handover, webhook receiver verification, deployment guide, database migration/rollback guidance, and operations runbook.
- Added the exact repository-backed YAS configuration inventory, explicit external unknowns, diagnostic procedure, and separate staging/YAS acceptance boundaries.
- Added 25 executable acceptance scenarios, minimum evidence package, security and known-limitations checklists, and production go/no-go criteria.
- Internal documentation verification is complete; staging acceptance, YAS sign-off, rollback approval, and production approval remain pending. No production code, test, migration, configuration, API contract, `SessionExecutor`, or `RuntimeLoop` behavior changed.

## 2026-07-19 — RC1-6 tenant delivery webhooks (awaiting approval)

- Added authenticated tenant-scoped `GET /api/v1/webhook` and create-or-replace `PUT /api/v1/webhook` using one encrypted configuration and write-only secret rotation with a 24-hour overlap.
- Added atomic terminal webhook-event recording from RC1-5 normalized evidence and exactly one post-commit delivery attempt for delivered, failed, expired, or rejected public states.
- Added exact-body HMAC-SHA256 signing, 10-second HTTP timeout, public-only payloads, 90-day attempt retention, tenant isolation, duplicate suppression, and failure isolation.
- Added no retries, retry workers, dead-letter queues, dashboards, statistics, billing, wallet, schedules, campaigns, templates, SMPP runtime, `SessionExecutor`, or `RuntimeLoop` behavior.

## 2026-07-19 — RC1-5A webhook contract amendment

- Approved one tenant webhook configuration at `GET /api/v1/webhook` and create-or-replace `PUT /api/v1/webhook`, with no POST, DELETE, multiple endpoints, priorities, or filters.
- Froze a 32-random-byte minimum secret, 24-hour rotation overlap, HMAC-SHA256 timestamp/body signatures, five-minute replay window, 10-second timeout, one RC1 delivery attempt, and 90-day attempt retention.
- Limited events to exactly-once delivered, failed, expired, and rejected terminal outcomes with public-only payload fields; failures never roll back delivery persistence.
- Clarified the required sequence as RC1-5A, then RC1-6, then RC1-7. No production code, test, migration, route, or runtime behavior changed.

## 2026-07-19 — RC1-5 delivery receipt correlation (awaiting approval)

- Added one post-handler inbound consumer that reuses validated SMPP receipt evidence and correlates provider message identifiers to acknowledged outbound attempts.
- Added immutable normalized DLR evidence, atomic message/event projection, exact duplicate idempotency, ambiguous-reference isolation, terminal conflict retention, and rollback safety.
- Reused RC1-4 public status projection without modifying the public API contract or adding endpoints.
- Added no webhook, callback, retry, schedule, campaign, template, report, billing, wallet, reconnect, SessionExecutor, or RuntimeLoop behavior.

## 2026-07-19 — RC1-4 public message status API (awaiting approval)

- Normalized authenticated `GET /api/v1/messages/{message_id}` to the committed public message-details representation and current-request correlation header.
- Added tenant/application-scoped lookup, strict public identifier validation, closed status projection, safe retrieval logging, and standardized not-found and validation errors.
- Kept existing message and delivery enums internal and added no state mutation or invented transition.
- Added no dedicated status endpoint, DLR processing, callback, webhook, retry, scheduling, batch retrieval, reporting, billing, wallet, SMPP, session, or runtime behavior.

## 2026-07-19 — RC1-3 bulk message submission API (awaiting approval)

- Implemented `POST /api/v1/messages/bulk` with 1–100 ordered items, best-effort item processing, HTTP `202`, stable counters, and one accepted or rejected result per input index.
- Reused the RC1-2 single-message creation service for accepted items and added tenant-scoped durable whole-request replay metadata with concurrent convergence and conflict fencing.
- Preserved the current-request `X-Correlation-ID`, existing authentication and rate limiting, and sanitized request-level and per-item errors.
- Added no public batch resource, GET, cancellation, retry, scheduling, template, campaign, DLR, status, webhook, billing, wallet, SMPP, session, or runtime behavior.

## 2026-07-19 — RC1-1A bulk API contract amendment

- Approved the documentation contract for `POST /api/v1/messages/bulk`: 1–100 ordered items, best-effort partial success, HTTP `202`, ordered item results, and `accepted`/`rejected` counters.
- Approved a UUIDv4 `batch_id` generated once and retained on replay without creating a public batch resource or GET endpoint.
- Froze tenant-scoped whole-request idempotency, replay-stable results, current-request correlation headers, request-level versus item-level errors, and concurrent convergence/conflict behavior.
- Updated the active sequence through RC1-7 while keeping Milestone 6A deferred, not cancelled.
- No production code, route, migration, test, DLR, webhook, billing, or SMPP/runtime behavior changed.

## 2026-07-19 — RC1-2 Send API hardening (implemented and merged)

- Hardened only `POST /api/v1/messages` with frozen request fields plus legacy aliases, a normalized queued response, UUIDv4 correlation propagation, and stable public send errors.
- Added tenant-scoped durable exact replay using a one-way idempotency-key hash and canonical request fingerprint; conflicting payloads fail with `409 IDEMPOTENCY_CONFLICT` and different tenants may reuse a key.
- Clarified that the response header identifies the current HTTP request while the replayed response body and message/business log retain the original submission correlation.
- Preserved existing authentication, rate limiting, non-send API response compatibility, persistence-only message creation, and protected SMPP/runtime behavior.
- Added no bulk, status projection, DLR processing, webhook, queue, retry, billing, or wallet behavior.

## 2026-07-19 — RC1-1 API contract freeze proposed

- Audited six proposed API v1 message and delivery-webhook resources against current routes, validation, resources, persistence, authentication, correlation, errors, and lifecycle authorities.
- Added the proposed API contract and OpenAPI 3.1 companion with normalized lifecycle and DLR rules, durable replay semantics, signed webhook boundaries, compatibility gaps, and explicit approval blockers.
- Recorded RC1-1 through RC1-7 as the temporary priority sequence. Milestone 6A is deferred, not cancelled.
- Implementation remains blocked pending independent approval; no production behavior changed.

## 2026-07-19 — Milestone 5C-6 inbound processing orchestration (awaiting approval)

- Added one application orchestrator composing the existing lifecycle repository, authorized PDU decoder, processing contract, classifier, router, and handler into one claim-to-terminal flow.
- Added immutable processed, quarantined, no-envelope, and sanitized failure results with exactly-once stage and terminal-mutation coverage, plus dedicated MySQL fencing and interrupted-claim recovery verification.
- Added no schema change, tenant/MO/DLR business persistence or projection, correlation, callback, webhook, queue, retry worker, billing, acknowledgement, session, or `RuntimeLoop` behavior.

## 2026-07-19 — Milestone 5C-5 inbound envelope lifecycle repository (awaiting approval)

- Extended the existing encrypted inbound-envelope repository and table with database-clock claims, opaque ownership fencing, authorized read/decryption, and immutable `PROCESSED`/`QUARANTINED` terminal transitions.
- Added stale-claim takeover at the exact expiry boundary, same-outcome terminal idempotency, conflicting-outcome rejection, MySQL constraints/indexes, and two-connection concurrency coverage.
- Preserved ciphertext, provenance, identity, recording, persist-before-ack, session flow, and `RuntimeLoop`; added no interpretation, orchestration, tenant/business projection, queue, retry, callback, or webhook behavior.

## 2026-07-19 — Milestone 5C-4 inbound envelope processing contract (awaiting approval)

- Added immutable processing input and safe identity/provenance references for one existing `AVAILABLE` inbound envelope.
- Added a closed handled, rejected, and quarantined outcome hierarchy plus a narrow framework-free processor interface and boundary-owned invariant failures.
- Added no persistence, lifecycle mutation, encryption, transport, queue, callback, webhook, tenant/business projection, orchestration, session, or `RuntimeLoop` behavior.

## 2026-07-18 — Corrective Milestone 5B-7 durable inbound-envelope foundation (awaiting approval)

- Added immutable inbound-envelope identity and provider/session/sequence provenance values plus sensitive exact-PDU content protection.
- Added encrypted `smpp_inbound_envelopes` persistence with initial `AVAILABLE` state, transport-identity idempotency, constraints, indexes, and database timestamps.
- Required durable envelope recording before `deliver_sm_resp`; persistence failure now prevents acknowledgement and invalidates the session.
- Added no claims, terminal processing states, recovery, tenant/MO/DLR business behavior, queue, retry, callback, billing, or `RuntimeLoop` change.

## 2026-07-18 — Milestone 5C-3 inbound handler boundary (awaiting approval)

- Added a pure protocol handler that maps the three immutable inbound route categories to immutable typed handling results while retaining the exact routing and protocol results.
- Integrated exactly one handler invocation for routed receive results; non-routable results and failures continue unchanged.
- Added no persistence, queues, callbacks, events, tenant lookup, application routing, business processing, retries, reconnects, metrics, HTTP, or state changes; `RuntimeLoop` remains unchanged.

## 2026-07-18 — Milestone 5C-2 inbound routing foundation (awaiting approval)

- Added a pure, deterministic inbound router that maps mobile-originated, delivery-receipt, and unknown `deliver_sm` protocol results to immutable typed routes.
- Preserved all non-routable and failure protocol results unchanged and inserted exactly one router invocation into the existing single receive path.
- Added no application routing, tenant lookup, persistence, database writes, repositories, queues, callbacks, events, retries, reconnects, daemon, metrics, HTTP, or business processing; `RuntimeLoop` remains unchanged.

## 2026-07-18 — Milestone 5C-1 `deliver_sm` reception and classification (awaiting approval)

- Added immutable `DeliverSm`/`DeliverSmResp` protocol PDUs, strict body validation, and raw preservation of optional and unknown TLVs.
- Added protocol-only mobile-originated, delivery-receipt, and unknown classification from `esm_class`, receipt TLVs, and receipt text.
- Added one sequence-matched `ESME_ROK` response and immutable typed results while preserving the single receive path and leaving `RuntimeLoop` unchanged.
- Added no persistence, routing, tenant/business processing, queues, callbacks, retries, reconnects, scheduling, daemon, metrics, diagnostics, webhooks, HTTP endpoints, or database writes.

## 2026-07-18 — Milestone 5B-7 SMPP runtime protocol event processing (awaiting approval)

- Added a single bound-session `receiveOne()` path with exactly one framed read, protocol decode, event dispatch, and optional response write.
- Added immutable typed classifications for handled `enquire_link`, `generic_nack`, unsupported requests, unexpected responses, unknown commands, malformed PDUs, transport failures, and illegal session state.
- Added automatic header-only `enquire_link_resp` using the incoming sequence and shared protocol command-length authority, plus deterministic fragmented-input and failure tests.
- Kept `RuntimeLoop` unchanged and added no `deliver_sm`/DLR processing, inbound routing, persistence, retries, reconnects, windowing, scheduling, daemon, metric, or diagnostic behavior.

## 2026-07-18 — Milestone 5B-6 SMPP outbound execution (awaiting approval)

- Added a `RuntimeExecutor` for lease acquisition/renewal/release, submission claims, snapshot mapping, offline PDU encoding, and injected sleep execution while retaining `RuntimeLoop` as a pure planner.
- Added a narrow `SmppTransport` boundary and blocking `TcpTransport` implementation with TCP/TLS selection, timeouts, full writes, framed reads, and deterministic transport failures.
- Added explicit SMPP session state and execution for connect, transceiver bind, bound enquire-link, `submit_sm`/`submit_sm_resp`, unbind, and disconnect using the offline encoder and decoder.
- Added deterministic repository, sleeper, and transport-fake tests with no live SMSC dependency.
- Deferred reconnect, retry, DLR handling, inbound routing, metrics, diagnostics, daemon commands, and persistent runtime execution.
- Corrected the runtime-to-session boundary with an immutable claimed-submission handoff and typed SMSC accepted, rejected, and sanitized failure outcomes.
- Moved bind and `submit_sm` command construction into protocol-owned factories and made mapping defaults and the unsegmented 254-byte payload limit explicit.
- Hardened TCP/TLS with validated endpoints and independent timeouts, explicit verified TLS 1.2+ policy, bounded PDU frames, injectable stream I/O, full writes, exact reads, categorized failures, and deterministic cleanup.
- Expanded session states to binding, bound, unbinding, closed, and failed, with fatal exchange invalidation and typed response/rejection semantics.

## 2026-07-18 — Milestone 5B-5 deterministic SMPP runtime skeleton (awaiting approval)

- Prepared a framework-free deterministic intent planner with immutable contexts, typed execution outcomes, ordered intent actions, typed failures, injected clock, and validated provider/lease/batch/poll/idle configuration.
- Added explicit acquiring, owned, lease-lost, stopping, and stopped transitions with provider, owner, generation, expiry, and claimed-submission ownership validation.
- Added intent-only claimed-submission mapping to immutable `submit_sm` and offline encoding, with no binary payload retained by runtime results.
- Made mapping and encoding failures preserve valid ownership and sequence deterministically; retries and durable claimed-submission recovery remain deferred.
- Added no repository execution, mapper execution, encoder execution, sleeping, sockets, TCP/TLS, live bind or submission, transport I/O, reconnect, keepalive, retry, DLR, inbound routing, diagnostics, metrics, commands, persistent processes, or Laravel facade use.

## 2026-07-17 — Milestone 5B-4 offline SMPP protocol engine

- Added immutable SMPP header, TLV, bind, submission, enquire-link, unbind, and generic-nack protocol objects.
- Added closed registry-driven binary encoding and decoding for network-order integers, field-bounded C-Octet strings, TLVs, and supported PDU bodies, with protocol-specific rejection of malformed, truncated, unsupported, length-invalid, and overflowing input.
- Enforced immutable command/header, octet, TLV collection, response status/body, and SMPP field-size invariants; `submit_sm.sm_length` is derived from and limited to 0–254 payload octets.
- Preserved unknown valid and duplicate TLVs byte-for-byte in wire order without provider-specific semantic interpretation.
- Made body-derived command length a shared encoder/construction invariant and documented the required 64-bit PHP runtime (`PHP_INT_SIZE >= 8`).
- Added a deterministic in-memory positive `uint32` sequence generator with wrap from `UINT32_MAX` to one.
- Preserved a strict protocol/runtime boundary: no sockets, TCP, TLS, bind or submission execution, runtime, dispatcher, lease behavior, reconnect, keepalive, retry, DLR, inbound routing, metrics, or diagnostics were introduced.

## 2026-07-17 — Milestone 5B-3 SMPP submission handoff

- Added encrypted durable outbound submission enqueue from the transactional outbox publisher without provider dispatch.
- Added deterministic database-time batch claims that lock and validate the active unexpired session lease before locking submissions, rejecting missing or stale ownership without mutation.
- Limited submission lifecycle to `pending` and `claimed`; no SMPP protocol, runtime loop, retry, recovery, terminal state, inbound, DLR, or diagnostics were introduced.

## 2026-07-17 — Milestone 5B-2 SMPP session lease

- Added logical SMPP lease ownership through one durable row per provider with opaque owner validation and generation fencing; physical socket fencing remains deferred.
- Added authoritative MySQL UTC database time, validated lease durations, row-locked transactional acquire, renew, expiration-based release, observational current-snapshot lookup, expired takeover, and stale-owner/generation rejection.
- Added sanitized database-failure handling, SQLite lifecycle coverage, and dedicated-MySQL concurrency coverage for initial acquisition, expired takeover, renew/takeover, and release/takeover races.
- Added no runtime process, socket, SMPP protocol, event loop, keepalive, reconnect, publisher, submission, inbound, DLR, or diagnostic behavior.

## 2026-07-17 — Milestone 5B-1 YAS SMPP runtime foundation

- Added immutable, fail-closed runtime timing configuration requiring explicit values pending provider confirmation, with no YAS-dependent numeric defaults.
- Restricted runtime-enabled environment parsing to actual booleans and exact lowercase `true` or `false`, and documented that every timing value remains mandatory whenever configuration is resolved while disabled.
- Added transport-lifecycle enums, validated framework-free runtime value objects, and minimal unbound transport/repository contracts.
- Protected the lease owner token from debug and serialization output and prohibited configuration and owner reconstruction.
- Added no database, migration, repository implementation, lease behavior, socket, protocol engine, runtime process, command, handoff, inbound persistence, receive loop, keepalive, reconnect, diagnostic, or DLR behavior.

## 2026-07-17 — Milestone 5A YAS SMPP operational hardening

- Added one immutable, fail-closed YAS connection configuration shared by operational dispatch and both diagnostic commands.
- Enforced exact transceiver, `TR`, SMPP 3.4, source `5/0`, destination `2/1`, endpoint, credential, and bounded read/write timeout requirements; validated the confirmed `max_sessions=1` configuration without claiming cross-process coordination.
- Marked `connect_timeout` as validated deferred compatibility configuration because the current vendor path cannot enforce a distinct TCP connection timeout.
- Made per-submission `registered_delivery` encoding explicit as `0x00` or `0x01` and removed adapter protocol fallbacks.
- Prohibited configuration cloning and serialized reconstruction, added sanitized missing-vault handling, and installed a default throwing no-socket PHPUnit client binding.
- Kept sessions short-lived and operational activation disabled by default; added no receive loop, persistent worker, DLR handling, callbacks, webhooks, pooling, or locking.
- Added offline tests for protocol values, validation, bind parity, registered-delivery bytes, fail-closed behavior, and sensitive-data exclusion.

## 2026-07-17 — Milestone 4 internal wallet foundation

- Added one prepaid wallet per tenant and currency with non-negative available and reserved integer projections.
- Added message-linked active/captured/released reservations and an immutable balanced funding/available/reserved/consumed subledger.
- Added internal credit, reserve, capture, and release operations with short row-locked transactions, atomic message billing projections, safe events, and minimal audit evidence.
- Added dedicated operation-aware wallet HMAC idempotency without storing raw keys.
- Added SQLite lifecycle coverage and opt-in dedicated-MySQL row-lock and uniqueness tests.
- Did not activate dispatcher billing or change provider, SMPP, outbox, route, controller, or public API behavior.
- Protected raw wallet idempotency keys from DTO JSON, array, debug, and public-property serialization while retaining a narrow hashing-only reader.
- Added sanitized unique-key winner recovery, pre-insert reserve overflow validation, and guarded wallet/reservation Eloquent mutation boundaries.
- Added tenant-scoped read-only ledger reconciliation for projections, funding, consumption, active reservations, transaction balance, and operation-specific entry signs.
- Replaced raw-mutation MySQL locking checks with opt-in synchronized subprocess tests that exercise concurrent public accounting-service operations.

## Milestone 3C — Dispatch Persistence and YAS Provider Activation

- Unified manual and operational SMS address construction so both trim and validate alphanumeric senders and convert canonical `+` recipients to the same plus-free SMPP destination address without changing persisted recipient data.
- Enforced absolute transaction-level-zero entry and provider invocation, preventing nested transactions or savepoints around network I/O.
- Strengthened finalization ownership with tenant, message and attempt internal/public identifiers, submitting status, immutable provider code, and dispatch token.
- Replaced generic evidence cleanup with strict provider-code, provider-reference, response-code, failure-category, and connection-state validation.
- Removed exception source paths and line numbers from SMPP results, normal logs, adapter details, and manual command output.
- Added fail-closed validation for YAS name, transceiver bind mode, SMPP 3.4, `TR` system type, TON/NPI values, credentials, endpoints, and timeouts.
- Made every submission outcome terminal at the attempt layer; accepted attempts now set submitted, accepted, and completed timestamps.
- Added token-owned begin/finalize dispatch transactions around a transaction-free provider invocation, with atomic attempt, message-state, and append-only event persistence.
- Added the forward-only operational attempt schema extension, including public/provider identifiers, transmission evidence, dispatch ownership, completion time, and provider-reference indexing.
- Added channel request factories and an SMS-only YAS application-provider wrapper over the unchanged manual SMPP adapter path.
- Added fail-closed YAS activation through `YAS_OPERATIONAL_DISPATCH_ENABLED=false` plus strict connection configuration validation.
- Mapped accepted, rejected, unknown, and unavailable outcomes to explicit attempt/message/delivery states; only unavailable fails outbox publication. Billing, retries, queues, workers, scheduling, DLR, APIs, and routing remain unchanged.
- Added coverage for ownership, duplicate finalization, provider outcomes, safe evidence, default-disabled activation, and preservation of the manual SMPP behavior.

## Milestone 3B — Message Dispatcher

- Added a channel-neutral, read-only message dispatcher with tenant/application/message-scoped loading and current outbound/accepted/payload eligibility checks.
- Added explicit provider resolution with no automatic discovery and typed accepted, rejected, unknown, and unavailable outcomes.
- Added transient SMS payload handling that decrypts recipient and body only for provider invocation and excludes them from results, logs, persistence, and outbox payloads.
- Added an opt-in `MessageAccepted` outbox bridge that permits publication only for confirmed provider acceptance and maps all other outcomes to sanitized failures.
- Kept the runtime outbox publisher disabled and registered no YAS, SMPP, placeholder, or other operational provider; no attempts, status changes, queues, retries, billing, DLR, API, migration, or webhook behavior was added.

## Milestone 3A — Transactional Outbox Publisher

- Added an oldest-first claim/execute/finalize outbox lifecycle: short row-locked claim and ownership-checked finalization transactions surround a transaction-free callback.
- Enforced a zero-transaction entry invariant and added the sanitized `outbox_transaction_context_invalid` failure category before any event can be claimed.
- Incremented `attempts` once at claim time per callback invocation and required a unique claim token for both success and failure finalization so stale workers cannot finalize another worker's event.
- Added explicit ownership-loss result and command failure reporting without mutating the event owned by another worker.
- Added a fail-closed disabled publisher that stores only `outbox_publisher_not_configured` and cannot consume an event as published without operational publication.
- Added `gateway:publish-outbox` with `--once`, `--limit`, and mutation-free `--dry-run` options.
- Added sanitized fixed-category failure persistence and automated coverage for committed claims, transaction-free callbacks, ownership checks, stale workers, concurrent claims, dry runs, disabled runtime behavior, and published-record exclusion.
- Documented terminal failed and abandoned publishing states, with retry and stale-claim recovery deferred, and added opt-in two-connection MySQL locking verification because SQLite does not enforce row locks.

## Local Integration Setup Command

- Added the idempotent, confirmation-gated `gateway:setup:integration` command for creating or safely reusing an active tenant, application, setup-only user, and tenant membership without creating credentials, passwords, messages, or unrelated records.

## Administrative API Credential Issuance Command

- Added the confirmation-gated `gateway:credential:issue` command for local and integration administration, with tenant-scoped application resolution, status checks, one-time key disclosure, and no plaintext logging or persistence.

## Milestone 2B — Message Submission API

- Added authenticated `/api/v1/messages` submission, retrieval, and cursor-listing endpoints for single transactional SMS messages.
- Added separate request and message correlation identifiers, standardized safe JSON errors, explicit public resources, credential-aware rate limiting, and tenant/application-scoped queries.
- Added deterministic database-backed idempotency conflicts and API privacy tests while preserving persistence-only asynchronous behavior: no job dispatch, outbox publishing, provider call, attempt creation, routing, or billing.

## Milestone 2A — Messaging Domain Foundation

- Added tenant-safe, channel-neutral message persistence with typed PHP enums stored in VARCHAR columns, SMS-specific protected content, submission attempts, append-only events, and a transactional outbox foundation.
- Added composite database ownership constraints for applications, credentials, messages, and tenant-denormalized child records, plus database-level message idempotency with sanitized conflict errors.
- Added randomized Laravel encryption for recipients and message bodies, keyed HMAC-SHA256 recipient lookup hashes, masked display values, metadata redaction, safe public-ULID outbox payloads, factories, and automated privacy/rollback/isolation tests.
- Added persistence only; no API, queue publisher, routing, provider sending, billing calculation, delivery-receipt receiver, webhook, or UI was introduced.

## Milestone 1 — Multi-Tenant Core

- Added tenants, applications, memberships, hashed API credentials, fixed-role RBAC foundations, and append-only audit records.
- Added strict credential-derived request context, standardized public authentication failures, and explicit tenant-safe query helpers without global scopes.
- Added the MySQL-compatible composite ownership constraint, throttled usage timestamps, internal-header default-deny controls, and tenant-isolation/security tests.

## Milestone 0 live SMS submit validation

- Added a non-retryable `submit_unconfirmed` outcome when transmitted `submit_sm` receives no acknowledgement.
- Added sanitized relative file and line metadata for live submit exceptions.
- Exposed only the sanitized submit exception class in manual failure diagnostics.
- Refined live SMPP submit diagnostics with sanitized response and transport categories while preserving acknowledgement requirements.
- Added a guarded manual command for submitting one short GSM-compatible SMS through the confirmed YAS SMPP transceiver connection.
- Added typed SMPP address, submission request, and transport result objects with explicit provider-result mapping.
- Added safe, idempotent cleanup and sanitized operator output without expanding into persistence, queues, APIs, billing, tenancy, or delivery-receipt receiving.

## 1. Security Objectives

MHI Gateway must protect tenant data, provider secrets, wallet balances, delivery history and customer communications. The platform should be designed for multi-tenant isolation, least-privilege access, secure processing of sensitive data and strong operational resilience.

## 2. Core Security Principles

- Never log passwords, API keys, SMPP credentials, private signing keys or wallet secrets.
- Treat all tenant data as sensitive and require strict isolation.
- Enforce authorization at the service layer, not only in controllers.
- Encrypt secrets at rest and use secure transport for all integrations.
- Validate all inbound provider callbacks before processing them.
- Preserve integrity through immutable audit logs and tamper-evident records.

## 3. Authentication and Access Control

### 3.1 Public API authentication

The Public API must use authenticated credentials bound to a specific application and tenant.

Recommended controls:
- Bearer tokens for user-facing integrations
- Signed API keys for server-to-server integrations
- Mutual TLS where required for high-trust partners
- Per-credential scopes for sending, wallet queries, webhook management and reporting
- Short-lived access tokens with refresh rotation where applicable

### 3.2 Internal Admin UI authentication

The Internal Admin UI should use a separate authentication path from the Public API.

Recommended controls:
- Session-based authentication with secure cookie settings
- MFA for privileged roles such as Platform Admin, Tenant Admin and Finance
- Role-based access control enforced server-side
- Administrative actions should be isolated from public API traffic and use stronger session controls

## 4. Hashing vs Encryption

These are different controls and must be used for different purposes.

### Hashing
Use hashing for non-reversible verification.
- Passwords and password reset tokens
- One-way verification of webhook signatures or integrity fingerprints
- Deterministic token or reference comparison where reversibility is not required

### Encryption
Use encryption for reversible protection of sensitive data.
- Provider credentials and SMPP secrets
- Phone numbers and other PII
- Message content and attachments where retention requires confidentiality
- Database fields that store secrets or regulated customer data

### Cryptographic guidance
- Use strong modern algorithms such as AES-256-GCM for symmetric encryption
- Use Argon2id, bcrypt or scrypt for password hashing
- Never use plaintext storage for secrets, even temporarily
- Store decryption keys separately from the data and rotate them on a schedule

## 5. Development vs Production Secret Management

### Development environment
- .env files are acceptable for local development only
- Local secrets must never be committed to source control
- .env files should be excluded from version control and scanned for leakage
- Development credentials must be isolated from production systems

### Production environment
- Secrets must be managed by a dedicated secrets manager such as Vault, AWS Secrets Manager, Azure Key Vault or a similar platform
- Application configuration should load secrets at runtime from the secret manager
- Production secrets should be rotated regularly and support emergency rotation
- Access to production secrets must be limited to approved service identities

## 6. PII Protection

PII must be protected at collection, storage, processing and deletion.

### Required controls
- Encrypt phone numbers at rest and in transit
- Mask phone numbers in logs, admin screens and support workflows unless an explicit need exists
- Hash sensitive identifiers such as phone numbers for lookup or deduplication where reversibility is not required
- Apply retention rules to message content and attachments so data is deleted when no longer needed
- Restrict access to message bodies and PII to roles that require it

### Message retention
- Message bodies should be retained only for the minimum period required by policy or legal requirements
- Short-lived message content should be stored in encrypted form and purged automatically
- Retention schedules must be auditable and configurable per tenant where required

## 7. Immutable Audit Logs

Security-relevant events must be captured in immutable audit logs.

Requirements:
- Append-only storage with no in-place updates or deletes for audit records
- Timestamped events including actor, tenant, action, target, source IP and outcome
- Tamper-evident hashing or log chaining for integrity verification
- Separate storage from operational logs where possible
- Retention aligned with regulatory and internal policy requirements

Examples of audit events:
- authentication success or failure
- role changes
- wallet reservation, capture and reversal
- provider connection changes
- webhook verification failures
- secret rotation and access to secrets

## 8. API Security and Abuse Protection

The Public API and Internal Admin API must resist abuse and misuse.

### Rate limiting and abuse detection
- Enforce per-credential and per-tenant rate limits for read and write operations
- Apply burst protection and distributed throttling for public traffic
- Detect abnormal patterns such as repeated failures, suspicious IP ranges and high-volume retries
- Return structured rate-limit responses with retry guidance

### Replay-attack protection
- Require request timestamps for signed requests
- Use nonce values for replay-resistant signatures where supported
- Use idempotency keys for state-changing operations such as message submission and webhook registration
- Reject stale requests outside an approved validity window

### Request correlation and tracing
- Generate request IDs and correlation IDs for every incoming request
- Propagate them through logs, queue messages and downstream provider calls
- Use them to troubleshoot incidents and investigate abuse events

## 9. Queue Security

Queue workers and queued jobs must be protected as part of the trust boundary.

Required controls:
- Encrypt sensitive job payloads before enqueueing where needed
- Do not place secrets directly in queue payloads
- Use scoped credentials for workers and service identities
- Restrict queue access to authorized services only
- Serialize request and tenant context with each job
- Reject and quarantine malformed or suspicious jobs

## 10. SMPP Security

SMPP integrations require transport and session protections.

Required controls:
- Use persistent binds with heartbeat management
- Enable enquire_link health checks to detect dead sessions
- Implement reconnection logic with backoff and retry limits
- Redact credentials in logs, metrics and exception traces
- Validate bind credentials before activation
- Isolate provider-specific SMPP sessions from general application traffic

## 11. Role-Based Access Control

The platform should support the following roles:

- Platform Admin: full platform control, including tenant administration and critical configuration
- Tenant Admin: controls a single tenant’s applications, credentials and users
- Finance: accesses billing, wallet, reconciliation and financial audit data
- Messaging: manages message templates, channel routing and operational delivery workflows
- Support: reviews message state, customer issues and debugging data within scope
- Auditor: read-only access to security, compliance and immutable audit events
- Read Only: read-only access to non-sensitive operational data

### RBAC principles
- Default deny for all access paths
- Least-privilege role assignment
- Separation of duties between billing, provider management and message operations
- Privileged actions must require additional verification or approval where appropriate

## 12. Secure Billing Transactions

Billing and financial state changes must be atomic and auditable.

Required controls:
- Use immutable ledger entries for wallet movements
- Reserve funds before dispatch, capture funds on delivery or finalization and release funds on cancellation or failure
- Prevent double debit or double credit through transactional workflows and idempotent billing operations
- Keep billing state separate from message status state
- Record every financial transition in immutable audit logs
- Require authorization at the service layer for all wallet and balance mutations

## 13. Database and Storage Protection

### Encrypted database columns
Encrypt or tokenize sensitive columns such as:
- phone_number
- message_body where applicable
- provider_secret
- signing_secret
- smtp_password
- smpp_password
- webhook_secret
- payment_reference or tokenized billing identifiers

### Storage recommendations
- Use database-level encryption or application-level field encryption where appropriate
- Keep encryption keys separate from data stores
- Apply column-level encryption only where sensitive data is stored directly

## 14. Backup, Recovery and Encryption

Backups must be protected as carefully as production data.

Required controls:
- Encrypt backups at rest and in transit
- Store encryption keys separately from the backup repository
- Separate backup copies across at least two locations where possible
- Test restore procedures regularly
- Restrict backup access to authorized operators and service identities

## 15. Monitoring, Metrics and Alerts

Security monitoring should be continuous and actionable.

### Metrics
- authentication attempts and failures
- rate-limit events
- wallet reserve and capture failures
- webhook verification failures
- provider connection errors and reconnect counts
- queue backlog and dead-letter rates
- suspicious burst traffic or repeated retries
- encryption and secret-rotation health

### Alerts
- repeated authentication failures
- sudden spikes in message volume or spend
- failed billing transactions
- provider connection instability
- elevated queue age or dead-letter growth
- missing or failed backups
- unusual access from new IP ranges or geographies

## 16. HTTP Security Headers

All public and admin responses should include appropriate security headers.

Recommended headers:
- Strict-Transport-Security
- X-Content-Type-Options
- X-Frame-Options or Content-Security-Policy
- Referrer-Policy
- Permissions-Policy
- Cache-Control: no-store for sensitive responses

## 17. Production Hardening Checklist

Before production deployment, verify:

- tenant boundaries are enforced in all services
- wallet debit operations cannot exceed the available balance
- provider credentials are encrypted and inaccessible to unauthorized users
- webhook verification rejects unsigned traffic
- audit logs capture significant state changes
- secrets are managed outside source control and rotated on schedule
- queue workers use scoped credentials and do not expose secrets in payloads
- SMPP sessions reconnect safely and redact credentials in logs
- HTTP security headers are present on all relevant responses
- backups are encrypted and tested for restore
- monitoring and alerting cover high-risk security events

## 18. Standards Alignment

The platform should align with the following security frameworks and guidance:

- OWASP Top 10 for web application security
- OWASP API Security Top 10
- OWASP ASVS for secure application design and verification
- Relevant PCI, GDPR and regional privacy requirements where applicable

## 19. Disaster Recovery Goals

The platform should define recovery objectives for continuity and business resilience.

Recommended targets:
- RPO: 15 minutes for critical transactional data where feasible
- RTO: 1 hour for core messaging and billing services during a major incident
- RTO for full platform recovery: 4 hours or less depending on hosting and backup topology

## 20. Security Events Model

Critical security incidents should be captured in a dedicated security_events model.

Suggested fields:
- id
- tenant_id
- severity
- event_type
- title
- description
- source_ip
- actor_id
- detected_at
- status
- remediation_status
- correlation_id
- related_resource_type
- related_resource_id

Security events should be immutable, reviewable and linked to audit evidence where possible.
