From 60895bc306dc691940f0b64609f4381de81d8f14 Mon Sep 17 00:00:00 2001 From: Daniel Naab Date: Mon, 6 Oct 2025 11:10:39 -0500 Subject: [PATCH] Generalize secrets prefix for Flexion sandbox --- apps/cli/src/cli-controller/secrets.ts | 21 ++++++++-------- apps/sandbox/package.json | 2 +- apps/sandbox/src/index.ts | 18 ++++---------- infra/cdktf/src/spaces/aws/demo.ts | 2 +- infra/cdktf/src/spaces/aws/main.ts | 2 +- .../commands/set-login-gov-secrets.test.ts | 19 +++++++++------ .../src/commands/set-login-gov-secrets.ts | 10 +++++--- infra/core/src/lib/index.ts | 1 + infra/core/src/lib/secrets/index.ts | 24 +++++++++---------- infra/core/src/values.ts | 12 +++++----- .../__tests__/doj-pardon-marijuana.test.ts | 5 +++- .../documents/pdf/adapters/bedrock-parser.ts | 1 - packages/forms/src/llm/cache/hash.ts | 8 +++++-- .../forms/src/llm/services/generate-object.ts | 7 +++--- packages/forms/src/services/index.ts | 5 +++- 15 files changed, 73 insertions(+), 64 deletions(-) diff --git a/apps/cli/src/cli-controller/secrets.ts b/apps/cli/src/cli-controller/secrets.ts index c3b06a1f..7901b778 100644 --- a/apps/cli/src/cli-controller/secrets.ts +++ b/apps/cli/src/cli-controller/secrets.ts @@ -2,11 +2,7 @@ import { promises as fs } from 'fs'; import path from 'path'; import { Command } from 'commander'; -import { - type DeployEnv, - commands, - getSecretsVault, -} from '@flexion/forms-infra-core'; +import { commands, getSecretsVault } from '@flexion/forms-infra-core'; import { type Context } from './types.js'; export const addSecretCommands = (ctx: Context, cli: Command) => { @@ -79,17 +75,20 @@ export const addSecretCommands = (ctx: Context, cli: Command) => { .command('set-login-gov-keys') .description( 'generate and save login.gov keypair; if it already exists, it is not ' + - 'updated (future work might include adding key rotation)', + 'updated (future work might include adding key rotation)' ) - .argument('', 'deployment environment (dev, demo)') - .argument('', 'application key') - .action(async (env: DeployEnv, appKey: string) => { + .argument( + '', + 'root key for secrets (e.g., flexion-forms-demo, tts-10x-forms-dev)' + ) + .argument('', 'application key (e.g., server-doj, server-kansas)') + .action(async (rootKey: string, appKey: string) => { const vault = await getSecretsVault(ctx.file); const secretsDir = path.resolve(__dirname, '../../../infra/secrets'); const loginResult = await commands.setLoginGovSecrets( { vault, secretsDir }, - env, - appKey, + rootKey, + appKey ); if (loginResult.preexisting) { console.log('Keypair already exists.'); diff --git a/apps/sandbox/package.json b/apps/sandbox/package.json index e8f02c8c..c90b230f 100644 --- a/apps/sandbox/package.json +++ b/apps/sandbox/package.json @@ -13,7 +13,7 @@ "build": "tsup src/* --format esm", "clean": "rimraf dist tsconfig.tsbuildinfo coverage", "dev": "tsup src/* --watch --format esm", - "start": "VCAP_SERVICES='{\"aws-rds\": [{ \"credentials\": { \"uri\": \"\" }}]}' node dist/index.js", + "start": "node dist/index.js", "test": "vitest run --coverage" }, "dependencies": { diff --git a/apps/sandbox/src/index.ts b/apps/sandbox/src/index.ts index 75e360b3..43900967 100644 --- a/apps/sandbox/src/index.ts +++ b/apps/sandbox/src/index.ts @@ -5,17 +5,6 @@ import { createCustomServer } from './server.js'; const port = process.env.PORT || 4321; -const getCloudGovServerSecrets = () => { - if (process.env.VCAP_SERVICES === undefined) { - return; - } - const services = JSON.parse(process.env.VCAP_SERVICES || '{}'); - return { - //loginGovClientSecret: services['user-provided']?.credentials?.SECRET_LOGIN_GOV_PRIVATE_KEY, - dbUri: services['aws-rds'][0].credentials.uri as string, - }; -}; - const getAppRunnerSecrets = async () => { const dbSecretArn = process.env.DB_SECRET_ARN; const dbHost = process.env.DB_HOST; @@ -23,6 +12,9 @@ const getAppRunnerSecrets = async () => { const dbName = process.env.DB_NAME; if (!dbSecretArn || !dbHost || !dbPort || !dbName) { + console.error( + 'Missing required environment variables: DB_SECRET_ARN, DB_HOST, DB_PORT, DB_NAME' + ); return; } @@ -35,11 +27,11 @@ const getAppRunnerSecrets = async () => { const dbSecret = JSON.parse(dbSecretString); return { - dbUri: `postgresql://${dbSecret.username}:${dbSecret.password}@${dbHost}:${dbPort}/${dbName}` + dbUri: `postgresql://${dbSecret.username}:${dbSecret.password}@${dbHost}:${dbPort}/${dbName}`, }; }; -const secrets = getCloudGovServerSecrets() || (await getAppRunnerSecrets()); +const secrets = await getAppRunnerSecrets(); if (secrets === undefined) { console.error('Error getting secrets'); process.exit(1); diff --git a/infra/cdktf/src/spaces/aws/demo.ts b/infra/cdktf/src/spaces/aws/demo.ts index 0cfc0195..4a4cdd50 100644 --- a/infra/cdktf/src/spaces/aws/demo.ts +++ b/infra/cdktf/src/spaces/aws/demo.ts @@ -18,7 +18,7 @@ class AwsDemoStack extends TerraformStack { // Create the sandbox infrastructure new SandboxStack(this, stackName, { - environment: 'demo-aws', + environment: 'flexion-forms-demo', }); } } diff --git a/infra/cdktf/src/spaces/aws/main.ts b/infra/cdktf/src/spaces/aws/main.ts index 5914decd..b6d173f3 100644 --- a/infra/cdktf/src/spaces/aws/main.ts +++ b/infra/cdktf/src/spaces/aws/main.ts @@ -18,7 +18,7 @@ class AwsMainStack extends TerraformStack { // Create the sandbox infrastructure new SandboxStack(this, stackName, { - environment: 'main-aws', + environment: 'flexion-forms-main', }); } } diff --git a/infra/core/src/commands/set-login-gov-secrets.test.ts b/infra/core/src/commands/set-login-gov-secrets.test.ts index 4534336b..5db602ff 100644 --- a/infra/core/src/commands/set-login-gov-secrets.test.ts +++ b/infra/core/src/commands/set-login-gov-secrets.test.ts @@ -25,13 +25,18 @@ describe('set-login-gov-secrets command', () => { }), }; const appKey = randomUUID(); - const result = await setLoginGovSecrets(context, 'dev', appKey); + const result = await setLoginGovSecrets( + context, + 'flexion-forms-dev', + appKey + ); expect(result.preexisting).toEqual(false); expect( await context.vault.getSecrets(await context.vault.getSecretKeys()) ).toEqual({ - [`/tts-10x-forms-dev/${appKey}/login.gov/public-key`]: 'mock public key', - [`/tts-10x-forms-dev/${appKey}/login.gov/private-key`]: 'mock private key', + [`/flexion-forms-dev/${appKey}/login.gov/public-key`]: 'mock public key', + [`/flexion-forms-dev/${appKey}/login.gov/private-key`]: + 'mock private key', }); }); @@ -50,7 +55,7 @@ describe('set-login-gov-secrets command', () => { privateKey: 'mock private key - 1', }), }, - 'dev', + 'flexion-forms-dev', appKey ); const secondResult = await setLoginGovSecrets( @@ -61,7 +66,7 @@ describe('set-login-gov-secrets command', () => { privateKey: 'mock private key - 2', }), }, - 'dev', + 'flexion-forms-dev', appKey ); @@ -69,9 +74,9 @@ describe('set-login-gov-secrets command', () => { expect( await context.vault.getSecrets(await context.vault.getSecretKeys()) ).toEqual({ - [`/tts-10x-forms-dev/${appKey}/login.gov/public-key`]: + [`/flexion-forms-dev/${appKey}/login.gov/public-key`]: 'mock public key - 1', - [`/tts-10x-forms-dev/${appKey}/login.gov/private-key`]: + [`/flexion-forms-dev/${appKey}/login.gov/private-key`]: 'mock private key - 1', }); }); diff --git a/infra/core/src/commands/set-login-gov-secrets.ts b/infra/core/src/commands/set-login-gov-secrets.ts index afd95237..7cd3e7f1 100644 --- a/infra/core/src/commands/set-login-gov-secrets.ts +++ b/infra/core/src/commands/set-login-gov-secrets.ts @@ -3,7 +3,7 @@ import { promises as fs } from 'fs'; import { promisify } from 'util'; import { type SecretsVault } from '../lib/types.js'; -import { type DeployEnv, getAppLoginGovKeys } from '../values.js'; +import { getAppLoginGovKeys } from '../values.js'; const execPromise = promisify(exec); @@ -24,13 +24,17 @@ type Context = { /** * Sets or retrieves Login.gov secrets for the given application key. It retrieves and returns the * existing key pair or generates, stores, and returns new key pair if one didn't exist previously. + * + * @param ctx Context with vault and secrets directory + * @param rootKey The root key for secrets (e.g., 'flexion-forms-demo', 'tts-10x-forms-dev') + * @param appKey The application key (e.g., 'server-doj', 'server-kansas') */ export const setLoginGovSecrets = async ( ctx: Context, - env: DeployEnv, + rootKey: string, appKey: string ) => { - const loginKeys = getAppLoginGovKeys(env, appKey); + const loginKeys = getAppLoginGovKeys(rootKey, appKey); // If the keypair is already set, do nothing and return it. const existingPublicKey = await ctx.vault.getSecret(loginKeys.publicKey); diff --git a/infra/core/src/lib/index.ts b/infra/core/src/lib/index.ts index 8702eb7a..00946a3a 100644 --- a/infra/core/src/lib/index.ts +++ b/infra/core/src/lib/index.ts @@ -2,6 +2,7 @@ import { type SecretMap, type SecretsVault } from './types.js'; export { getSecretMapFromJsonString } from './types.js'; export * from './adapters/index.js'; +export * from './secrets/index.js'; export const getSecretMap = async (vault: SecretsVault): Promise => { const secretKeys = await vault.getSecretKeys(); diff --git a/infra/core/src/lib/secrets/index.ts b/infra/core/src/lib/secrets/index.ts index 48a80085..7a7b8981 100644 --- a/infra/core/src/lib/secrets/index.ts +++ b/infra/core/src/lib/secrets/index.ts @@ -1,15 +1,13 @@ -export const getSecretKeys = (env: string) => [ - `/tts-10x-forms-${env}/cloudfoundry/password`, - `/tts-10x-forms-${env}/cloudfoundry/username`, - `/tts-10x-forms-${env}/server-doj/leidos-intranet-quorum/password`, - `/tts-10x-forms-${env}/server-doj/leidos-intranet-quorum/username`, - `/tts-10x-forms-${env}/server-doj/login.gov/private-key`, - `/tts-10x-forms-${env}/server-doj/login.gov/public-key`, - `/tts-10x-forms-${env}/server-kansas/login.gov/private-key`, - `/tts-10x-forms-${env}/server-kansas/login.gov/public-key`, +export const getSecretKeys = (rootKey: string) => [ + `/${rootKey}/cloudfoundry/password`, + `/${rootKey}/cloudfoundry/username`, + `/${rootKey}/server-doj/leidos-intranet-quorum/password`, + `/${rootKey}/server-doj/leidos-intranet-quorum/username`, + `/${rootKey}/server-doj/login.gov/private-key`, + `/${rootKey}/server-doj/login.gov/public-key`, + `/${rootKey}/server-kansas/login.gov/private-key`, + `/${rootKey}/server-kansas/login.gov/public-key`, + `/${rootKey}/database`, ]; -const secretPrefix = (env: string) => `/tts-10x-forms-${env}`; - -export const getDatabaseSecretKey = (env: string) => - `${secretPrefix(env)}/database`; +export const getDatabaseSecretKey = (rootKey: string) => `/${rootKey}/database`; diff --git a/infra/core/src/values.ts b/infra/core/src/values.ts index 725203e8..b86fe934 100644 --- a/infra/core/src/values.ts +++ b/infra/core/src/values.ts @@ -1,16 +1,16 @@ export type DeployEnv = 'dev' | 'demo'; -const getPathPrefix = (env: DeployEnv) => `/tts-10x-forms-${env}`; - /** * Generates an object containing the paths for private/public keys pairs * associated with login.gov for an application in the specified * deployment environment. + * + * @param rootKey The root key for secrets (e.g., 'flexion-forms-demo', 'tts-10x-forms-dev') + * @param appKey The application key (e.g., 'server-doj', 'server-kansas') */ -export const getAppLoginGovKeys = (env: DeployEnv, appKey: string) => { - const prefix = getPathPrefix(env); +export const getAppLoginGovKeys = (rootKey: string, appKey: string) => { return { - privateKey: `${prefix}/${appKey}/login.gov/private-key`, - publicKey: `${prefix}/${appKey}/login.gov/public-key`, + privateKey: `/${rootKey}/${appKey}/login.gov/private-key`, + publicKey: `/${rootKey}/${appKey}/login.gov/public-key`, }; }; diff --git a/packages/forms/src/documents/__tests__/doj-pardon-marijuana.test.ts b/packages/forms/src/documents/__tests__/doj-pardon-marijuana.test.ts index d8ec9d74..c365c8ec 100644 --- a/packages/forms/src/documents/__tests__/doj-pardon-marijuana.test.ts +++ b/packages/forms/src/documents/__tests__/doj-pardon-marijuana.test.ts @@ -94,7 +94,10 @@ describe('DOJ Pardon Attorney Office - Marijuana pardon application form', () => const { createTestPdfParser } = await import('../pdf/index.js'); const { defaultFormConfig } = await import('../../patterns/index.js'); const parser = createTestPdfParser(); - const result = await parsePdf({ parser, formConfig: defaultFormConfig }, pdfBytes); + const result = await parsePdf( + { parser, formConfig: defaultFormConfig }, + pdfBytes + ); const { parsedPdf, fields } = result; // Should create valid pattern structure diff --git a/packages/forms/src/documents/pdf/adapters/bedrock-parser.ts b/packages/forms/src/documents/pdf/adapters/bedrock-parser.ts index 30db248f..ee391381 100644 --- a/packages/forms/src/documents/pdf/adapters/bedrock-parser.ts +++ b/packages/forms/src/documents/pdf/adapters/bedrock-parser.ts @@ -9,7 +9,6 @@ import { type BedrockConfig, } from '../../../llm/providers/bedrock.js'; - /** * System prompt for the LLM */ diff --git a/packages/forms/src/llm/cache/hash.ts b/packages/forms/src/llm/cache/hash.ts index fc4eab64..42d3f295 100644 --- a/packages/forms/src/llm/cache/hash.ts +++ b/packages/forms/src/llm/cache/hash.ts @@ -13,10 +13,14 @@ export const computeObjectCacheKey = async ( const keyComponents = { model: extractModelId(params.model), system: 'system' in params ? params.system : undefined, - messages: 'messages' in params ? await hashMessages(params.messages ?? []) : undefined, + messages: + 'messages' in params + ? await hashMessages(params.messages ?? []) + : undefined, schema: 'schema' in params ? await hashSchema(params.schema) : undefined, schemaName: 'schemaName' in params ? params.schemaName : undefined, - schemaDescription: 'schemaDescription' in params ? params.schemaDescription : undefined, + schemaDescription: + 'schemaDescription' in params ? params.schemaDescription : undefined, temperature: 'temperature' in params ? params.temperature : undefined, topP: 'topP' in params ? params.topP : undefined, maxTokens: 'maxTokens' in params ? params.maxTokens : undefined, diff --git a/packages/forms/src/llm/services/generate-object.ts b/packages/forms/src/llm/services/generate-object.ts index 18e057d9..1c39a157 100644 --- a/packages/forms/src/llm/services/generate-object.ts +++ b/packages/forms/src/llm/services/generate-object.ts @@ -35,9 +35,10 @@ export const generateObjectCached = async ( const cacheKey = await computeObjectCacheKey(params); // Try cache first - const cached = await context.cache.get< - Awaited>> - >(cacheKey); + const cached = + await context.cache.get>>>( + cacheKey + ); if (cached) { console.log('[LLM Cache] Hit:', cacheKey.slice(0, 16)); diff --git a/packages/forms/src/services/index.ts b/packages/forms/src/services/index.ts index 96a313c3..161b0e2d 100644 --- a/packages/forms/src/services/index.ts +++ b/packages/forms/src/services/index.ts @@ -1,6 +1,9 @@ import { type ServiceMethod, createService } from '@flexion/forms-common'; -import { type FormServiceContext, type InternalFormServiceContext } from '../context/index.js'; +import { + type FormServiceContext, + type InternalFormServiceContext, +} from '../context/index.js'; import { parsePdf as parsePdfCore } from '../documents/pdf/parsing-api.js'; import { type AddForm, addForm } from './add-form.js';