@venturekit/infra 0.0.0-dev.20260514025219 → 0.0.0-dev.20260515022321

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 (49) hide show
  1. package/dist/cdk/app-stack.d.ts +330 -0
  2. package/dist/cdk/app-stack.d.ts.map +1 -0
  3. package/dist/cdk/app-stack.js +875 -0
  4. package/dist/cdk/app-stack.js.map +1 -0
  5. package/dist/cdk/config-stack.d.ts +104 -0
  6. package/dist/cdk/config-stack.d.ts.map +1 -0
  7. package/dist/cdk/config-stack.js +161 -0
  8. package/dist/cdk/config-stack.js.map +1 -0
  9. package/dist/cdk/data-stack.d.ts +198 -0
  10. package/dist/cdk/data-stack.d.ts.map +1 -0
  11. package/dist/cdk/data-stack.js +511 -0
  12. package/dist/cdk/data-stack.js.map +1 -0
  13. package/dist/cdk/edge-stack.d.ts +91 -0
  14. package/dist/cdk/edge-stack.d.ts.map +1 -0
  15. package/dist/cdk/edge-stack.js +364 -0
  16. package/dist/cdk/edge-stack.js.map +1 -0
  17. package/dist/cdk/identity-stack.d.ts +98 -0
  18. package/dist/cdk/identity-stack.d.ts.map +1 -0
  19. package/dist/cdk/identity-stack.js +254 -0
  20. package/dist/cdk/identity-stack.js.map +1 -0
  21. package/dist/cdk/index.d.ts +16 -0
  22. package/dist/cdk/index.d.ts.map +1 -1
  23. package/dist/cdk/index.js +17 -0
  24. package/dist/cdk/index.js.map +1 -1
  25. package/dist/cdk/messaging-stack.d.ts +109 -0
  26. package/dist/cdk/messaging-stack.d.ts.map +1 -0
  27. package/dist/cdk/messaging-stack.js +320 -0
  28. package/dist/cdk/messaging-stack.js.map +1 -0
  29. package/dist/cdk/network-stack.d.ts +87 -0
  30. package/dist/cdk/network-stack.d.ts.map +1 -0
  31. package/dist/cdk/network-stack.js +265 -0
  32. package/dist/cdk/network-stack.js.map +1 -0
  33. package/dist/cdk/shared/cross-stack-refs.d.ts +278 -0
  34. package/dist/cdk/shared/cross-stack-refs.d.ts.map +1 -0
  35. package/dist/cdk/shared/cross-stack-refs.js +326 -0
  36. package/dist/cdk/shared/cross-stack-refs.js.map +1 -0
  37. package/dist/cdk/shared/lambda-helpers.d.ts +82 -0
  38. package/dist/cdk/shared/lambda-helpers.d.ts.map +1 -0
  39. package/dist/cdk/shared/lambda-helpers.js +201 -0
  40. package/dist/cdk/shared/lambda-helpers.js.map +1 -0
  41. package/dist/cdk/shared/ssm-keys.d.ts +460 -0
  42. package/dist/cdk/shared/ssm-keys.d.ts.map +1 -0
  43. package/dist/cdk/shared/ssm-keys.js +291 -0
  44. package/dist/cdk/shared/ssm-keys.js.map +1 -0
  45. package/dist/cdk/stack.d.ts +3 -11
  46. package/dist/cdk/stack.d.ts.map +1 -1
  47. package/dist/cdk/stack.js +184 -41
  48. package/dist/cdk/stack.js.map +1 -1
  49. package/package.json +2 -2
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Tier 2 — Identity stack.
3
+ *
4
+ * Owns:
5
+ * - Cognito User Pool(s) — one per declared `auth` intent
6
+ * - App Client(s) per pool (`auth.clients[]`, defaulting to one
7
+ * `'default'` client when omitted)
8
+ * - Federated provider Secrets Manager placeholder(s) holding
9
+ * `{clientId, clientSecret}` for Google / Apple / Meta /
10
+ * other OAuth providers declared in `auth.federated[]`
11
+ *
12
+ * # Lifecycle position
13
+ *
14
+ * Updated rarely (~quarterly): MFA toggle, password-policy bump,
15
+ * new federated provider, occasional new app client. Survives stack
16
+ * delete because losing a User Pool means losing every user
17
+ * identity + password hash — by far the most catastrophic
18
+ * recoverable state in a VentureKit deploy. Both `DeletionPolicy:
19
+ * Retain` AND Cognito's native `deletionProtection` flag must be
20
+ * flipped before the pool can be deleted, and every flip is
21
+ * CloudTrail-logged.
22
+ *
23
+ * # Public surface (via SSM)
24
+ *
25
+ * - `identityKeys.userPoolId(authId)` — the Cognito pool ID
26
+ * - `identityKeys.userPoolArn(authId)` — for IAM grant scoping
27
+ * in the app stack
28
+ * - `identityKeys.appClientId(authId)` — first declared client
29
+ * (the canonical one app code reads)
30
+ * - `identityKeys.federatedSecretArn(authId, provider)` per
31
+ * provider in `auth.federated[]`
32
+ *
33
+ * # What this stack does NOT do
34
+ *
35
+ * - **No IAM grants.** Lambdas need
36
+ * `cognito-idp:AdminCreateUser` / `AdminInitiateAuth` /
37
+ * `AdminConfirmSignUp` etc. on the pool, and `secretsmanager:Get
38
+ * SecretValue` on each federated provider Secret. Those grants
39
+ * produce `AWS::IAM::Policy` resources attached to the
40
+ * shared-Lambda-role — which lives in the app stack. The app
41
+ * stack imports the pool ARN + secret ARNs via SSM and applies
42
+ * the grants there.
43
+ *
44
+ * - **No env-var injection.** The legacy single-stack code
45
+ * side-effected `this.ventureBaseEnv['COGNITO_USER_POOL_ID'] =
46
+ * userPool.userPoolId` to broadcast the pool ID into every
47
+ * Lambda's environment at synth time. Cross-stack, the app
48
+ * stack pulls the pool ID via `readSsmString` and injects it
49
+ * into Lambda env-vars itself — same broadcast, lock-free.
50
+ *
51
+ * - **No Cognito Hosted UI / IdP wiring.** VentureKit deliberately
52
+ * doesn't enable Cognito's Hosted UI or wire the federated
53
+ * providers as Cognito Identity Providers. Your app owns the
54
+ * login screen; the SPA does the OAuth dance with the IdP
55
+ * directly and posts the resulting tokens to your API.
56
+ * `signInAsFederatedUser` from `@venturekit/auth/server`
57
+ * verifies them and mints Cognito session tokens via
58
+ * `AdminInitiateAuth` — which is why the federated providers
59
+ * need `ALLOW_ADMIN_USER_PASSWORD_AUTH` enabled on the app
60
+ * client below.
61
+ */
62
+ import * as cdk from 'aws-cdk-lib';
63
+ import * as cognito from 'aws-cdk-lib/aws-cognito';
64
+ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
65
+ import { DATA_SAFETY_CONFIG } from '@venturekit/core';
66
+ import { writeSsm } from './shared/cross-stack-refs.js';
67
+ import { identityKeys } from './shared/ssm-keys.js';
68
+ export class VentureIdentityStack extends cdk.Stack {
69
+ /**
70
+ * In-process accessors for sibling stacks (the app stack reads
71
+ * the pool reference for IAM grants when deployed in the same
72
+ * `cdk.App`). Keyed by `auth.id`.
73
+ */
74
+ userPools = new Map();
75
+ appClients = new Map();
76
+ /**
77
+ * Federated provider secrets, keyed by `${authId}:${provider}`.
78
+ * Order-stable for tests; the actual app stack consumer reads via
79
+ * SSM rather than this in-memory map.
80
+ */
81
+ federatedSecrets = new Map();
82
+ isStrictRetain;
83
+ constructor(scope, id, props) {
84
+ super(scope, id, props);
85
+ const { projectName, stage, envConfig, infrastructure } = props;
86
+ const dataSafetyConfig = DATA_SAFETY_CONFIG[envConfig.dataSafety];
87
+ this.isStrictRetain = dataSafetyConfig.removalPolicy === 'retain';
88
+ if (!infrastructure.auth)
89
+ return;
90
+ for (const intent of infrastructure.auth) {
91
+ this.createAuth(intent, projectName, stage, envConfig);
92
+ }
93
+ }
94
+ createAuth(intent, projectName, stage, envConfig) {
95
+ const isStrictRetain = this.isStrictRetain;
96
+ // Stable logical ID: `auth-{intent.id}` — preset changes never
97
+ // trigger a replacement (which would orphan every user record).
98
+ const logicalId = `auth-${intent.id}`;
99
+ const passwordPolicy = intent.passwordStrength === 'strong'
100
+ ? {
101
+ minLength: 12,
102
+ requireLowercase: true,
103
+ requireUppercase: true,
104
+ requireDigits: true,
105
+ requireSymbols: true,
106
+ }
107
+ : {
108
+ minLength: 8,
109
+ requireLowercase: true,
110
+ requireUppercase: true,
111
+ requireDigits: true,
112
+ requireSymbols: false,
113
+ };
114
+ // Custom user attributes are declared as mutable strings so they
115
+ // appear as `custom:<name>` claims on issued JWTs — and so the
116
+ // `vk dev` cognito-local provisioning, which mirrors this Schema,
117
+ // matches production exactly. Application code that reads
118
+ // `custom:tenantId` etc. depends on these being declared on the
119
+ // pool.
120
+ const customAttrNames = Array.isArray(intent.customAttributes)
121
+ ? intent.customAttributes
122
+ : [];
123
+ const customAttributes = {};
124
+ for (const name of customAttrNames) {
125
+ customAttributes[name] = new cognito.StringAttribute({
126
+ minLen: 1,
127
+ maxLen: 2048,
128
+ mutable: true,
129
+ });
130
+ }
131
+ const userPool = new cognito.UserPool(this, logicalId, {
132
+ userPoolName: `${projectName}-${intent.id}-${stage}`,
133
+ // Self-signup defaults OFF — most production apps use invite
134
+ // flows; opt in with `allowSignUp: true` for consumer products.
135
+ selfSignUpEnabled: intent.allowSignUp ?? false,
136
+ // Email + phone aliases let a user authenticate with either
137
+ // without a duplicate account, mirroring the §1 user model
138
+ // where buyers register with one and log in with the other.
139
+ signInAliases: {
140
+ email: intent.signInWith?.includes('email') ?? true,
141
+ phone: intent.signInWith?.includes('phone') ?? false,
142
+ username: intent.signInWith?.includes('username') ?? false,
143
+ },
144
+ autoVerify: {
145
+ email: intent.signInWith?.includes('email') ?? true,
146
+ phone: intent.signInWith?.includes('phone') ?? false,
147
+ },
148
+ passwordPolicy,
149
+ mfa: intent.mfa === 'required'
150
+ ? cognito.Mfa.REQUIRED
151
+ : intent.mfa === 'optional'
152
+ ? cognito.Mfa.OPTIONAL
153
+ : cognito.Mfa.OFF,
154
+ removalPolicy: isStrictRetain ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY,
155
+ // Native Cognito deletion-protection on top of CFN RETAIN.
156
+ // CFN-level RETAIN only blocks CFN-driven deletes; the pool
157
+ // can still be deleted via console / API / CLI. Both layers
158
+ // must be flipped before deletion — every flip CloudTrail-
159
+ // logged. Losing a User Pool means losing every user identity
160
+ // + password hash; users must re-register via password reset.
161
+ deletionProtection: isStrictRetain,
162
+ // Advanced security mode (compromise detection, risk-based
163
+ // adaptive authentication) for medium+ presets. Carries a
164
+ // per-MAU cost; off on free/nano.
165
+ advancedSecurityMode: envConfig.lambda.memoryMb >= 512
166
+ ? cognito.AdvancedSecurityMode.ENFORCED
167
+ : undefined,
168
+ accountRecovery: cognito.AccountRecovery.EMAIL_ONLY,
169
+ ...(customAttrNames.length > 0 ? { customAttributes } : {}),
170
+ });
171
+ if (isStrictRetain) {
172
+ const cfnPool = userPool.node.defaultChild;
173
+ cfnPool.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN;
174
+ }
175
+ this.userPools.set(intent.id, userPool);
176
+ // ── App clients ───────────────────────────────────────────────
177
+ const clients = intent.clients && intent.clients.length > 0
178
+ ? intent.clients
179
+ : [{ name: 'default' }];
180
+ // When federated providers are declared, the federated sign-in
181
+ // helper in `@venturekit/auth/server` mints session tokens
182
+ // through `AdminInitiateAuth` after creating/looking up the user
183
+ // via `AdminCreateUser` / `AdminGetUser`. That flow needs
184
+ // `ALLOW_ADMIN_USER_PASSWORD_AUTH` enabled on the app client.
185
+ // Password sign-in keeps using the public `USER_PASSWORD_AUTH`
186
+ // path so we leave both turned on.
187
+ const federatedProviders = Array.isArray(intent.federated)
188
+ ? intent.federated
189
+ : [];
190
+ const enableAdminAuth = federatedProviders.length > 0;
191
+ let primaryClient;
192
+ for (const client of clients) {
193
+ const appClient = userPool.addClient(client.name, {
194
+ authFlows: {
195
+ userPassword: true,
196
+ userSrp: true,
197
+ ...(enableAdminAuth ? { adminUserPassword: true } : {}),
198
+ },
199
+ // Hide whether a username exists from failed sign-in errors —
200
+ // standard credential-stuffing / user-enumeration defense.
201
+ preventUserExistenceErrors: true,
202
+ });
203
+ if (!primaryClient) {
204
+ primaryClient = appClient;
205
+ }
206
+ }
207
+ // `clients` always has ≥1 entry (defaulted above).
208
+ this.appClients.set(intent.id, primaryClient);
209
+ // ── Public SSM surface for the pool ────────────────────────────
210
+ writeSsm(this, identityKeys.userPoolId(projectName, stage, intent.id), userPool.userPoolId, {
211
+ description: `Cognito User Pool ID for auth '${intent.id}'`,
212
+ });
213
+ writeSsm(this, identityKeys.userPoolArn(projectName, stage, intent.id), userPool.userPoolArn, {
214
+ description: `Cognito User Pool ARN — IAM scoping target for AdminInitiateAuth etc.`,
215
+ });
216
+ writeSsm(this, identityKeys.primaryClientId(projectName, stage, intent.id), primaryClient.userPoolClientId, { description: `Primary Cognito App Client ID for auth '${intent.id}'` });
217
+ // ── Federated identity-provider secret placeholders ────────────
218
+ // Each declared third-party provider gets a Secrets Manager
219
+ // placeholder holding `{clientId, clientSecret}`. Operators paste
220
+ // the real values from the Google Cloud / Meta for Developers /
221
+ // Apple Developer console after the first deploy:
222
+ //
223
+ // aws secretsmanager put-secret-value --secret-id <arn> \
224
+ // --secret-string '{"clientId":"…","clientSecret":"…"}'
225
+ //
226
+ // The ARN flows to Lambdas via SSM (the app stack reads
227
+ // `identityKeys.federatedSecretArn(...)` and injects
228
+ // `COGNITO_FEDERATED_<PROVIDER>_SECRET_ARN` at synth time).
229
+ for (const provider of federatedProviders) {
230
+ const secret = new secretsmanager.Secret(this, `${logicalId}-federated-${provider}`, {
231
+ secretName: `venturekit/${projectName}/${stage}/auth/${intent.id}/${provider}`,
232
+ description: `OAuth client credentials for ${provider} federated sign-in ` +
233
+ `on auth pool '${intent.id}'. Populate after first deploy.`,
234
+ secretObjectValue: {
235
+ clientId: cdk.SecretValue.unsafePlainText('PLACEHOLDER'),
236
+ clientSecret: cdk.SecretValue.unsafePlainText('PLACEHOLDER'),
237
+ },
238
+ // Federated provider credentials are operator-set values
239
+ // pasted from third-party developer consoles. They CAN be
240
+ // re-generated, but doing so requires manually revisiting
241
+ // that console — we'd rather not force that on every
242
+ // stack-replace event. Promote to RETAIN under strict so
243
+ // accidental stack deletes don't wipe them.
244
+ removalPolicy: isStrictRetain ? cdk.RemovalPolicy.RETAIN : cdk.RemovalPolicy.DESTROY,
245
+ });
246
+ this.federatedSecrets.set(`${intent.id}:${provider}`, secret);
247
+ writeSsm(this, identityKeys.federatedSecretArn(projectName, stage, intent.id, provider), secret.secretArn, {
248
+ description: `Federated ${provider} OAuth secret ARN for auth pool '${intent.id}'. ` +
249
+ `Operators populate via 'aws secretsmanager put-secret-value'.`,
250
+ });
251
+ }
252
+ }
253
+ }
254
+ //# sourceMappingURL=identity-stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity-stack.js","sourceRoot":"","sources":["../../src/cdk/identity-stack.ts"],"names":[],"mappings":"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"}
@@ -7,4 +7,20 @@ export { VentureStack, discoverRouteFiles } from './stack.js';
7
7
  export type { VentureStackProps, DiscoveredRoute } from './stack.js';
