Cryptography, Field Encryption & Secrets
Relevant source files
The following files were used as context for generating this wiki page:
- src/forze/application/contracts/crypto/cipher.py
- src/forze/application/contracts/secrets/ports.py
- src/forze/base/crypto/aead.py
- tests/unit/test_forze_postgres/test_document_encryption.py
- tests/unit/test_forze/application/integrations/crypto/test_document_wiring.py
- skills/forze-auth-tenancy-secrets/SKILL.md
Forze provides a structured approach to securing sensitive data through high-level abstractions for cryptography, application-level field encryption, and secret management. The system is designed to decouple business logic from specific cryptographic implementations, allowing for pluggable backends such as HashiCorp Vault, AWS KMS, or local environment-based secrets.
The cryptography module focuses on Authenticated Encryption with Associated Data (AEAD), ensuring both confidentiality and integrity of protected fields. Field encryption is integrated directly into the document persistence pipeline, enabling automatic encryption/decryption of specific fields without manual intervention in handlers.
Architecture and Core Components
The system is built around several key ports and contracts that define how cryptographic operations and secret resolutions are performed.
Cryptographic Primitives
At the base level, Forze utilizes AEAD (Authenticated Encryption with Associated Data) to handle encryption. This ensures that any tampered ciphertext will fail decryption, providing a layer of integrity.
The diagram shows the relationship between the AEAD protocol, its implementation, and the Keyring which orchestrates cryptographic dependencies.
Sources: src/forze/base/crypto/aead.py, tests/unit/test_forze/application/integrations/crypto/test_document_wiring.py:34-40
Key Management and Resolution
Cryptographic operations require keys, which are managed through KeyManagementPort and resolved via a Keyring. Forze supports both standard encryption (randomized) and deterministic encryption (required for searchable encrypted fields).
| Component | Description |
|---|---|
Keyring | A container for KMS, AEAD, and Key Directory ports used for standard encryption. |
KeyRef | A reference to a specific key, typically containing a key_id. |
DeterministicFieldCipher | Used when encrypted fields must remain searchable by producing the same ciphertext for the same plaintext. |
CryptoDepsModule | A dependency module that wires KMS and Key Directories into the execution context. |
Sources: tests/unit/test_forze/application/integrations/crypto/test_document_wiring.py:34-46, tests/unit/test_forze_postgres/test_document_encryption.py:44-50
Field Encryption Workflow
Field encryption is implemented as a wrapper around model codecs. When a DocumentSpec marks specific fields as encrypted, the system injects an EncryptingModelCodec into the persistence ports.
Encryption Wiring Logic
The wiring process validates that the necessary cryptographic infrastructure is available before enabling field encryption.
This flow represents the safety checks performed during the resolution of document codecs to ensure data is not silently left unencrypted.
Sources: tests/unit/test_forze/application/integrations/crypto/test_document_wiring.py:68-125, tests/unit/test_forze_postgres/test_document_encryption.py:61-75
Implementation Details
When encrypted_fields are defined in a DocumentSpec, the following happens:
- Codec Wrapping: The standard model codecs (read, domain, create, update) are wrapped in an
EncryptingModelCodec. - Transparent Operations: Handlers continue to work with plain domain models. The encryption happens during the serialization/deserialization phase within the adapter.
- Fail-Closed Security: If a spec requires encryption but no
CryptoDepsModule(Keyring) is provided, the system raises aCoreExceptionwithExceptionKind.CONFIGURATION.
Sources: tests/unit/test_forze_postgres/test_document_encryption.py:78-95, src/forze/application/contracts/crypto/cipher.py
Secret Management
The SecretsPort provides a unified interface for resolving application secrets and sensitive configuration.
Secret Resolution
Secrets are referenced using a SecretRef (a logical path) and can be resolved into structured data models using Pydantic.
The sequence illustrates how secrets are retrieved and transformed into typed objects.
Sources: src/forze/application/contracts/secrets/ports.py, skills/forze-auth-tenancy-secrets/SKILL.md:183-190
Supported Backends
Forze supports multiple backends for secret storage, configured via SecretsDepsModule:
- EnvSecrets: Resolves secrets from environment variables.
- DirectorySecrets: Reads secrets from files in a specific root directory (e.g., K8s Secret volumes).
- MappingSecrets: In-memory map, primarily used for development and testing.
- Vault (forze_vault): Integration with HashiCorp Vault KV v2.
Sources: skills/forze-auth-tenancy-secrets/SKILL.md:193-210
Security Guidelines and Anti-Patterns
To maintain the security posture of the application, several architectural constraints are enforced:
- Boundary Binding: Identity and tenancy information should be bound at the entry points (FastAPI middleware, Workers) and only read from the
ExecutionContextwithin handlers. - No Raw Keys in Specs:
DocumentSpecorStorageSpecshould never contain actual secret values or keys; they should only contain logical names or references. - Deterministic vs. Randomized: Use randomized encryption for maximum security unless a field must be searchable, in which case a
DeterministicFieldCiphermust be explicitly configured. - Fail-Closed Configuration: The system is designed to fail startup or operation if an encryption requirement is specified but the supporting infrastructure (KMS/Keyring) is missing.
Sources: skills/forze-auth-tenancy-secrets/SKILL.md:223-238, tests/unit/test_forze/application/integrations/crypto/test_document_wiring.py:101-110
Summary
The "Cryptography, Field Encryption & Secrets" system provides a robust framework for handling sensitive information. By utilizing AEAD-based field encryption and a flexible secret resolution port, Forze ensures that security is an integrated part of the data lifecycle rather than an afterthought. The architecture promotes a "fail-closed" philosophy, preventing the accidental exposure of sensitive data due to misconfiguration.