36 lines
1.7 KiB
Plaintext
36 lines
1.7 KiB
Plaintext
// R-P5 (P5) — per-tenant data-encryption-key store for AD-8 envelope encryption.
|
|
//
|
|
// AD-21: one self-contained schema file per module. `tenantId` is a BARE String
|
|
// scalar (no `@relation`) so this file has zero merge-conflict surface against
|
|
// `core.prisma`/`accounts.prisma` (no back-relation field on Tenant needed).
|
|
//
|
|
// AD-8 (revised by EXT-06: app-side envelope, no cloud-KMS): each tenant has ONE
|
|
// random Data-Encryption-Key (DEK). The DEK is never stored in the clear — only
|
|
// WRAPPED with the app master key (the `KeyWrapper` in
|
|
// packages/foundation/src/crypto/key-wrapper.ts; today EnvKeyWrapper over
|
|
// CRYPTO_SECRET, later swappable for self-hosted Vault/OpenBao WITHOUT
|
|
// re-encrypting any field). Sensitive fields across modules (HR password safe,
|
|
// HR emergency contact, R-P5 OAuth/API-key credentials) are sealed with the
|
|
// tenant DEK.
|
|
//
|
|
// Crypto-shredding (M165 / AK-P21): deleting a tenant's row here makes ALL of
|
|
// that tenant's envelope ciphertext permanently unreadable. The orchestration
|
|
// (load/create/destroy + in-memory unwrapped-DEK cache) lives in
|
|
// packages/db/src/crypto/tenant-envelope.server.ts.
|
|
|
|
/// One wrapped Data-Encryption-Key per tenant. `wrappedDek` is the tenant DEK
|
|
/// encrypted with the app master key (never the plaintext DEK). `keyVersion`
|
|
/// allows rotating the wrapping scheme without touching field ciphertext.
|
|
model TenantEncryptionKey {
|
|
id String @id @default(cuid())
|
|
createdAt DateTime @default(now())
|
|
updatedAt DateTime @updatedAt
|
|
tenantId String @unique
|
|
// The tenant DEK, wrapped (encrypted) with the app master key. Opaque blob;
|
|
// useless without the master key, so a DB leak alone exposes nothing.
|
|
wrappedDek Bytes
|
|
keyVersion Int @default(1)
|
|
|
|
@@index([tenantId])
|
|
}
|