@auth-craft/aws-cf-stack 1.9.0 → 1.10.1

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.
package/.env.example CHANGED
@@ -167,6 +167,9 @@ LAMBDA_LOG_RETENTION_DAYS=
167
167
  # X-Ray tracing is billed per recorded trace. Unset → disabled in dev, active otherwise.
168
168
  # active | disabled | pass_through
169
169
  LAMBDA_TRACING=
170
+ # Adding missing GSIs to an existing table (one per CloudFormation update): max minutes to
171
+ # wait for each to backfill before giving up (re-running resumes). Unset → 60.
172
+ AUTH_CRAFT_GSI_WAIT_MINUTES=
170
173
  LAMBDA_APP_URL=
171
174
  LAMBDA_SUPPORT_EMAIL=
172
175
  LAMBDA_CORS_ORIGINS=
package/README.md CHANGED
@@ -87,7 +87,9 @@ all point at the single worker.
87
87
 
88
88
  - `aws` CLI authenticated (or `AWS_*` creds in env), `cdk` (or `npx cdk`), `wrangler`,
89
89
  `node` ≥ 20, `jq`, `curl`.
90
- - AWS + Cloudflare credentials.
90
+ - AWS + Cloudflare credentials. Besides the CDK deploy roles, the identity running the CLI
91
+ needs `dynamodb:DescribeTable` on the `<app>-<stage>-<layer>-data` table and
92
+ `cloudformation:GetTemplate` on the data stack (see [Table schema & GSI rollout](#table-schema--gsi-rollout)).
91
93
 
92
94
  ## Usage
93
95
 
@@ -163,6 +165,38 @@ CF_EDGE_GATE_SECRET="$SNAP_EDGE" CLOUDFLARE_ACCOUNT_ID="$SNAP_CF_ACCT" \
163
165
  npx auth-craft-stack all --stage "$STAGE"
164
166
  ```
165
167
 
168
+ ## Table schema & GSI rollout
169
+
170
+ The table's keys and GSIs come from one source: `TABLE_SCHEMA` in
171
+ `@auth-craft/database-plugin-dynamodb`, snapshotted into `cdk/lib/table-schema.json` at build
172
+ time. The CDK stack, the integration tests and the create-table script all read it, so a deployed
173
+ table can no longer miss an index the repositories query.
174
+
175
+ CloudFormation creates or deletes **at most one GSI per table update**. A new table gets every GSI
176
+ at once, but an existing table deployed by an older version may lack several. Before the normal
177
+ deploy, `lambda` therefore:
178
+
179
+ 1. `DescribeTable`s the existing table (nothing to do if it doesn't exist yet).
180
+ 2. Deploys the data stack with the table's current GSIs **plus the next missing one**.
181
+ 3. Waits for that GSI to finish backfilling (`ACTIVE`), then repeats until the schema is complete.
182
+
183
+ Each GSI takes a few minutes on a populated table; the wait is capped by
184
+ `AUTH_CRAFT_GSI_WAIT_MINUTES` (default 60) and an interrupted run resumes where it stopped.
185
+
186
+ GSIs are compared by their full definition (keys, key attribute types, projection), not by name.
187
+ The CLI stops before deploying, and prints how to fix it, when:
188
+
189
+ - the table has a GSI that CloudFormation doesn't know about (created by hand);
190
+ - the stack declares a GSI the table lacks (deleted by hand). The CLI prints the
191
+ `aws dynamodb update-table` command that recreates it as the stack declares it;
192
+ - the table has a GSI the schema no longer declares;
193
+ - a GSI's definition differs from the schema. CloudFormation would have to replace it, which is
194
+ two GSI operations in one update. A changed index needs a new name in `TABLE_SCHEMA`;
195
+ - a rollout step finished without adding its GSI.
196
+
197
+ The GSI subset for each rollout step goes to the CDK app as context (`-c dataGsis=...`), never as
198
+ an environment variable, so nothing in your shell or `--env-file` can shrink the full deploy.
199
+
166
200
  ## Observability cost controls (log retention, tracing)
167
201
 
168
202
  CloudWatch Logs and X-Ray are usually the two line items that make a low-traffic **dev**
@@ -363,6 +397,11 @@ time). Bump the version to deploy newer auth-craft.
363
397
  already-deployed queue. The stack grants the Lambda `sqs:SendMessage` on that ARN, but it
364
398
  can't create a cross-stack queue — deploy your email/queue stack first, then this one.
365
399
 
400
+ - **`DATABASE_QUERY_ERROR` from invite flows on a table deployed by ≤ 1.9.0** — the table lacks
401
+ `GSI_InviteToken` / `GSI_InviteeEmail` / `GSI_InviteePhone`. Deploy `lambda` with ≥ 1.10.0; it
402
+ adds them one at a time. The Lambda log names the cause:
403
+ `ValidationException: The table does not have the specified index: …`.
404
+
366
405
  ## Email delivery modes
367
406
 
368
407
  `LAMBDA_EMAIL_MODE` = `console` (default) | `smtp` | `queue`.
@@ -382,6 +421,9 @@ time). Bump the version to deploy newer auth-craft.
382
421
  (ESM, `node:*` external — provided by the Workers runtime), the same way wrangler bundles
383
422
  internally. Uses the esbuild binary in the app's `node_modules` (the root `.bin/esbuild`
384
423
  symlink can be stale because esbuild is in pnpm `ignoredBuiltDependencies`).
424
+ - **Table schema**: `scripts/sync-table-schema.mjs` writes `cdk/lib/table-schema.json` from
425
+ `TABLE_SCHEMA` (database-plugin-dynamodb is unpublished, so the CDK app can't import it at the
426
+ consumer). The JSON is committed; `pnpm test` fails if it's stale (`pnpm sync:table-schema`).
385
427
  - **Admin CLI**: esbuilds `packages/auth-admin-cli/src/index.ts` → `assets/admin/cli.mjs`,
386
428
  fully inlined (auth-core + plugins + `pg` + aws-sdk). It is **bundled, not a dependency**,
387
429
  because `@auth-craft/auth-admin-cli` is `private` and pulls in ~7 workspace packages —
@@ -389,6 +431,5 @@ time). Bump the version to deploy newer auth-craft.
389
431
 
390
432
  The bundled `cdk/lib/lambda-stack.ts` deploys the Lambda via
391
433
  `lambda.Code.fromAsset('../../assets/lambda')` + handler `index.handler` (not `NodejsFunction`),
392
- so no source build happens at the consumer's deploy time. It is a **copy** of the app's
393
- `infrastructure/lib/lambda-stack.ts` — keep `cdk/lib/*.ts` in sync until a future refactor
394
- unifies them.
434
+ so no source build happens at the consumer's deploy time. These `cdk/lib/*.ts` stacks are the
435
+ only CDK definition of the auth deploy — the app's old `infrastructure/` copy was removed.