> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pensionsportal.ie/llms.txt
> Use this file to discover all available pages before exploring further.

# Encryption at Rest and in Transit

> How PensionsPortal.ie protects sensitive data using TLS, database encryption, and AES-256 encryption for PPS numbers.

PensionsPortal.ie applies encryption at multiple layers: in transit between all parties, at rest in the database, and at the application layer for particularly sensitive personal data (PPS numbers).

## Encryption in Transit

### TLS

All connections to PensionsPortal.ie are encrypted using **TLS 1.2 or higher**. TLS 1.0 and 1.1 are not supported.

Cloudflare handles TLS termination at the edge, with a modern cipher suite and automatic certificate management. The origin connection from Cloudflare to Vercel is also encrypted.

### HSTS

HTTP Strict Transport Security is enforced via a response header set in `next.config.ts`:

```
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
```

This instructs browsers to:

* Only connect to the domain over HTTPS for 2 years (`max-age=63072000`)
* Apply the policy to all subdomains (`includeSubDomains`)
* Submit the domain for HSTS preload lists (`preload`)

See [Security Headers](/security/security-headers-csp-hsts) for the full header configuration.

### Database Connections

Connections from the application to Neon PostgreSQL use SSL/TLS. The Neon connection string includes SSL mode enforcement. Unencrypted database connections are not supported.

### Email

Transactional email is sent via Resend, which uses TLS for SMTP delivery (STARTTLS / SMTPS).

## Encryption at Rest

### Database (Neon PostgreSQL)

Neon PostgreSQL (hosted on AWS `eu-west-2`) provides **AES-256 encryption at rest** for all stored data at the storage layer. This is managed by the cloud provider and is transparent to the application.

### Vercel Blob Storage

Documents uploaded to `@vercel/blob` are stored with server-side encryption managed by Vercel's underlying cloud infrastructure.

## Application-Layer Encryption (PPS Numbers)

PPS (Personal Public Service) numbers are **encrypted at the application layer** before being written to the database. This provides an additional layer of protection beyond database-level encryption.

### Implementation

```ts theme={null}
// Members schema — src/db/schema/members.ts
ppsNumberEncrypted: text("pps_number_encrypted"),
```

The plaintext PPS number is never stored in the database. The encryption key is held only in the `PPS_ENCRYPTION_KEY` environment variable in Vercel.

### Key Specification

| Property    | Value                             |
| ----------- | --------------------------------- |
| Algorithm   | AES-256                           |
| Key size    | 256-bit (32 bytes)                |
| Key format  | 64-character hex string           |
| Key storage | Vercel environment variables only |

### Key Generation

```bash theme={null}
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```

### Key Management Rules

* The key is never logged, committed to code, or included in API responses
* Key rotation requires re-encrypting all stored PPS values — a planned maintenance operation
* Loss of the key means PPS values cannot be recovered — the key must be backed up securely outside the application (e.g., a secrets manager or offline vault)

<Warning>
  The `PPS_ENCRYPTION_KEY` must be backed up securely outside of Vercel. If Vercel access is lost and no backup exists, stored PPS numbers become permanently unrecoverable.
</Warning>

## GDPR Obligations

The GDPR requires that PPS numbers (a special category identifier under Irish data protection law) be protected by appropriate technical measures. Application-layer AES-256 encryption, combined with storage-level encryption and TLS in transit, satisfies this requirement.

The `members` schema comment notes: *"PPS number stored encrypted at application layer (P0.6 GDPR requirement). Never log, never include in Inngest payloads, never sync to HubSpot."*
