Cryptography & Secrets Management
Relevant source files
The following files were used as context for generating this wiki page:
Cryptography and secrets management within the AI Code Reviewer project ensure the security of sensitive data, such as LLM API keys and GitHub App credentials, when stored at rest. The system utilizes authenticated symmetric encryption to protect these secrets, ensuring that plaintext credentials exist only in memory during the execution of requests that require them.
Sources: packages/crypto/src/encryption.ts:1-10, README.md:121-125
The architecture prioritizes the principle that plaintext secrets are never logged, persisted, or transmitted unencrypted. A dedicated EncryptionService handles the lifecycle of encrypted data, including key rotation through versioning.
Sources: packages/crypto/README.md:20-25
Encryption Architecture
The core of the system is the EncryptionService located in @repo/crypto. It implements the AES-256-GCM (Galois/Counter Mode) algorithm, which provides both confidentiality and authenticity.
Algorithm Specifications
- Cipher: AES-256-GCM.
- Master Key: A 32-byte key provided via the
ENCRYPTION_KEYenvironment variable. - Initialization Vector (IV): A 12-byte random buffer generated per encryption.
- Authentication Tag: A 16-byte tag used to verify data integrity during decryption.
Sources: packages/crypto/src/encryption.ts:24-34, packages/crypto/README.md:6-12
Data Layout
Encrypted data is stored as a Buffer with a specific layout to facilitate versioning and decryption:
| Component | Size | Description |
|---|---|---|
keyVersion | 1 Byte | Identifies which master key version was used. |
iv | 12 Bytes | Random Initialization Vector. |
authTag | 16 Bytes | GCM integrity check tag. |
ciphertext | N Bytes | The actual encrypted data. |
Sources: packages/crypto/src/encryption.ts:14-22, packages/crypto/README.md:14-18
Encryption Flow
The following diagram illustrates the process of encrypting a plaintext secret before storage.
The envelope construction ensures all metadata required for decryption is bundled with the ciphertext.
Sources: packages/crypto/src/encryption.ts:87-106
Secrets Lifecycle & Storage
Secrets are primarily stored in the ProviderCredential table in the database. This table holds encrypted API keys for LLM providers (e.g., OpenAI, Anthropic) and embedding providers.
Database Schema for Secrets
The ProviderCredential model explicitly defines fields for managing encrypted content and its associated version.
model ProviderCredential {
id String @id @default(cuid())
installationId String
kind ProviderCredentialKind
providerKind String
encryptedSecret Bytes // AES-256-GCM ciphertext
encryptionKeyVersion Int @default(1)
active Boolean @default(true)
updatedAt DateTime @updatedAt
}
Sources: packages/db/prisma/schema.prisma:218-243
Key Rotation Strategy
The system supports non-disruptive master key rotation through a multi-stage process:
- Stage 1: Configure a new
activeVersionand key. The service uses the new key for encryptions while retaining older keys in alegacyKeysmap for decryption. - Stage 2: Run a migration job to re-encrypt old database rows using the latest version.
- Stage 3: Remove the old master key from the environment.
Sources: packages/crypto/src/encryption.ts:16-21, packages/crypto/src/encryption.ts:60-78
GitHub App Security
The project manages GitHub App security through RSA private keys and HMAC signature verification.
Authentication
The GitHubAppClient uses an RSA Private Key (supplied as GITHUB_APP_PRIVATE_KEY) to authenticate as a GitHub App and generate short-lived installation tokens.
Sources: README.md:215-225, packages/github/src/app-client.ts (referenced from README.md:145-147)
Webhook Verification
Incoming webhooks from GitHub are validated using a shared secret (GITHUB_WEBHOOK_SECRET). The system calculates an HMAC-SHA256 signature of the payload and compares it to the x-hub-signature-256 header using timingSafeEqual to prevent timing attacks.
Sources: README.md:148-150, README.md:210-213
Security Safeguards
To prevent accidental exposure, several safeguards are implemented:
- Redaction: The
pinologger is configured to redact fields such asapiKey,encryptedSecret,Authorization,secret,password, andtoken. - Self-Test: The
EncryptionServiceincludes aselfTest()method that performs a roundtrip encryption/decryption check at boot time to ensure the master key is valid. - Input Validation: The service refuses to encrypt empty plaintext to catch configuration or programming errors early.
Sources: packages/crypto/src/encryption.ts:133-145, packages/crypto/README.md:20-25
Summary Table: Security Constraints
| Category | Requirement | Source |
|---|---|---|
| At Rest | All secrets (API keys) MUST be encrypted using AES-256-GCM. | crypto/README.md |
| In Transit | Secrets are never transmitted unencrypted; usage is ephemeral in memory. | crypto/src/encryption.ts |
| Observability | Sensitive fields must be redacted in logs. | crypto/README.md |
| Verification | Webhook signatures must use timingSafeEqual. | README.md |
Sources: packages/crypto/README.md:20-25, packages/crypto/src/encryption.ts:1-10, README.md:210-213