@geekmidas/cli 10.0.0-alpha.16 → 10.0.0-alpha.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/dist/{bundler-BeNDZdHQ.cjs → bundler-mjqGAvu1.cjs} +2 -2
  2. package/dist/{bundler-BeNDZdHQ.cjs.map → bundler-mjqGAvu1.cjs.map} +1 -1
  3. package/dist/{bundler-BodTL4kf.mjs → bundler-mwH7WOb1.mjs} +2 -2
  4. package/dist/{bundler-BodTL4kf.mjs.map → bundler-mwH7WOb1.mjs.map} +1 -1
  5. package/dist/{config-Q1rvKHvw.cjs → config-DSXI480r.cjs} +2 -2
  6. package/dist/{config-Q1rvKHvw.cjs.map → config-DSXI480r.cjs.map} +1 -1
  7. package/dist/{config-pqGD3hF9.mjs → config-DdKkoUE1.mjs} +2 -2
  8. package/dist/{config-pqGD3hF9.mjs.map → config-DdKkoUE1.mjs.map} +1 -1
  9. package/dist/config.cjs +2 -2
  10. package/dist/config.d.cts +2 -2
  11. package/dist/config.d.mts +2 -2
  12. package/dist/config.mjs +2 -2
  13. package/dist/{credentials-Q6F0uqJq.cjs → credentials-C6Pfn6GY.cjs} +31 -9
  14. package/dist/credentials-C6Pfn6GY.cjs.map +1 -0
  15. package/dist/{credentials-BekkKtY_.mjs → credentials-lbv-j0Be.mjs} +32 -10
  16. package/dist/{credentials-BekkKtY_.mjs.map → credentials-lbv-j0Be.mjs.map} +1 -1
  17. package/dist/discover-BERGFg8v.cjs.map +1 -1
  18. package/dist/discover-CK-n3w6b.mjs.map +1 -1
  19. package/dist/{github-CRwnxKmD.mjs → github-DlIrXPee.mjs} +21 -8
  20. package/dist/github-DlIrXPee.mjs.map +1 -0
  21. package/dist/{github-BfflNPVe.cjs → github-n7N9yOff.cjs} +22 -9
  22. package/dist/github-n7N9yOff.cjs.map +1 -0
  23. package/dist/{index-C-VLwB_Y.d.mts → index-B4H4XF7A.d.mts} +2 -2
  24. package/dist/{index-C-HkOEBt.d.cts.map → index-B4H4XF7A.d.mts.map} +1 -1
  25. package/dist/{index-C-HkOEBt.d.cts → index-RCUizGxL.d.cts} +2 -2
  26. package/dist/{index-C-VLwB_Y.d.mts.map → index-RCUizGxL.d.cts.map} +1 -1
  27. package/dist/index.cjs +164 -172
  28. package/dist/index.cjs.map +1 -1
  29. package/dist/index.mjs +159 -167
  30. package/dist/index.mjs.map +1 -1
  31. package/dist/{openapi-Dcbme-Us.cjs → openapi-B8bu_OXN.cjs} +2 -2
  32. package/dist/{openapi-Dcbme-Us.cjs.map → openapi-B8bu_OXN.cjs.map} +1 -1
  33. package/dist/{openapi-vy4JzhUv.mjs → openapi-DfjbhrsM.mjs} +2 -2
  34. package/dist/{openapi-vy4JzhUv.mjs.map → openapi-DfjbhrsM.mjs.map} +1 -1
  35. package/dist/openapi.cjs +1 -1
  36. package/dist/openapi.d.cts +1 -1
  37. package/dist/openapi.d.mts +1 -1
  38. package/dist/openapi.mjs +1 -1
  39. package/dist/reconcile/public.d.cts +1 -1
  40. package/dist/reconcile/public.d.mts +1 -1
  41. package/dist/{fullstack-secrets-DXsWO-5m.cjs → reconcile-Cq4cO83R.cjs} +45 -1
  42. package/dist/reconcile-Cq4cO83R.cjs.map +1 -0
  43. package/dist/{fullstack-secrets-BET8gqwK.mjs → reconcile-fmoluZmZ.mjs} +34 -2
  44. package/dist/reconcile-fmoluZmZ.mjs.map +1 -0
  45. package/dist/ssm-BKN1Cg4z.mjs +60 -0
  46. package/dist/ssm-BKN1Cg4z.mjs.map +1 -0
  47. package/dist/ssm-FxeSAt35.cjs +60 -0
  48. package/dist/ssm-FxeSAt35.cjs.map +1 -0
  49. package/dist/{storage-C-30BD1e.cjs → storage-DgypfSD6.cjs} +83 -4
  50. package/dist/storage-DgypfSD6.cjs.map +1 -0
  51. package/dist/{storage-P_b3w7uj.mjs → storage-TP0x118M.mjs} +77 -4
  52. package/dist/storage-TP0x118M.mjs.map +1 -0
  53. package/dist/transfer-2aRVVJ6I.mjs +122 -0
  54. package/dist/transfer-2aRVVJ6I.mjs.map +1 -0
  55. package/dist/transfer-C3WZVmj_.cjs +151 -0
  56. package/dist/transfer-C3WZVmj_.cjs.map +1 -0
  57. package/dist/{types-RhXmHR-e.d.cts → types-7R81hu0i.d.mts} +138 -1
  58. package/dist/types-7R81hu0i.d.mts.map +1 -0
  59. package/dist/{types-kvBQjpPo.d.mts → types-N7c2O2m7.d.cts} +140 -1
  60. package/dist/types-N7c2O2m7.d.cts.map +1 -0
  61. package/dist/workspace/index.cjs +1 -1
  62. package/dist/workspace/index.d.cts +2 -2
  63. package/dist/workspace/index.d.mts +2 -2
  64. package/dist/workspace/index.mjs +1 -1
  65. package/dist/{workspace-CgwmXEtc.mjs → workspace-Bu3xNCej.mjs} +14 -3
  66. package/dist/{workspace-CgwmXEtc.mjs.map → workspace-Bu3xNCej.mjs.map} +1 -1
  67. package/dist/{workspace-mFpI_ky_.cjs → workspace-CRD9YinV.cjs} +19 -2
  68. package/dist/{workspace-mFpI_ky_.cjs.map → workspace-CRD9YinV.cjs.map} +1 -1
  69. package/package.json +11 -11
  70. package/dist/credentials-Q6F0uqJq.cjs.map +0 -1
  71. package/dist/fullstack-secrets-BET8gqwK.mjs.map +0 -1
  72. package/dist/fullstack-secrets-DXsWO-5m.cjs.map +0 -1
  73. package/dist/github-BfflNPVe.cjs.map +0 -1
  74. package/dist/github-CRwnxKmD.mjs.map +0 -1
  75. package/dist/keystore-B4PRlbgE.mjs +0 -81
  76. package/dist/keystore-B4PRlbgE.mjs.map +0 -1
  77. package/dist/keystore-DgGRj2Yy.cjs +0 -98
  78. package/dist/keystore-DgGRj2Yy.cjs.map +0 -1
  79. package/dist/reconcile-Dd2iyILf.cjs +0 -34
  80. package/dist/reconcile-Dd2iyILf.cjs.map +0 -1
  81. package/dist/reconcile-DhQ00DpU.mjs +0 -34
  82. package/dist/reconcile-DhQ00DpU.mjs.map +0 -1
  83. package/dist/storage-C-30BD1e.cjs.map +0 -1
  84. package/dist/storage-P_b3w7uj.mjs.map +0 -1
  85. package/dist/sync-B3xxXtJT.mjs +0 -88
  86. package/dist/sync-B3xxXtJT.mjs.map +0 -1
  87. package/dist/sync-D7uTIIEi.cjs +0 -111
  88. package/dist/sync-D7uTIIEi.cjs.map +0 -1
  89. package/dist/types-RhXmHR-e.d.cts.map +0 -1
  90. package/dist/types-kvBQjpPo.d.mts.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage-TP0x118M.mjs","names":[],"sources":["../src/secrets/keystore.ts","../src/secrets/storage.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { chmod, mkdir, readFile, rm, writeFile } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, join } from 'node:path';\n\n/** Key length for AES-256 encryption */\nconst KEY_LENGTH = 32; // 256 bits\n\n/**\n * Get the keystore directory for a project.\n * Keys are stored at ~/.gkm/{project-name}/\n *\n * @param projectName - Name of the project (defaults to current directory name)\n * @returns Path to the keystore directory\n */\nexport function getKeystoreDir(projectName?: string): string {\n\tconst name = projectName ?? basename(process.cwd());\n\treturn join(homedir(), '.gkm', name);\n}\n\n/**\n * Get the path to a stage's encryption key.\n *\n * @param stage - Stage name (e.g., 'development', 'production')\n * @param projectName - Name of the project (defaults to current directory name)\n * @returns Path to the key file\n */\nexport function getKeyPath(stage: string, projectName?: string): string {\n\treturn join(getKeystoreDir(projectName), `${stage}.key`);\n}\n\n/**\n * Check if a key exists for a stage.\n */\nexport function keyExists(stage: string, projectName?: string): boolean {\n\treturn existsSync(getKeyPath(stage, projectName));\n}\n\n/**\n * Generate a new encryption key for a stage.\n * The key is stored at ~/.gkm/{project-name}/{stage}.key with restricted permissions.\n *\n * @param stage - Stage name\n * @param projectName - Project name (defaults to current directory name)\n * @returns The generated key as a hex string\n */\nexport async function generateKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string> {\n\tconst keyDir = getKeystoreDir(projectName);\n\tconst keyPath = getKeyPath(stage, projectName);\n\n\t// Ensure keystore directory exists with restricted permissions\n\tawait mkdir(keyDir, { recursive: true, mode: 0o700 });\n\n\t// Generate random key\n\tconst key = randomBytes(KEY_LENGTH).toString('hex');\n\n\t// Write key with restricted permissions (owner read/write only)\n\tawait writeFile(keyPath, key, { mode: 0o600, encoding: 'utf-8' });\n\n\t// Ensure permissions are set correctly (in case file existed)\n\tawait chmod(keyPath, 0o600);\n\n\treturn key;\n}\n\n/**\n * Read an encryption key for a stage.\n *\n * @param stage - Stage name\n * @param projectName - Project name (defaults to current directory name)\n * @returns The key as a hex string, or null if not found\n */\nexport async function readKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string | null> {\n\tconst keyPath = getKeyPath(stage, projectName);\n\n\tif (!existsSync(keyPath)) {\n\t\treturn null;\n\t}\n\n\tconst key = await readFile(keyPath, 'utf-8');\n\treturn key.trim();\n}\n\n/**\n * Read an encryption key for a stage, throwing if not found.\n */\nexport async function requireKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string> {\n\tconst key = await readKey(stage, projectName);\n\n\tif (!key) {\n\t\tconst name = projectName ?? basename(process.cwd());\n\t\tthrow new Error(\n\t\t\t`Encryption key not found for stage \"${stage}\" in project \"${name}\". ` +\n\t\t\t\t`Expected key at: ${getKeyPath(stage, projectName)}`,\n\t\t);\n\t}\n\n\treturn key;\n}\n\n/**\n * Delete a key for a stage.\n */\nexport async function deleteKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<void> {\n\tconst keyPath = getKeyPath(stage, projectName);\n\n\tif (existsSync(keyPath)) {\n\t\tawait rm(keyPath);\n\t}\n}\n\n/**\n * Get or create a key for a stage.\n * If the key already exists, it is returned. Otherwise, a new key is generated.\n *\n * @param stage - Stage name\n * @param projectName - Project name (defaults to current directory name)\n * @returns The key as a hex string\n */\nexport async function getOrCreateKey(\n\tstage: string,\n\tprojectName?: string,\n): Promise<string> {\n\tconst existingKey = await readKey(stage, projectName);\n\n\tif (existingKey) {\n\t\treturn existingKey;\n\t}\n\n\treturn generateKey(stage, projectName);\n}\n","import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';\nimport { existsSync } from 'node:fs';\nimport { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { basename, join } from 'node:path';\nimport { getOrCreateKey, readKey } from './keystore';\nimport type { EmbeddableSecrets, StageSecrets } from './types';\n\n/** Default secrets directory relative to project root */\nconst SECRETS_DIR = '.gkm/secrets';\n\n/** AES-256-GCM configuration */\nconst ALGORITHM = 'aes-256-gcm';\nconst IV_LENGTH = 12; // 96 bits for GCM\nconst AUTH_TAG_LENGTH = 16; // 128 bits\n\n/** Encrypted secrets file structure */\ninterface EncryptedSecretsFile {\n\t/** Version for future format changes */\n\tversion: 1;\n\t/** Base64 encoded encrypted data (ciphertext + auth tag) */\n\tencrypted: string;\n\t/** Hex encoded IV */\n\tiv: string;\n}\n\n/**\n * Get the secrets directory path.\n */\nexport function getSecretsDir(cwd = process.cwd()): string {\n\treturn join(cwd, SECRETS_DIR);\n}\n\n/**\n * Get the secrets file path for a stage.\n */\nexport function getSecretsPath(stage: string, cwd = process.cwd()): string {\n\treturn join(getSecretsDir(cwd), `${stage}.json`);\n}\n\n/**\n * Check if secrets exist for a stage.\n */\nexport function secretsExist(stage: string, cwd = process.cwd()): boolean {\n\treturn existsSync(getSecretsPath(stage, cwd));\n}\n\n/**\n * Initialize an empty StageSecrets object for a stage.\n */\nexport function initStageSecrets(stage: string): StageSecrets {\n\tconst now = new Date().toISOString();\n\treturn {\n\t\tstage,\n\t\tcreatedAt: now,\n\t\tupdatedAt: now,\n\t\tservices: {},\n\t\turls: {},\n\t\tcustom: {},\n\t};\n}\n\n/**\n * Encrypt secrets using a key.\n */\nfunction encryptSecretsData(\n\tsecrets: StageSecrets,\n\tkeyHex: string,\n): EncryptedSecretsFile {\n\tconst key = Buffer.from(keyHex, 'hex');\n\tconst iv = randomBytes(IV_LENGTH);\n\n\t// Serialize secrets to JSON\n\tconst plaintext = JSON.stringify(secrets);\n\n\t// Encrypt\n\tconst cipher = createCipheriv(ALGORITHM, key, iv);\n\tconst ciphertext = Buffer.concat([\n\t\tcipher.update(plaintext, 'utf-8'),\n\t\tcipher.final(),\n\t]);\n\n\t// Get auth tag\n\tconst authTag = cipher.getAuthTag();\n\n\t// Combine ciphertext + auth tag\n\tconst combined = Buffer.concat([ciphertext, authTag]);\n\n\treturn {\n\t\tversion: 1,\n\t\tencrypted: combined.toString('base64'),\n\t\tiv: iv.toString('hex'),\n\t};\n}\n\n/**\n * Decrypt secrets using a key.\n */\nfunction decryptSecretsData(\n\tdata: EncryptedSecretsFile,\n\tkeyHex: string,\n): StageSecrets {\n\tconst key = Buffer.from(keyHex, 'hex');\n\tconst ivBuffer = Buffer.from(data.iv, 'hex');\n\tconst combined = Buffer.from(data.encrypted, 'base64');\n\n\t// Split ciphertext and auth tag\n\tconst ciphertext = combined.subarray(0, -AUTH_TAG_LENGTH);\n\tconst authTag = combined.subarray(-AUTH_TAG_LENGTH);\n\n\t// Decrypt\n\tconst decipher = createDecipheriv(ALGORITHM, key, ivBuffer);\n\tdecipher.setAuthTag(authTag);\n\n\tconst plaintext = Buffer.concat([\n\t\tdecipher.update(ciphertext),\n\t\tdecipher.final(),\n\t]);\n\n\treturn JSON.parse(plaintext.toString('utf-8')) as StageSecrets;\n}\n\n/**\n * Read secrets for a stage (encrypted).\n * Requires the decryption key to be present at ~/.gkm/{project}/{stage}.key\n *\n * @returns StageSecrets or null if not found\n */\nexport async function readStageSecrets(\n\tstage: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets | null> {\n\tconst path = getSecretsPath(stage, cwd);\n\n\tif (!existsSync(path)) {\n\t\treturn null;\n\t}\n\n\tconst content = await readFile(path, 'utf-8');\n\tconst data = JSON.parse(content);\n\n\t// Check if this is an encrypted file (has version field)\n\tif (data.version === 1 && data.encrypted && data.iv) {\n\t\tconst projectName = basename(cwd);\n\t\tconst key = await readKey(stage, projectName);\n\n\t\tif (!key) {\n\t\t\tthrow new Error(\n\t\t\t\t`Decryption key not found for stage \"${stage}\". ` +\n\t\t\t\t\t`Expected key at: ~/.gkm/${projectName}/${stage}.key`,\n\t\t\t);\n\t\t}\n\n\t\treturn decryptSecretsData(data as EncryptedSecretsFile, key);\n\t}\n\n\t// Legacy: unencrypted format (for backwards compatibility)\n\treturn data as StageSecrets;\n}\n\n/**\n * Write secrets for a stage (encrypted).\n * Creates or uses existing encryption key at ~/.gkm/{project}/{stage}.key\n */\nexport async function writeStageSecrets(\n\tsecrets: StageSecrets,\n\tcwd = process.cwd(),\n): Promise<void> {\n\tconst dir = getSecretsDir(cwd);\n\tconst path = getSecretsPath(secrets.stage, cwd);\n\tconst projectName = basename(cwd);\n\n\t// Ensure directory exists\n\tawait mkdir(dir, { recursive: true });\n\n\t// Get or create encryption key\n\tconst key = await getOrCreateKey(secrets.stage, projectName);\n\n\t// Encrypt and write\n\tconst encrypted = encryptSecretsData(secrets, key);\n\tawait writeFile(path, JSON.stringify(encrypted, null, 2), 'utf-8');\n}\n\n/**\n * Convert StageSecrets to embeddable format (flat key-value pairs).\n * This is what gets encrypted and embedded in the bundle.\n */\nexport function toEmbeddableSecrets(secrets: StageSecrets): EmbeddableSecrets {\n\treturn {\n\t\t...secrets.urls,\n\t\t...secrets.custom,\n\t\t// Also include individual service credentials if needed\n\t\t...(secrets.services.postgres && {\n\t\t\tPOSTGRES_USER: secrets.services.postgres.username,\n\t\t\tPOSTGRES_PASSWORD: secrets.services.postgres.password,\n\t\t\tPOSTGRES_DB: secrets.services.postgres.database ?? 'app',\n\t\t\tPOSTGRES_HOST: secrets.services.postgres.host,\n\t\t\tPOSTGRES_PORT: String(secrets.services.postgres.port),\n\t\t}),\n\t\t...(secrets.services.redis && {\n\t\t\tREDIS_PASSWORD: secrets.services.redis.password,\n\t\t\tREDIS_HOST: secrets.services.redis.host,\n\t\t\tREDIS_PORT: String(secrets.services.redis.port),\n\t\t}),\n\t\t...(secrets.services.rabbitmq && {\n\t\t\tRABBITMQ_USER: secrets.services.rabbitmq.username,\n\t\t\tRABBITMQ_PASSWORD: secrets.services.rabbitmq.password,\n\t\t\tRABBITMQ_HOST: secrets.services.rabbitmq.host,\n\t\t\tRABBITMQ_PORT: String(secrets.services.rabbitmq.port),\n\t\t\tRABBITMQ_VHOST: secrets.services.rabbitmq.vhost ?? '/',\n\t\t}),\n\t\t...(secrets.services.minio && {\n\t\t\tSTORAGE_ACCESS_KEY_ID: secrets.services.minio.username,\n\t\t\tSTORAGE_SECRET_ACCESS_KEY: secrets.services.minio.password,\n\t\t\tSTORAGE_BUCKET: secrets.services.minio.bucket ?? 'app',\n\t\t\tSTORAGE_REGION: 'eu-west-1',\n\t\t\tSTORAGE_FORCE_PATH_STYLE: 'true',\n\t\t}),\n\t\t...(secrets.services.mailpit && {\n\t\t\tSMTP_HOST: secrets.services.mailpit.host,\n\t\t\tSMTP_PORT: String(secrets.services.mailpit.port),\n\t\t\tSMTP_USER: secrets.services.mailpit.username,\n\t\t\tSMTP_PASS: secrets.services.mailpit.password,\n\t\t\tSMTP_SECURE: 'false',\n\t\t\tMAIL_FROM: 'noreply@localhost',\n\t\t}),\n\t\t...(secrets.services.localstack && {\n\t\t\tAWS_ACCESS_KEY_ID:\n\t\t\t\tsecrets.services.localstack.accessKeyId ??\n\t\t\t\tsecrets.services.localstack.username,\n\t\t\tAWS_SECRET_ACCESS_KEY: secrets.services.localstack.password,\n\t\t\tAWS_REGION: secrets.services.localstack.region ?? 'us-east-1',\n\t\t\tAWS_ENDPOINT_URL: `http://${secrets.services.localstack.host}:${secrets.services.localstack.port}`,\n\t\t}),\n\t\t...(secrets.services.pgboss && {\n\t\t\tPGBOSS_DB_USER: secrets.services.pgboss.username,\n\t\t\tPGBOSS_DB_PASSWORD: secrets.services.pgboss.password,\n\t\t}),\n\t};\n}\n\n/**\n * Update a custom secret in the secrets file.\n */\nexport async function setCustomSecret(\n\tstage: string,\n\tkey: string,\n\tvalue: string,\n\tcwd = process.cwd(),\n): Promise<StageSecrets> {\n\tconst secrets = await readStageSecrets(stage, cwd);\n\n\tif (!secrets) {\n\t\tthrow new Error(\n\t\t\t`Secrets not found for stage \"${stage}\". Run \"gkm secrets:init --stage ${stage}\" first.`,\n\t\t);\n\t}\n\n\tconst updated: StageSecrets = {\n\t\t...secrets,\n\t\tupdatedAt: new Date().toISOString(),\n\t\tcustom: {\n\t\t\t...secrets.custom,\n\t\t\t[key]: value,\n\t\t},\n\t};\n\n\tawait writeStageSecrets(updated, cwd);\n\treturn updated;\n}\n\n/**\n * Mask a password for display (show first 4 and last 2 chars).\n */\nexport function maskPassword(password: string): string {\n\tif (password.length <= 8) {\n\t\treturn '********';\n\t}\n\treturn `${password.slice(0, 4)}${'*'.repeat(password.length - 6)}${password.slice(-2)}`;\n}\n\n/**\n * Result of environment variable validation.\n */\nexport interface EnvValidationResult {\n\t/** Whether all required environment variables are present */\n\tvalid: boolean;\n\t/** List of missing environment variable names */\n\tmissing: string[];\n\t/** List of environment variables that are provided */\n\tprovided: string[];\n\t/** List of environment variables that were required */\n\trequired: string[];\n}\n\n/**\n * Validate that all required environment variables are present in secrets.\n *\n * @param requiredVars - Array of environment variable names required by the application\n * @param secrets - Stage secrets to validate against\n * @returns Validation result with missing and provided variables\n *\n * @example\n * ```typescript\n * const required = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET'];\n * const secrets = await readStageSecrets('production');\n * const result = validateEnvironmentVariables(required, secrets);\n *\n * if (!result.valid) {\n * console.error(`Missing environment variables: ${result.missing.join(', ')}`);\n * }\n * ```\n */\nexport function validateEnvironmentVariables(\n\trequiredVars: string[],\n\tsecrets: StageSecrets,\n): EnvValidationResult {\n\tconst embeddable = toEmbeddableSecrets(secrets);\n\tconst availableVars = new Set(Object.keys(embeddable));\n\n\tconst missing: string[] = [];\n\tconst provided: string[] = [];\n\n\tfor (const varName of requiredVars) {\n\t\tif (availableVars.has(varName)) {\n\t\t\tprovided.push(varName);\n\t\t} else {\n\t\t\tmissing.push(varName);\n\t\t}\n\t}\n\n\treturn {\n\t\tvalid: missing.length === 0,\n\t\tmissing: missing.sort(),\n\t\tprovided: provided.sort(),\n\t\trequired: [...requiredVars].sort(),\n\t};\n}\n"],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
