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 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
// 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
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)
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.
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.”