Documentation
13. Encryption
Encrypted columns, key handling, masked exports, decrypt=True and blind indexes.
Encryption goal
Encrypted columns are encrypted before storage. RelPyDB stores ciphertext in memory and in persistence files. Public exports mask encrypted values unless decrypt=True.
Key rule: the encryption key is never saved inside the RelPyDB JSON file.
Functions and flags
| Item | Usage | Meaning |
|---|---|---|
RelPy.generate_encryption_key() | key = RelPy.generate_encryption_key() | Creates a key. |
RelPy(encryption_key=key) | constructor | Creates DB with key loaded. |
set_encryption_key(key) | after load | Attach key later. |
is_encrypted=True | add_column(...) | Encrypt this column on insert/update. |
decrypt=True | exports/query results | Return plaintext if key is loaded. |
Complete example
key = RelPy.generate_encryption_key()
db = RelPy(encryption_key=key)
db.create_table("users")
db.add_column("users", "id", AutoNumber, is_primary_key=True)
db.add_column("users", "email", str, nullable=False, is_encrypted=True)
db.insert("users", {"email": "alice@example.com"})
db.to_list("users")
# [{"id": 1, "email": "[ENCRYPTED]"}]
db.to_list("users", decrypt=True)
# [{"id": 1, "email": "alice@example.com"}]Blind indexes
Encrypted equality search uses a blind index. RelPyDB stores a keyed HMAC-like lookup value next to the ciphertext. This allows equality lookup without indexing plaintext.
db.create_index("users", "email")
rows = (
db.query("users")
.where(col("email") == "alice@example.com")
.to_list(decrypt=True)
)| Operation | Support | Reason |
|---|---|---|
== | Supported with blind index | Stable keyed lookup. |
in_ | Possible by equality matching | Each candidate can be checked. |
LIKE | Requires decrypt scan | Blind index does not preserve substrings. |
BETWEEN | Requires decrypt scan | Blind index does not preserve ordering. |
ORDER BY | Requires plaintext at runtime | Encrypted values are not order-preserving. |
Save/load with encryption
db.save("users.relpy.json")
loaded = RelPy.load("users.relpy.json")
loaded.to_list("users")
# masked values
loaded.set_encryption_key(key)
loaded.to_list("users", decrypt=True)
# plaintext valuesYou can also pass the key during load:
loaded = RelPy.load("users.relpy.json", encryption_key=key)Encryption and exports — behaviour summary
| Situation | to_list() default | to_list(decrypt=True) |
|---|---|---|
| No key loaded | [ENCRYPTED] | Raises EncryptionError |
| Key loaded | [ENCRYPTED] | Plaintext value |
| Loaded from file, key passed | [ENCRYPTED] | Plaintext value |
| Equality query on encrypted col | Works via blind index — no key needed for reads | |