Skip to content

PII Encryption

AMSDAL provides built-in field-level encryption for Personally Identifiable Information (PII). Mark sensitive fields with the PIIStr type, and AMSDAL automatically encrypts values before writing to the database and decrypts them on demand when reading.

Data is encrypted using AES-256-GCM with envelope encryption backed by AWS KMS — the same approach used by AWS services themselves.

from amsdal.models import Model, PIIStr
from pydantic import Field

class Customer(Model):
    name: str
    email: PIIStr = Field(title='email')
    phone: PIIStr | None = Field(default=None, title='phone')

With this definition:

  • email and phone are stored encrypted in the database
  • On read, they return encrypted ciphertext by default
  • To get plaintext values, explicitly request decryption via QuerySet or REST API

How It Works

┌─────────────┐      ┌──────────────────┐      ┌──────────────────┐
│  Your App   │ ──── │  AMSDAL          │ ──── │  PII Cryptor     │
│  (Model)    │      │  (Framework)     │      │  (Service)       │
└─────────────┘      └──────────────────┘      └──────────────────┘
                                                        │
                                                ┌───────┴────────┐
                                                │   AWS KMS      │
                                                └────────────────┘
  1. You define model fields using PIIStr
  2. On save, AMSDAL detects PII fields and sends their values to the PII Cryptor service for encryption before writing to the database
  3. On read, encrypted values are returned as-is unless you explicitly request decryption
  4. The PII Cryptor service uses envelope encryption: AWS KMS generates a data encryption key, the service encrypts values with AES-256-GCM, and only the encrypted key is stored alongside the ciphertext

This means plaintext PII never exists in your database — even if the database is compromised, the data remains protected.


Defining PII Fields

Use PIIStr as the field type for any string field containing sensitive data:

from amsdal.models import Model, PIIStr
from pydantic import Field

class User(Model):
    username: str
    email: PIIStr = Field(title='email')
    ssn: PIIStr | None = Field(default=None, title='ssn')

PIIStr is a regular str with metadata that tells AMSDAL to encrypt/decrypt it. You can use it anywhere you'd use str — required fields, optional fields, with defaults.

Detecting PII Fields Programmatically

from amsdal.models import get_pii_fields

fields = get_pii_fields(User)
# ['email', 'ssn']

Encryption and Decryption

Encryption — Automatic on Save

PII fields are encrypted automatically when saving objects. No extra code needed:

user = User(username='alice', email='alice@example.com', ssn='123-45-6789')
await User.objects.bulk_acreate([user])
# email and ssn are encrypted before hitting the database

In the database, the values look like:

#01#<encrypted_key>:<iv>:<ciphertext>:<tag>#10#

Decryption — Opt-in on Read

By default, PII fields return encrypted ciphertext. To get plaintext values, chain .decrypt_pii() on the QuerySet:

# Encrypted values (default)
users = await User.objects.aexecute()
print(users[0].email)  # #01#abc123:def456:ghi789:jkl000#10#

# Decrypted values
users = await User.objects.decrypt_pii().aexecute()
print(users[0].email)  # alice@example.com

This is intentional — decryption requires a call to the PII Cryptor service and ultimately to AWS KMS, so it only happens when you explicitly need the plaintext.


REST API

All object endpoints support the decrypt_pii query parameter:

GET /api/objects/?class_name=User&decrypt_pii=true
GET /api/objects/{address}/?decrypt_pii=true
POST /api/objects/?decrypt_pii=true
PATCH /api/objects/{address}/?decrypt_pii=true

When decrypt_pii=true, the response includes decrypted PII field values. When omitted or false, encrypted ciphertext is returned.


Configuration

AMSDAL Cloud

When deploying to AMSDAL Cloud, PII encryption works out of the box — no configuration needed. The AMSDAL_PII_CRYPTOR_BASE_URL and AMSDAL_PII_CRYPTOR_CLIENT_ID are set automatically during deployment.

By default, all deployed applications use shared encryption keys. If you need a dedicated KMS key for your application (e.g. for compliance or data isolation requirements), contact us and we'll provision one for you.

Self-Hosted / Local Development

When running outside AMSDAL Cloud, the PII Cryptor service connection is configured via environment variables:

Variable Description Default
AMSDAL_PII_CRYPTOR_BASE_URL URL of the PII Cryptor service ""
AMSDAL_PII_CRYPTOR_CLIENT_ID Client identifier (for per-client KMS keys) ""
# .env
AMSDAL_PII_CRYPTOR_BASE_URL=https://pii-cryptor.internal:8000
AMSDAL_PII_CRYPTOR_CLIENT_ID=my-app

When AMSDAL_PII_CRYPTOR_BASE_URL is empty, AMSDAL uses a fake crypto service that wraps values with #01#...#10# markers without real encryption. This is useful for local development without an AWS setup.


Security Architecture

Envelope Encryption

The PII Cryptor service implements envelope encryption:

  1. Encrypt: AWS KMS generates a unique Data Encryption Key (DEK) per request. Values are encrypted with AES-256-GCM using this DEK. The DEK itself is encrypted by KMS and stored alongside the ciphertext.
  2. Decrypt: The encrypted DEK is sent to KMS for decryption, then used to decrypt the values locally.

This means AWS KMS never sees your actual data — only the encryption keys.

AES-256-GCM

Each value is encrypted with:

  • AES-256 — 256-bit symmetric encryption
  • GCM mode — authenticated encryption that detects tampering
  • Random IV — unique initialization vector per value, even within the same request

Key Management

  • Encryption keys are managed by AWS KMS — you never handle raw key material
  • Supports per-client KMS keys for multi-tenant isolation via the X-CLIENT-ID header
  • AWS KMS handles automatic key rotation (every 365 days), retaining old key versions for decryption

What's Protected

Layer Protection
Database PII stored as encrypted ciphertext
Application Decryption only happens on explicit request
Transport HTTPS between service and KMS
Key storage Keys managed by AWS KMS, never stored in plaintext