8
8
  export { MigrationRunner, hasMigrations } from './migration-runner.js';
9
9
  export type { MigrationRunnerProps } from './migration-runner.js';
10
+ export { VentureNetworkStack } from './network-stack.js';
11
+ export type { VentureNetworkStackProps } from './network-stack.js';
12
+ export { VentureDataStack } from './data-stack.js';
13
+ export type { VentureDataStackProps } from './data-stack.js';
14
+ export { VentureIdentityStack } from './identity-stack.js';
15
+ export type { VentureIdentityStackProps } from './identity-stack.js';
16
+ export { VentureConfigStack } from './config-stack.js';
17
+ export type { VentureConfigStackProps } from './config-stack.js';
18
+ export { VentureMessagingStack } from './messaging-stack.js';
19
+ export type { VentureMessagingStackProps } from './messaging-stack.js';
20
+ export { VentureEdgeStack } from './edge-stack.js';
21
+ export type { VentureEdgeStackProps } from './edge-stack.js';
22
+ export { VentureAppStack } from './app-stack.js';
23
+ export type { VentureAppStackProps, AppStackImports } from './app-stack.js';
24
+ export { writeSsm, writeSsmStringList, readSsmString, readSsmStringList, importVpc, importRdsConnection, importBucket, importDynamoTable, importUserPool, importQueue, importTopic, importSecret, importHttpApi, logicalIdForParameter, } from './shared/cross-stack-refs.js';
25
+ export { ssmKeys, networkKeys, dataKeys, identityKeys, configKeys, messagingKeys, edgeKeys, } from './shared/ssm-keys.js';
10
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cdk/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC9D,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACvE,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cdk/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAC9D,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACvE,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAOlE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,YAAY,EAAE,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,YAAY,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,YAAY,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAK5E,OAAO,EACL,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,SAAS,EACT,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,WAAW,EACX,YAAY,EACZ,aAAa,EACb,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,OAAO,EACP,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,aAAa,EACb,QAAQ,GACT,MAAM,sBAAsB,CAAC"}
package/dist/cdk/index.js CHANGED
@@ -5,4 +5,21 @@
5
5
  */