+ import { T as assertDeployedStage } from "./workspace-Bu3xNCej.mjs";
3
+ import { n as readStageSecrets, s as writeStageSecrets } from "./storage-TP0x118M.mjs";
4
+ import { t as reconcileMissingSecrets } from "./reconcile-fmoluZmZ.mjs";
5
+ //#region src/secrets/store.ts
6
+ /** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */
7
+ var FileSecretsStore = class {
8
+ root;
9
+ constructor(root) {
10
+ this.root = root;
11
+ }
12
+ pull(stage) {
13
+ return readStageSecrets(stage, this.root);
14
+ }
15
+ push(_stage, secrets) {
16
+ return writeStageSecrets(secrets, this.root);
17
+ }
18
+ };
19
+ /**
20
+ * The store a stage's secrets live in.
21
+ *
22
+ * The local stage is always the file: its secrets belong to the machine
23
+ * running `gkm dev`. A deployed stage uses `secrets.store`.
24
+ */
25
+ async function secretsStoreFor(workspace, stage, options = {}) {
26
+ const configured = workspace.secrets.store ?? "file";
27
+ if (stage === workspace.stages.local || configured === "file") return new FileSecretsStore(workspace.root);
28
+ if (configured.provider !== "ssm") return configured.provider;
29
+ const { SsmSecretsStore } = await import("./ssm-BKN1Cg4z.mjs");
30
+ return new SsmSecretsStore({
31
+ project: workspace.name,
32
+ region: configured.region,
33
+ ...options.profile ? { profile: options.profile } : {}
34
+ });
35
+ }
36
+ /** Whether a stage's secrets live somewhere other than this machine. */
37
+ function isRemoteStore(workspace, stage) {
38
+ const configured = workspace.secrets.store ?? "file";
39
+ return stage !== workspace.stages.local && configured !== "file";
40
+ }
41
+ //#endregion
42
+ //#region src/secrets/transfer.ts
43
+ /**
44
+ * Moving a deployed stage's secrets between this machine and its store.
45
+ *
46
+ * `push` sends the encrypted local copy to the stage's store; `pull` brings it
47
+ * back and writes the local copy a deploy reads — generating a key for it if
48
+ * the machine has none, which is what a fresh CI runner needs. Both reconcile
49
+ * the keys the workspace now derives, so a store never lags a new construct.
50
+ */
51
+ var transfer_exports = /* @__PURE__ */ __exportAll({
52
+ NoLocalSecrets: () => NoLocalSecrets,
53
+ NoRemoteSecretsStore: () => NoRemoteSecretsStore,
54
+ NoStoredSecrets: () => NoStoredSecrets,
55
+ pullStageSecrets: () => pullStageSecrets,
56
+ pushStageSecrets: () => pushStageSecrets
57
+ });
58
+ /** A push or pull for a stage with nowhere to push or pull from. */
59
+ var NoRemoteSecretsStore = class extends Error {
60
+ stage;
61
+ constructor(stage) {
62
+ super(`Stage "${stage}" keeps its secrets in the local file, so there is nothing to push or pull. Set secrets.store in gkm.config.ts (e.g. { provider: 'ssm', region: 'eu-west-1' }) for deployed stages.`);
63
+ this.stage = stage;
64
+ this.name = "NoRemoteSecretsStore";
65
+ }
66
+ };
67
+ /** A push for a stage whose secrets this machine does not have. */
68
+ var NoLocalSecrets = class extends Error {
69
+ stage;
70
+ constructor(stage) {
71
+ super(`No secrets for stage "${stage}" on this machine. Run gkm secrets:init --stage ${stage} first.`);
72
+ this.stage = stage;
73
+ this.name = "NoLocalSecrets";
74
+ }
75
+ };
76
+ /** A pull for a stage the store holds nothing for. */
77
+ var NoStoredSecrets = class extends Error {
78
+ stage;
79
+ constructor(stage) {
80
+ super(`The store holds no secrets for stage "${stage}". Push them first: gkm secrets:push --stage ${stage}.`);
81
+ this.stage = stage;
82
+ this.name = "NoStoredSecrets";
83
+ }
84
+ };
85
+ async function reconcile(secrets, workspace, stage) {
86
+ const { derivedContainers } = await import("./credentials-lbv-j0Be.mjs").then((n) => n.h);
87
+ const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
88
+ return result ? {
89
+ secrets: result.secrets,
90
+ addedKeys: result.addedKeys
91
+ } : {
92
+ secrets,
93
+ addedKeys: []
94
+ };
95
+ }
96
+ function remoteOnly(workspace, stage) {
97
+ assertDeployedStage(workspace.stages, stage);
98
+ if (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);
99
+ }
100
+ /** Send this machine's secrets for a deployed stage to its store. */
101
+ async function pushStageSecrets(workspace, stage, options = {}) {
102
+ remoteOnly(workspace, stage);
103
+ const local = await readStageSecrets(stage, workspace.root);
104
+ if (!local) throw new NoLocalSecrets(stage);
105
+ const result = await reconcile(local, workspace, stage);
106
+ if (result.addedKeys.length) await writeStageSecrets(result.secrets, workspace.root);
107
+ await (await secretsStoreFor(workspace, stage, options)).push(stage, result.secrets);
108
+ return result;
109
+ }
110
+ /** Bring a deployed stage's secrets from its store into the local copy. */
111
+ async function pullStageSecrets(workspace, stage, options = {}) {
112
+ remoteOnly(workspace, stage);
113
+ const stored = await (await secretsStoreFor(workspace, stage, options)).pull(stage);
114
+ if (!stored) throw new NoStoredSecrets(stage);
115
+ const result = await reconcile(stored, workspace, stage);
116
+ await writeStageSecrets(result.secrets, workspace.root);
117
+ return result;
118
+ }
119
+ //#endregion
120
+ export { isRemoteStore as a, transfer_exports as i, pullStageSecrets as n, pushStageSecrets as r, NoStoredSecrets as t };
121
+
122
+ //# sourceMappingURL=transfer-2aRVVJ6I.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transfer-2aRVVJ6I.mjs","names":[],"sources":["../src/secrets/store.ts","../src/secrets/transfer.ts"],"sourcesContent":["/**\n * Where a stage's secrets live when they are not only on one machine.\n *\n * A deployed stage has to reach its secrets from wherever it is deployed from\n * — a teammate's laptop today, a CI runner tomorrow — and `.gkm/` is not\n * committed, so the encrypted file on the machine that created them is not a\n * home for them. A store is: `push` puts a stage's secrets there, `pull`\n * brings them back, and the deploy reads the local copy `pull` writes.\n *\n * The same shape deploy state has (`StateProvider`): one interface, a backend\n * per place, and a custom object for any place this package does not ship.\n */\n\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface SecretsStore {\n\t/** The stage's secrets, or null if the store holds none for it. */\n\tpull(stage: string): Promise<StageSecrets | null>;\n\t/** Replace the stage's secrets in the store. */\n\tpush(stage: string, secrets: StageSecrets): Promise<void>;\n}\n\n/** Secrets in SSM Parameter Store, in the account of the active credentials. */\nexport interface SsmSecretsStoreConfig {\n\tprovider: 'ssm';\n\tregion: string;\n}\n\n/** Any other backend: an object implementing {@link SecretsStore}. */\nexport interface CustomSecretsStoreConfig {\n\tprovider: SecretsStore;\n}\n\n/**\n * `secrets.store` in gkm.config.ts.\n *\n * `'file'` — the default — keeps each stage's secrets in the encrypted\n * `.gkm/secrets/<stage>.json` beside its key in `~/.gkm/`. It cannot serve a\n * deploy from CI while `.gkm/` is gitignored; `ssm` or a custom store can.\n */\nexport type SecretsStoreConfig =\n\t| 'file'\n\t| SsmSecretsStoreConfig\n\t| CustomSecretsStoreConfig;\n\n/** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */\nexport class FileSecretsStore implements SecretsStore {\n\tconstructor(private readonly root: string) {}\n\n\tpull(stage: string): Promise<StageSecrets | null> {\n\t\treturn readStageSecrets(stage, this.root);\n\t}\n\n\tpush(_stage: string, secrets: StageSecrets): Promise<void> {\n\t\treturn writeStageSecrets(secrets, this.root);\n\t}\n}\n\nexport interface SecretsStoreOptions {\n\t/**\n\t * The AWS profile for the stage's account. Only that profile is used —\n\t * never AWS_* from the environment — so a store cannot quietly land in\n\t * whichever account the shell was exported for.\n\t */\n\tprofile?: string;\n}\n\n/**\n * The store a stage's secrets live in.\n *\n * The local stage is always the file: its secrets belong to the machine\n * running `gkm dev`. A deployed stage uses `secrets.store`.\n */\nexport async function secretsStoreFor(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<SecretsStore> {\n\tconst configured = workspace.secrets.store ?? 'file';\n\n\tif (stage === workspace.stages.local || configured === 'file') {\n\t\treturn new FileSecretsStore(workspace.root);\n\t}\n\n\tif (configured.provider !== 'ssm') {\n\t\treturn configured.provider;\n\t}\n\n\tconst { SsmSecretsStore } = await import('./ssm.js');\n\treturn new SsmSecretsStore({\n\t\tproject: workspace.name,\n\t\tregion: configured.region,\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n}\n\n/** Whether a stage's secrets live somewhere other than this machine. */\nexport function isRemoteStore(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): boolean {\n\tconst configured = workspace.secrets.store ?? 'file';\n\treturn stage !== workspace.stages.local && configured !== 'file';\n}\n","/**\n * Moving a deployed stage's secrets between this machine and its store.\n *\n * `push` sends the encrypted local copy to the stage's store; `pull` brings it\n * back and writes the local copy a deploy reads — generating a key for it if\n * the machine has none, which is what a fresh CI runner needs. Both reconcile\n * the keys the workspace now derives, so a store never lags a new construct.\n */\n\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { reconcileMissingSecrets } from './reconcile.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport {\n\tisRemoteStore,\n\ttype SecretsStoreOptions,\n\tsecretsStoreFor,\n} from './store.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface TransferResult {\n\tsecrets: StageSecrets;\n\t/** Keys the workspace derives that the secrets were missing, now added. */\n\taddedKeys: string[];\n}\n\n/** A push or pull for a stage with nowhere to push or pull from. */\nexport class NoRemoteSecretsStore extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`Stage \"${stage}\" keeps its secrets in the local file, so there is nothing to push or pull. ` +\n\t\t\t\t\"Set secrets.store in gkm.config.ts (e.g. { provider: 'ssm', region: 'eu-west-1' }) for deployed stages.\",\n\t\t);\n\t\tthis.name = 'NoRemoteSecretsStore';\n\t}\n}\n\n/** A push for a stage whose secrets this machine does not have. */\nexport class NoLocalSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`No secrets for stage \"${stage}\" on this machine. Run gkm secrets:init --stage ${stage} first.`,\n\t\t);\n\t\tthis.name = 'NoLocalSecrets';\n\t}\n}\n\n/** A pull for a stage the store holds nothing for. */\nexport class NoStoredSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`The store holds no secrets for stage \"${stage}\". Push them first: gkm secrets:push --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoStoredSecrets';\n\t}\n}\n\nasync function reconcile(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): Promise<TransferResult> {\n\tconst { derivedContainers } = await import('../reconcile/workspace.js');\n\tconst result = reconcileMissingSecrets(\n\t\tsecrets,\n\t\tworkspace,\n\t\tawait derivedContainers(workspace, stage),\n\t);\n\treturn result\n\t\t? { secrets: result.secrets, addedKeys: result.addedKeys }\n\t\t: { secrets, addedKeys: [] };\n}\n\nfunction remoteOnly(workspace: NormalizedWorkspace, stage: string): void {\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);\n}\n\n/** Send this machine's secrets for a deployed stage to its store. */\nexport async function pushStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst local = await readStageSecrets(stage, workspace.root);\n\tif (!local) throw new NoLocalSecrets(stage);\n\n\tconst result = await reconcile(local, workspace, stage);\n\tif (result.addedKeys.length) {\n\t\tawait writeStageSecrets(result.secrets, workspace.root);\n\t}\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tawait store.push(stage, result.secrets);\n\treturn result;\n}\n\n/** Bring a deployed stage's secrets from its store into the local copy. */\nexport async function pullStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tconst stored = await store.pull(stage);\n\tif (!stored) throw new NoStoredSecrets(stage);\n\n\tconst result = await reconcile(stored, workspace, stage);\n\tawait writeStageSecrets(result.secrets, workspace.root);\n\treturn result;\n}\n"],"mappings":"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"}
@@ -0,0 +1,151 @@
1
+ const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
+ const require_workspace = require("./workspace-CRD9YinV.cjs");
3
+ const require_storage = require("./storage-DgypfSD6.cjs");
4
+ const require_reconcile = require("./reconcile-Cq4cO83R.cjs");
5
+ //#region src/secrets/store.ts
6
+ /** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */
7
+ var FileSecretsStore = class {
8
+ root;
9
+ constructor(root) {
10
+ this.root = root;
11
+ }
12
+ pull(stage) {
13
+ return require_storage.readStageSecrets(stage, this.root);
14
+ }
15
+ push(_stage, secrets) {
16
+ return require_storage.writeStageSecrets(secrets, this.root);
17
+ }
18
+ };
19
+ /**
20
+ * The store a stage's secrets live in.
21
+ *
22
+ * The local stage is always the file: its secrets belong to the machine
23
+ * running `gkm dev`. A deployed stage uses `secrets.store`.
24
+ */
25
+ async function secretsStoreFor(workspace, stage, options = {}) {
26
+ const configured = workspace.secrets.store ?? "file";
27
+ if (stage === workspace.stages.local || configured === "file") return new FileSecretsStore(workspace.root);
28
+ if (configured.provider !== "ssm") return configured.provider;
29
+ const { SsmSecretsStore } = await Promise.resolve().then(() => require("./ssm-FxeSAt35.cjs"));
30
+ return new SsmSecretsStore({
31
+ project: workspace.name,
32
+ region: configured.region,
33
+ ...options.profile ? { profile: options.profile } : {}
34
+ });
35
+ }
36
+ /** Whether a stage's secrets live somewhere other than this machine. */
37
+ function isRemoteStore(workspace, stage) {
38
+ const configured = workspace.secrets.store ?? "file";
39
+ return stage !== workspace.stages.local && configured !== "file";
40
+ }
41
+ //#endregion
42
+ //#region src/secrets/transfer.ts
43
+ /**
44
+ * Moving a deployed stage's secrets between this machine and its store.
45
+ *
46
+ * `push` sends the encrypted local copy to the stage's store; `pull` brings it
47
+ * back and writes the local copy a deploy reads — generating a key for it if
48
+ * the machine has none, which is what a fresh CI runner needs. Both reconcile
49
+ * the keys the workspace now derives, so a store never lags a new construct.
50
+ */
51
+ var transfer_exports = /* @__PURE__ */ require_rolldown_runtime.__exportAll({
52
+ NoLocalSecrets: () => NoLocalSecrets,
53
+ NoRemoteSecretsStore: () => NoRemoteSecretsStore,
54
+ NoStoredSecrets: () => NoStoredSecrets,
55
+ pullStageSecrets: () => pullStageSecrets,
56
+ pushStageSecrets: () => pushStageSecrets
57
+ });
58
+ /** A push or pull for a stage with nowhere to push or pull from. */
59
+ var NoRemoteSecretsStore = class extends Error {
60
+ stage;
61
+ constructor(stage) {
62
+ super(`Stage "${stage}" keeps its secrets in the local file, so there is nothing to push or pull. Set secrets.store in gkm.config.ts (e.g. { provider: 'ssm', region: 'eu-west-1' }) for deployed stages.`);
63
+ this.stage = stage;
64
+ this.name = "NoRemoteSecretsStore";
65
+ }
66
+ };
67
+ /** A push for a stage whose secrets this machine does not have. */
68
+ var NoLocalSecrets = class extends Error {
69
+ stage;
70
+ constructor(stage) {
71
+ super(`No secrets for stage "${stage}" on this machine. Run gkm secrets:init --stage ${stage} first.`);
72
+ this.stage = stage;
73
+ this.name = "NoLocalSecrets";
74
+ }
75
+ };
76
+ /** A pull for a stage the store holds nothing for. */
77
+ var NoStoredSecrets = class extends Error {
78
+ stage;
79
+ constructor(stage) {
80
+ super(`The store holds no secrets for stage "${stage}". Push them first: gkm secrets:push --stage ${stage}.`);
81
+ this.stage = stage;
82
+ this.name = "NoStoredSecrets";
83
+ }
84
+ };
85
+ async function reconcile(secrets, workspace, stage) {
86
+ const { derivedContainers } = await Promise.resolve().then(() => require("./credentials-C6Pfn6GY.cjs")).then((n) => n.workspace_exports);
87
+ const result = require_reconcile.reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
88
+ return result ? {
89
+ secrets: result.secrets,
90
+ addedKeys: result.addedKeys
91
+ } : {
92
+ secrets,
93
+ addedKeys: []
94
+ };
95
+ }
96
+ function remoteOnly(workspace, stage) {
97
+ require_workspace.assertDeployedStage(workspace.stages, stage);
98
+ if (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);
99
+ }
100
+ /** Send this machine's secrets for a deployed stage to its store. */
101
+ async function pushStageSecrets(workspace, stage, options = {}) {
102
+ remoteOnly(workspace, stage);
103
+ const local = await require_storage.readStageSecrets(stage, workspace.root);
104
+ if (!local) throw new NoLocalSecrets(stage);
105
+ const result = await reconcile(local, workspace, stage);
106
+ if (result.addedKeys.length) await require_storage.writeStageSecrets(result.secrets, workspace.root);
107
+ await (await secretsStoreFor(workspace, stage, options)).push(stage, result.secrets);
108
+ return result;
109
+ }
110
+ /** Bring a deployed stage's secrets from its store into the local copy. */
111
+ async function pullStageSecrets(workspace, stage, options = {}) {
112
+ remoteOnly(workspace, stage);
113
+ const stored = await (await secretsStoreFor(workspace, stage, options)).pull(stage);
114
+ if (!stored) throw new NoStoredSecrets(stage);
115
+ const result = await reconcile(stored, workspace, stage);
116
+ await require_storage.writeStageSecrets(result.secrets, workspace.root);
117
+ return result;
118
+ }
119
+ //#endregion
120
+ Object.defineProperty(exports, "NoStoredSecrets", {
121
+ enumerable: true,
122
+ get: function() {
123
+ return NoStoredSecrets;
124
+ }
125
+ });
126
+ Object.defineProperty(exports, "isRemoteStore", {
127
+ enumerable: true,
128
+ get: function() {
129
+ return isRemoteStore;
130
+ }
131
+ });
132
+ Object.defineProperty(exports, "pullStageSecrets", {
133
+ enumerable: true,
134
+ get: function() {
135
+ return pullStageSecrets;
136
+ }
137
+ });
138
+ Object.defineProperty(exports, "pushStageSecrets", {
139
+ enumerable: true,
140
+ get: function() {
141
+ return pushStageSecrets;
142
+ }
143
+ });
144
+ Object.defineProperty(exports, "transfer_exports", {
145
+ enumerable: true,
146
+ get: function() {
147
+ return transfer_exports;
148
+ }
149
+ });
150
+
151
+ //# sourceMappingURL=transfer-C3WZVmj_.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transfer-C3WZVmj_.cjs","names":["readStageSecrets","writeStageSecrets","reconcileMissingSecrets","readStageSecrets","writeStageSecrets"],"sources":["../src/secrets/store.ts","../src/secrets/transfer.ts"],"sourcesContent":["/**\n * Where a stage's secrets live when they are not only on one machine.\n *\n * A deployed stage has to reach its secrets from wherever it is deployed from\n * — a teammate's laptop today, a CI runner tomorrow — and `.gkm/` is not\n * committed, so the encrypted file on the machine that created them is not a\n * home for them. A store is: `push` puts a stage's secrets there, `pull`\n * brings them back, and the deploy reads the local copy `pull` writes.\n *\n * The same shape deploy state has (`StateProvider`): one interface, a backend\n * per place, and a custom object for any place this package does not ship.\n */\n\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface SecretsStore {\n\t/** The stage's secrets, or null if the store holds none for it. */\n\tpull(stage: string): Promise<StageSecrets | null>;\n\t/** Replace the stage's secrets in the store. */\n\tpush(stage: string, secrets: StageSecrets): Promise<void>;\n}\n\n/** Secrets in SSM Parameter Store, in the account of the active credentials. */\nexport interface SsmSecretsStoreConfig {\n\tprovider: 'ssm';\n\tregion: string;\n}\n\n/** Any other backend: an object implementing {@link SecretsStore}. */\nexport interface CustomSecretsStoreConfig {\n\tprovider: SecretsStore;\n}\n\n/**\n * `secrets.store` in gkm.config.ts.\n *\n * `'file'` — the default — keeps each stage's secrets in the encrypted\n * `.gkm/secrets/<stage>.json` beside its key in `~/.gkm/`. It cannot serve a\n * deploy from CI while `.gkm/` is gitignored; `ssm` or a custom store can.\n */\nexport type SecretsStoreConfig =\n\t| 'file'\n\t| SsmSecretsStoreConfig\n\t| CustomSecretsStoreConfig;\n\n/** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */\nexport class FileSecretsStore implements SecretsStore {\n\tconstructor(private readonly root: string) {}\n\n\tpull(stage: string): Promise<StageSecrets | null> {\n\t\treturn readStageSecrets(stage, this.root);\n\t}\n\n\tpush(_stage: string, secrets: StageSecrets): Promise<void> {\n\t\treturn writeStageSecrets(secrets, this.root);\n\t}\n}\n\nexport interface SecretsStoreOptions {\n\t/**\n\t * The AWS profile for the stage's account. Only that profile is used —\n\t * never AWS_* from the environment — so a store cannot quietly land in\n\t * whichever account the shell was exported for.\n\t */\n\tprofile?: string;\n}\n\n/**\n * The store a stage's secrets live in.\n *\n * The local stage is always the file: its secrets belong to the machine\n * running `gkm dev`. A deployed stage uses `secrets.store`.\n */\nexport async function secretsStoreFor(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<SecretsStore> {\n\tconst configured = workspace.secrets.store ?? 'file';\n\n\tif (stage === workspace.stages.local || configured === 'file') {\n\t\treturn new FileSecretsStore(workspace.root);\n\t}\n\n\tif (configured.provider !== 'ssm') {\n\t\treturn configured.provider;\n\t}\n\n\tconst { SsmSecretsStore } = await import('./ssm.js');\n\treturn new SsmSecretsStore({\n\t\tproject: workspace.name,\n\t\tregion: configured.region,\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n}\n\n/** Whether a stage's secrets live somewhere other than this machine. */\nexport function isRemoteStore(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): boolean {\n\tconst configured = workspace.secrets.store ?? 'file';\n\treturn stage !== workspace.stages.local && configured !== 'file';\n}\n","/**\n * Moving a deployed stage's secrets between this machine and its store.\n *\n * `push` sends the encrypted local copy to the stage's store; `pull` brings it\n * back and writes the local copy a deploy reads — generating a key for it if\n * the machine has none, which is what a fresh CI runner needs. Both reconcile\n * the keys the workspace now derives, so a store never lags a new construct.\n */\n\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { reconcileMissingSecrets } from './reconcile.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport {\n\tisRemoteStore,\n\ttype SecretsStoreOptions,\n\tsecretsStoreFor,\n} from './store.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface TransferResult {\n\tsecrets: StageSecrets;\n\t/** Keys the workspace derives that the secrets were missing, now added. */\n\taddedKeys: string[];\n}\n\n/** A push or pull for a stage with nowhere to push or pull from. */\nexport class NoRemoteSecretsStore extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`Stage \"${stage}\" keeps its secrets in the local file, so there is nothing to push or pull. ` +\n\t\t\t\t\"Set secrets.store in gkm.config.ts (e.g. { provider: 'ssm', region: 'eu-west-1' }) for deployed stages.\",\n\t\t);\n\t\tthis.name = 'NoRemoteSecretsStore';\n\t}\n}\n\n/** A push for a stage whose secrets this machine does not have. */\nexport class NoLocalSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`No secrets for stage \"${stage}\" on this machine. Run gkm secrets:init --stage ${stage} first.`,\n\t\t);\n\t\tthis.name = 'NoLocalSecrets';\n\t}\n}\n\n/** A pull for a stage the store holds nothing for. */\nexport class NoStoredSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`The store holds no secrets for stage \"${stage}\". Push them first: gkm secrets:push --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoStoredSecrets';\n\t}\n}\n\nasync function reconcile(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): Promise<TransferResult> {\n\tconst { derivedContainers } = await import('../reconcile/workspace.js');\n\tconst result = reconcileMissingSecrets(\n\t\tsecrets,\n\t\tworkspace,\n\t\tawait derivedContainers(workspace, stage),\n\t);\n\treturn result\n\t\t? { secrets: result.secrets, addedKeys: result.addedKeys }\n\t\t: { secrets, addedKeys: [] };\n}\n\nfunction remoteOnly(workspace: NormalizedWorkspace, stage: string): void {\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);\n}\n\n/** Send this machine's secrets for a deployed stage to its store. */\nexport async function pushStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst local = await readStageSecrets(stage, workspace.root);\n\tif (!local) throw new NoLocalSecrets(stage);\n\n\tconst result = await reconcile(local, workspace, stage);\n\tif (result.addedKeys.length) {\n\t\tawait writeStageSecrets(result.secrets, workspace.root);\n\t}\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tawait store.push(stage, result.secrets);\n\treturn result;\n}\n\n/** Bring a deployed stage's secrets from its store into the local copy. */\nexport async function pullStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tconst stored = await store.pull(stage);\n\tif (!stored) throw new NoStoredSecrets(stage);\n\n\tconst result = await reconcile(stored, workspace, stage);\n\tawait writeStageSecrets(result.secrets, workspace.root);\n\treturn result;\n}\n"],"mappings":"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"}
@@ -1,4 +1,81 @@
1
1
  import { ConstructManifest } from "@geekmidas/manifest";
