Skip to main content

Payload Security

This topic explains why Framework M encrypts request payloads at the application layer and how the different modes trade off security, performance, and developer experience.

Why Encrypt at the Application Layer?

HTTPS protects data from passive network eavesdropping, but the request body is still visible to many components inside the request path:

  • Reverse proxies and load balancers that log bodies for debugging.
  • Web Application Firewalls (WAFs) that inspect content.
  • APM and observability tools that capture request traces.
  • Ingress controllers and sidecars.
  • Any compromised component between TLS termination and the application.

Application-Layer Payload Encryption (ALPE) ensures that only the client and the application can read the payload. Even if an intermediate component captures the request, it sees an opaque JWE envelope rather than cleartext passwords, emails, or PII.

The Four Modes

Framework M does not force a single policy. It offers a spectrum so the same codebase can run in local development, standard SaaS, and high-security production environments.

disabled

No encryption. The frontend sends Content-Type: application/json and the middleware passes requests through unchanged. This is the default for local development and CI because it keeps debugging simple and avoids extra key-fetching network calls.

selective

Encrypts a configured list of routes. By default this covers authentication endpoints where passwords and OTP codes are exchanged. It provides a pragmatic balance: sensitive authentication payloads are protected without encrypting every API call.

metadata

Encrypts fields flagged in DocType schemas. This is useful when only a subset of fields in a form are sensitive. For example, a patient record might encrypt ssn and email while leaving diagnosis_code in cleartext. The Desk form controller inspects the DocType schema and decides whether to encrypt the submission based on the flagged fields.

strict

Encrypts every mutating request (POST, PUT, PATCH). This is the highest-security mode, appropriate for fintech or zero-trust deployments where any write could contain sensitive data.

Key Exchange and Zero-Latency Design

Framework M avoids per-request handshakes, which would double latency. Instead:

  1. On app boot, the frontend fetches /api/v1/security/config once.
  2. If encryption is enabled, it lazily fetches /api/v1/security/jwks once and caches the public encryption key.
  3. Every subsequent eligible request uses window.crypto.subtle to generate an ephemeral AES key, encrypt the payload, wrap the AES key with the cached RSA public key, and send a JWE envelope.

The extra network cost is zero after the initial key fetch, and the cryptographic overhead is typically under one millisecond on modern browsers.

Middleware Placement

PayloadDecryptionMiddleware runs at priority 15, after rate limiting and before CSRF, idempotency, and authentication. This placement is intentional:

  • Rate limiting still sees request metadata (path, headers) and can drop abuse early.
  • CSRF and idempotency run on the decrypted body, so they work with standard JSON.
  • AuthMiddleware receives a normal LoginRequest DTO, so authentication handlers do not need decryption logic.

The OpenAPI Constraint

Field-level flags use standard Pydantic json_schema_extra, but Litestar's OpenAPI generator validates every extra key against its internal Schema object. Unknown keys cause OpenAPI generation to fail. Therefore:

  • DocType models can safely use json_schema_extra={"encrypted": True, "pii": True} because their schemas are produced by Pydantic and served by the metadata router, not by Litestar's OpenAPI generator.
  • Litestar request/response DTOs must not carry these flags. Auth routes rely on selective mode route configuration instead.

This separation keeps the feature simple and avoids custom Pydantic subclasses while respecting Litestar's OpenAPI constraints.

When to Use Each Mode

  • Local development / CI: disabled
  • Standard SaaS with password login: selective
  • Apps with scattered PII fields in Desk forms: metadata
  • Fintech, healthcare, zero-trust: strict