6
6
  export { VentureStack, discoverRouteFiles } from './stack.js';
7
7
  export { MigrationRunner, hasMigrations } from './migration-runner.js';
8
+ // ── Multi-stack tier classes (plan §11) ────────────────────────────
9
+ // Lifecycle-aligned split. The CLI's `.vk/cdk/app.ts` generator
10
+ // instantiates each tier conditionally based on declared intents.
11
+ // `VentureStack` (legacy single-stack) stays exported alongside
12
+ // these until plan §11 step 10 deletes it.
13
+ export { VentureNetworkStack } from './network-stack.js';
14
+ export { VentureDataStack } from './data-stack.js';
15
+ export { VentureIdentityStack } from './identity-stack.js';
16
+ export { VentureConfigStack } from './config-stack.js';
17
+ export { VentureMessagingStack } from './messaging-stack.js';
18
+ export { VentureEdgeStack } from './edge-stack.js';
19
+ export { VentureAppStack } from './app-stack.js';
20
+ // ── Cross-stack helpers ────────────────────────────────────────────
21
+ // Re-exported from `shared/` so consumers (other tier classes today,
22
+ // the generated app.ts tomorrow) import from a single entry point.
23
+ export { writeSsm, writeSsmStringList, readSsmString, readSsmStringList, importVpc, importRdsConnection, importBucket, importDynamoTable, importUserPool, importQueue, importTopic, importSecret, importHttpApi, logicalIdForParameter, } from './shared/cross-stack-refs.js';
24
+ export { ssmKeys, networkKeys, dataKeys, identityKeys, configKeys, messagingKeys, edgeKeys, } from './shared/ssm-keys.js';
8
25
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cdk/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cdk/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAE9D,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAGvE,sEAAsE;AACtE,gEAAgE;AAChE,kEAAkE;AAClE,gEAAgE;AAChE,2CAA2C;AAC3C,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,sEAAsE;AACtE,qEAAqE;AACrE,mEAAmE;AACnE,OAAO,EACL,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,SAAS,EACT,mBAAmB,EACnB,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,WAAW,EACX,WAAW,EACX,YAAY,EACZ,aAAa,EACb,qBAAqB,GACtB,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,OAAO,EACP,WAAW,EACX,QAAQ,EACR,YAAY,EACZ,UAAU,EACV,aAAa,EACb,QAAQ,GACT,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Tier 4 — Messaging stack.
3
+ *
4
+ * Owns the durable, persistent message-routing infrastructure. By
5
+ * design every resource here can outlive an app-stack tear-down:
6
+ * after `vk deploy` rebuilds the app stack, the new consumer
7
+ * Lambdas pick up exactly where the old ones left off because the
8
+ * queue + its in-flight messages survived.
9
+ *
10
+ * Resources:
11
+ *
12
+ * - One `AWS::SQS::Queue` + matching DLQ per `queues[]` intent.
13
+ * Encryption SQS_MANAGED, RETAIN under strict.
14
+ * - One Notify pipeline per `notify[]` intent:
15
+ * - SNS events topic (SES configuration-set destination)
16
+ * - SES `EmailIdentity` (domain or single-address)
17
+ * - Optional Route53 DKIM CNAMEs when `hostedZoneId` is set
18
+ * - SES `ConfigurationSet` + `EventDestination` publishing
19
+ * to the SNS topic
20
+ * - WhatsApp Cloud API token Secrets Manager placeholder
21
+ * when `'whatsapp'` is in `channels`
22
+ * - Two dedicated DLQs: dispatcher-DLQ and bounce-DLQ. The
23
+ * consumer Lambdas live in the app stack but their
24
+ * on-failure DLQs need RETAIN-class durability so
25
+ * operators can drain them on the rare delivery failure.
26
+ *
27
+ * # Lifecycle position
28
+ *
29
+ * Updated occasionally (~monthly): a new queue, a notify-channel
30
+ * toggle, a domain rotation. The contents (in-flight messages)
31
+ * are operationally critical — losing a queue with N messages in
32
+ * flight is a permanent message loss event. Both `DeletionPolicy:
33
+ * Retain` and AWS's lack of a queue-level "deletion protection"
34
+ * flag mean RETAIN is the only durability lever; we set it
35
+ * unconditionally under strict dataSafety.
36
+ *
37
+ * # Public surface (via SSM)
38
+ *
39
+ * - `messagingKeys.queueArn / queueUrl / queueDlqArn /
40
+ * queueVisibilityTimeoutSeconds` per `queues[]` intent
41
+ * - `messagingKeys.notifyDispatcherDlqArn /
42
+ * notifyBounceDlqArn` per `notify[]` intent
43
+ * - `configKeys.notifyEventsTopicArn /
44
+ * notifyConfigurationSetName / whatsappTokenSecretArn` per
45
+ * notify intent. (These straddle the messaging-vs-config
46
+ * boundary in the SSM key namespace because the operator pastes
47
+ * the WhatsApp token by hand and the SES configuration-set name
48
+ * is operator-facing identity, not just plumbing — but they're
49
+ * created here because they're inseparable from the SES /
50
+ * SNS topology.)
51
+ *
52
+ * # What this stack does NOT do
53
+ *
54
+ * - **No consumer Lambdas, no event-source mappings.** The Lambda
55
+ * that pulls from the queue lives in the app stack. The ESM
56
+ * binding it to the queue ALSO lives in the app stack — that
57
+ * way an app-stack redeploy can re-create the ESM cleanly without
58
+ * touching the persistent queue, which is the whole point of the
59
+ * split.
60
+ *
61
+ * - **No EventBridge `Rule`s.** Schedules + their Lambda targets
62
+ * stay together in the app stack — the Rule + the Lambda are
63
+ * inseparable by CDK's permission-grant mechanism, and a Rule on
64
+ * its own (with no targets) is dead infrastructure.
65
+ *
66
+ * - **No IAM grants.** App-stack Lambda-role wiring grants
67
+ * `sqs:SendMessage` / `sqs:ReceiveMessage` / `sns:Publish` /
68
+ * `ses:SendEmail` etc. on the imported messaging-tier resources.
69
+ *
70
+ * # Why the dispatcher / bounce DLQs live HERE not in the app stack
71
+ *
72
+ * Two reasons:
73
+ *
74
+ * 1. They're long-lived diagnostic surfaces. An app-stack
75
+ * redeploy that drops them means losing every accumulated
76
+ * delivery-failure record, which is exactly when the operator
77
+ * needs them most.
78
+ * 2. They're tier-stable. The dispatcher Lambda might rev
79
+ * monthly; its DLQ doesn't.
80
+ */
81
+ import * as cdk from 'aws-cdk-lib';
82
+ import * as sqs from 'aws-cdk-lib/aws-sqs';
83
+ import * as sns from 'aws-cdk-lib/aws-sns';
84
+ import * as secretsmanager from 'aws-cdk-lib/aws-secretsmanager';
85
+ import { Construct } from 'constructs';
86
+ import { type EnvConfig, type VentureIntent } from '@venturekit/core';
87
+ export interface VentureMessagingStackProps extends cdk.StackProps {
88
+ projectName: string;
89
+ stage: string;
90
+ envConfig: EnvConfig;
91
+ /** Reads `queues[]` and `notify[]`; ignores everything else. */
92
+ infrastructure: VentureIntent;
93
+ }
94
+ export declare class VentureMessagingStack extends cdk.Stack {
95
+ readonly queues: Map<string, {
96
+ queue: sqs.Queue;
97
+ dlq: sqs.Queue;
98
+ }>;
99
+ readonly notifyEventsTopics: Map<string, sns.Topic>;
100
+ readonly notifyDispatcherDlqs: Map<string, sqs.Queue>;
101
+ readonly notifyBounceDlqs: Map<string, sqs.Queue>;
102
+ readonly whatsappTokenSecrets: Map<string, secretsmanager.Secret>;
103
+ private readonly isStrictRetain;
104
+ private readonly removalPolicy;
105
+ constructor(scope: Construct, id: string, props: VentureMessagingStackProps);
106
+ private createQueue;
107
+ private createNotify;
108
+ }
109
+ //# sourceMappingURL=messaging-stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messaging-stack.d.ts","sourceRoot":"","sources":["../../src/cdk/messaging-stack.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+EG;AAEH,OAAO,KAAK,GAAG,MAAM,aAAa,CAAC;AACnC,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAC3C,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAG3C,OAAO,KAAK,cAAc,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAsB,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAI1F,MAAM,WAAW,0BAA2B,SAAQ,GAAG,CAAC,UAAU;IAChE,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,SAAS,CAAC;IACrB,gEAAgE;IAChE,cAAc,EAAE,aAAa,CAAC;CAC/B;AAED,qBAAa,qBAAsB,SAAQ,GAAG,CAAC,KAAK;IAClD,SAAgB,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC;QAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAA;KAAE,CAAC,CAAa;IACtF,SAAgB,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAa;IACvE,SAAgB,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAa;IACzE,SAAgB,gBAAgB,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAa;IACrE,SAAgB,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,MAAM,CAAC,CAAa;IAErF,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoB;gBAEtC,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,0BAA0B;IAoB3E,OAAO,CAAC,WAAW;IAqEnB,OAAO,CAAC,YAAY;CAqMrB"}