2
+ //#region src/secrets/types.d.ts
3
+ /** Credentials for a specific service */
4
+ interface ServiceCredentials {
5
+ host: string;
6
+ port: number;
7
+ username: string;
8
+ password: string;
9
+ /** Database name (for postgres) */
10
+ database?: string;
11
+ /** Virtual host (for rabbitmq) */
12
+ vhost?: string;
13
+ /** Bucket name (for minio) */
14
+ bucket?: string;
15
+ /** Access key ID (for localstack) */
16
+ accessKeyId?: string;
17
+ /** Region (for localstack) */
18
+ region?: string;
19
+ }
20
+ /** Stage secrets configuration */
21
+ interface StageSecrets {
22
+ /** Stage name (e.g., 'production', 'staging') */
23
+ stage: string;
24
+ /** ISO timestamp when secrets were created */
25
+ createdAt: string;
26
+ /** ISO timestamp when secrets were last updated */
27
+ updatedAt: string;
28
+ /** Event backend type (if events are enabled) */
29
+ eventsBackend?: EventsBackend;
30
+ /** Service-specific credentials */
31
+ services: {
32
+ postgres?: ServiceCredentials;
33
+ redis?: ServiceCredentials;
34
+ rabbitmq?: ServiceCredentials;
35
+ minio?: ServiceCredentials;
36
+ mailpit?: ServiceCredentials;
37
+ localstack?: ServiceCredentials;
38
+ pgboss?: ServiceCredentials;
39
+ };
40
+ /** Generated connection URLs */
41
+ urls: {
42
+ DATABASE_URL?: string;
43
+ REDIS_URL?: string;
44
+ RABBITMQ_URL?: string;
45
+ STORAGE_ENDPOINT?: string;
46
+ SMTP_HOST?: string;
47
+ SMTP_PORT?: string;
48
+ EVENT_PUBLISHER_CONNECTION_STRING?: string;
49
+ EVENT_SUBSCRIBER_CONNECTION_STRING?: string;
50
+ };
51
+ /** Custom user-defined secrets */
52
+ custom: Record<string, string>;
53
+ }
54
+ //#endregion
55
+ //#region src/secrets/store.d.ts
56
+ interface SecretsStore {
57
+ /** The stage's secrets, or null if the store holds none for it. */
58
+ pull(stage: string): Promise<StageSecrets | null>;
59
+ /** Replace the stage's secrets in the store. */
60
+ push(stage: string, secrets: StageSecrets): Promise<void>;
61
+ }
62
+ /** Secrets in SSM Parameter Store, in the account of the active credentials. */
63
+ interface SsmSecretsStoreConfig {
64
+ provider: 'ssm';
65
+ region: string;
66
+ }
67
+ /** Any other backend: an object implementing {@link SecretsStore}. */
68
+ interface CustomSecretsStoreConfig {
69
+ provider: SecretsStore;
70
+ }
71
+ /**
72
+ * `secrets.store` in gkm.config.ts.
73
+ *
74
+ * `'file'` — the default — keeps each stage's secrets in the encrypted
75
+ * `.gkm/secrets/<stage>.json` beside its key in `~/.gkm/`. It cannot serve a
76
+ * deploy from CI while `.gkm/` is gitignored; `ssm` or a custom store can.
77
+ */
78
+ type SecretsStoreConfig = 'file' | SsmSecretsStoreConfig | CustomSecretsStoreConfig;
2
79
  //#endregion
