From 20cc3e4e49f6ad9d440c44231986ca118b1f0eb9 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Sat, 21 Mar 2026 15:25:41 -0700 Subject: [PATCH] feat(backend): admin-panel encryption toggle via initEncryption() - FIELD_ENCRYPT_ENABLED env var (default: true, fallback only) - initEncryption(productId) polls encryption_enabled from platform-service - Admin panel toggle takes precedence, 3s timeout graceful fallback --- backend/src/lib/config.ts | 1 + backend/src/lib/field-encrypt.ts | 30 ++++++++++++++++++++++++++++++ backend/src/server.ts | 3 +++ 3 files changed, 34 insertions(+) diff --git a/backend/src/lib/config.ts b/backend/src/lib/config.ts index 0d0bfa3..1b517e7 100644 --- a/backend/src/lib/config.ts +++ b/backend/src/lib/config.ts @@ -15,6 +15,7 @@ const envSchema = baseBackendConfigSchema.extend({ TELEMETRY_ENABLED: z.coerce.boolean().default(false), FEATURE_FLAGS_ENABLED: z.coerce.boolean().default(false), // ── Field Encryption (@bytelyst/field-encrypt) ── + FIELD_ENCRYPT_ENABLED: z.coerce.boolean().default(true), FIELD_ENCRYPT_KEY_PROVIDER: z.enum(['akv', 'env', 'memory']).default('memory'), FIELD_ENCRYPT_KEY: z.string().default(''), FIELD_ENCRYPT_MEK_NAME: z.string().default('notelett-mek'), diff --git a/backend/src/lib/field-encrypt.ts b/backend/src/lib/field-encrypt.ts index bf967f8..349d5d8 100644 --- a/backend/src/lib/field-encrypt.ts +++ b/backend/src/lib/field-encrypt.ts @@ -1,16 +1,45 @@ /** * Field encryption singleton for NoteLett backend. + * + * Toggle precedence: + * 1. Admin panel `encryption_enabled` flag (polled at startup via initEncryption) + * 2. FIELD_ENCRYPT_ENABLED env var (fallback if platform-service unreachable) */ import { createFieldEncryptor, type FieldEncryptor } from '@bytelyst/field-encrypt'; import { config } from './config.js'; let _encryptor: FieldEncryptor | null = null; +let _enabled: boolean = config.FIELD_ENCRYPT_ENABLED; + +/** Poll encryption_enabled flag from platform-service at startup. */ +export async function initEncryption(productId: string, logger?: { info: (msg: string) => void }): Promise { + const log = logger ?? { info: () => {} }; + try { + const url = `${config.PLATFORM_SERVICE_URL}/flags/poll?platform=backend`; + const res = await fetch(url, { + signal: AbortSignal.timeout(3000), + headers: { 'x-product-id': productId }, + }); + if (res.ok) { + const data = (await res.json()) as { flags: Record }; + if (typeof data.flags?.encryption_enabled === 'boolean') { + _enabled = data.flags.encryption_enabled; + log.info(`Encryption flag from admin panel: ${_enabled ? 'ON' : 'OFF'}`); + return; + } + } + } catch { + // platform-service unreachable — use env var + } + log.info(`Encryption from env var FIELD_ENCRYPT_ENABLED: ${_enabled ? 'ON' : 'OFF'}`); +} export function getEncryptor(): FieldEncryptor { if (_encryptor) return _encryptor; _encryptor = createFieldEncryptor({ + enabled: _enabled, keyProvider: config.FIELD_ENCRYPT_KEY_PROVIDER, encryptionKey: config.FIELD_ENCRYPT_KEY || undefined, keyVaultUrl: config.AZURE_KEYVAULT_URL || undefined, @@ -23,4 +52,5 @@ export function getEncryptor(): FieldEncryptor { /** @internal — for testing only. */ export function _resetEncryptor(): void { _encryptor = null; + _enabled = config.FIELD_ENCRYPT_ENABLED; } diff --git a/backend/src/server.ts b/backend/src/server.ts index 617a076..f20e8ce 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -8,6 +8,7 @@ import { noteTaskRoutes } from './modules/note-tasks/routes.js'; import { savedViewRoutes } from './modules/saved-views/routes.js'; import { workspaceRoutes } from './modules/workspaces/routes.js'; import { initCosmosIfNeeded } from './lib/cosmos-init.js'; +import { initEncryption } from './lib/field-encrypt.js'; import { initDatastore } from './lib/datastore.js'; import { config } from './lib/config.js'; import { getAllFlags } from './lib/feature-flags.js'; @@ -76,4 +77,6 @@ app.get('/api/diagnostics/config', async () => ({ featureFlagsEnabled: config.FEATURE_FLAGS_ENABLED, })); +await initEncryption(PRODUCT_ID, app.log); + await startService(app, { port: config.PORT, host: config.HOST });