3
80
  //#region src/deploy/state.d.ts
4
81
  /**
@@ -4988,6 +5065,46 @@ declare const WorkspaceConfigSchema: ZodObject<{
4988
5065
  }, $strip>;
4989
5066
  secrets: ZodOptional<ZodObject<{
4990
5067
  enabled: ZodOptional<ZodBoolean>;
5068
+ store: ZodOptional<ZodUnion<readonly [ZodLiteral<"file">, ZodObject<{
5069
+ provider: ZodLiteral<"ssm">;
5070
+ region: ZodEnum<{
5071
+ "af-south-1": "af-south-1";
5072
+ "ap-east-1": "ap-east-1";
5073
+ "ap-northeast-1": "ap-northeast-1";
5074
+ "ap-northeast-2": "ap-northeast-2";
5075
+ "ap-northeast-3": "ap-northeast-3";
5076
+ "ap-south-1": "ap-south-1";
5077
+ "ap-south-2": "ap-south-2";
5078
+ "ap-southeast-1": "ap-southeast-1";
5079
+ "ap-southeast-2": "ap-southeast-2";
5080
+ "ap-southeast-3": "ap-southeast-3";
5081
+ "ap-southeast-4": "ap-southeast-4";
5082
+ "ca-central-1": "ca-central-1";
5083
+ "eu-central-1": "eu-central-1";
5084
+ "eu-central-2": "eu-central-2";
5085
+ "eu-north-1": "eu-north-1";
5086
+ "eu-south-1": "eu-south-1";
5087
+ "eu-south-2": "eu-south-2";
5088
+ "eu-west-1": "eu-west-1";
5089
+ "eu-west-2": "eu-west-2";
5090
+ "eu-west-3": "eu-west-3";
5091
+ "me-central-1": "me-central-1";
5092
+ "me-south-1": "me-south-1";
5093
+ "sa-east-1": "sa-east-1";
5094
+ "us-east-1": "us-east-1";
5095
+ "us-east-2": "us-east-2";
5096
+ "us-west-1": "us-west-1";
5097
+ "us-west-2": "us-west-2";
5098
+ }>;
5099
+ }, $strict>, ZodObject<{
5100
+ provider: ZodCustom<{
5101
+ pull: Function;
5102
+ push: Function;
5103
+ }, {
5104
+ pull: Function;
5105
+ push: Function;
5106
+ }>;
5107
+ }, $strip>]>>;
4991
5108
  algorithm: ZodOptional<ZodString>;
4992
5109
  kdf: ZodOptional<ZodEnum<{
4993
5110
  pbkdf2: "pbkdf2";
@@ -5302,6 +5419,14 @@ interface StagesConfig {
5302
5419
  interface SecretsConfig {
5303
5420
  /** Enable encrypted secrets (default: true) */
5304
5421
  enabled?: boolean;
5422
+ /**
5423
+ * Where deployed stages' secrets live, so a deploy can reach them from
5424
+ * anywhere: `'file'` (default — the encrypted `.gkm/secrets/<stage>.json`,
5425
+ * which cannot serve CI while `.gkm/` is gitignored), `{ provider: 'ssm',
5426
+ * region }` (each stage's secrets in its own AWS account), or any object
5427
+ * implementing `SecretsStore`. The local stage always uses the file.
5428
+ */
5429
+ store?: SecretsStoreConfig;
5305
5430
  /** Encryption algorithm (default: 'aes-256-gcm') */
5306
5431
  algorithm?: string;
5307
5432
  /** Key derivation function (default: 'scrypt') */
@@ -5891,6 +6016,16 @@ interface ProductionConfig {
5891
6016
  */
5892
6017
  optimizedHandlers?: boolean;
5893
6018
  }
6019
+ /**
6020
+ * Supported event backend types.
6021
+ *
6022
+ * - `pgboss`: Reuses PostgreSQL with a dedicated user/schema. Auto-enables db service.
6023
+ * - `sns`: Adds LocalStack container with SNS+SQS services.
6024
+ * - `rabbitmq`: Adds RabbitMQ container.
6025
+ *
6026
+ * All backends generate EVENT_PUBLISHER_CONNECTION_STRING and EVENT_SUBSCRIBER_CONNECTION_STRING.
6027
+ */
6028
+ type EventsBackend = 'pgboss' | 'sns' | 'rabbitmq';
5894
6029
  /**
5895
6030
  * Where a declared cache actually lives.
5896
6031
  *
@@ -6057,6 +6192,8 @@ interface GkmConfig {
6057
6192
  name?: string;
6058
6193
  /** The project's stages: which one is local, which deploy */
6059
6194
  stages: StagesConfig;
6195
+ /** Encrypted secrets, and where deployed stages' secrets are stored */
6196
+ secrets?: SecretsConfig;
6060
6197
  /**
6061
6198
  * Constructs glob pattern — one glob, every kind.
6062
6199
  *
@@ -6162,4 +6299,4 @@ interface GkmConfig {
6162
6299
  }
6163
6300
  //#endregion
6164
6301
  export { SUPPORTED_DEPLOY_TARGETS as A, WorkspaceInput as C, FrontendFramework as D, Framework as E, isPhase2DeployTarget as F, safeValidateWorkspaceConfig as I, validateWorkspaceConfig as L, formatValidationErrors as M, getDeployTargetError as N, MobileFramework as O, isDeployTargetSupported as P, WorkspaceConfig as S, BackendFramework as T, ModelsConfig as _, Routes as a, SecretsConfig as b, AppInput as c, DeployConfig as d, DeployTarget as f, LoadedConfig as g, InferredWorkspaceConfig as h, OpenApiConfig as i, WorkspaceConfigSchema as j, PHASE_2_DEPLOY_TARGETS as k, AppsRecord as l, InferAppNames as m, EmailBackend as n, AppConfig as o, DokployWorkspaceConfig as p, GkmConfig as r, AppConfigInput as s, CacheBackend as t, ConstrainedApps as u, NormalizedAppConfig as v, isWorkspaceConfig as w, SharedConfig as x, NormalizedWorkspace as y };
6165
- //# sourceMappingURL=types-RhXmHR-e.d.cts.map
6302
+ //# sourceMappingURL=types-7R81hu0i.d.mts.map