|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +Project-level instructions for future Claude Code sessions. Loaded on every session against this repo. |
| 4 | + |
| 5 | +## Project |
| 6 | + |
| 7 | +Reference architecture for serverless AWS Lambda + Powertools applications: three-stack CDK composition, five-rule-pack cdk-nag gating, end-to-end CMK encryption, WAF + CloudFront, CloudTrail data events, browser RUM with X-Ray correlation, Athena access-log analytics, and supply-chain hygiene. Designed to be forked via GitHub's "Use this template" — see the "Forking this template" section at the bottom. |
| 8 | + |
| 9 | +## Environments — two venvs, never mix |
| 10 | + |
| 11 | +CDK and Powertools require incompatible `attrs` versions (CDK pulls `attrs<26` via jsii; Powertools pulls `attrs>=26`). `[tool.uv.conflicts]` in `pyproject.toml` lets one `uv.lock` hold both resolutions, installed into separate venvs: |
| 12 | + |
| 13 | +- `.venv` — CDK workstation. Used for `cdk synth`, `cdk deploy`, stack-assertion tests, lint/format/typecheck of `hello_world/`. |
| 14 | +- `.venv-lambda` — Lambda runtime. Used for unit tests over `lambda/`, integration tests, the OpenAPI generator script. |
| 15 | + |
| 16 | +`make install` provisions both. The Makefile uses `UV_PROJECT_ENVIRONMENT=.venv-lambda` to switch into the runtime env without an activation dance. Never install Powertools into `.venv` or CDK into `.venv-lambda`. |
| 17 | + |
| 18 | +Run `make doctor` after `make install` to verify both venvs picked up the expected groups, `cdk`/`drawio` are on `PATH`, and pre-commit is wired. `make clean-venvs && make install` is the recovery path for a corrupted venv. |
| 19 | + |
| 20 | +## `cdk synth` must use `'**'` |
| 21 | + |
| 22 | +All three stacks live inside `HelloWorldStage` (a `cdk.Stage`). Bare `cdk synth` walks only the App's direct children, finds the Stage, doesn't recurse, and emits an empty synthesis that succeeds *without* running cdk-nag against the real stacks. `make cdk-synth` and the CI `cdk-check` job both invoke `cdk synth '**'`. If you run `cdk synth` directly during development, include the glob — otherwise the gate passes silently regardless of what cdk-nag would find. |
| 23 | + |
| 24 | +## cdk-nag is a hard gate — and runs only via CLI synth, not assertion tests |
| 25 | + |
| 26 | +Five rule packs run on every synth: AwsSolutions, Serverless, NIST 800-53 R5, HIPAA Security, PCI DSS 3.2.1. Findings fail CI. Resolve by: |
| 27 | + |
| 28 | +1. **Fix the underlying issue** (preferred). README "Design decisions and known limitations" documents recurring patterns. |
| 29 | +2. **Suppress with rationale**. Every suppression carries a `reason=` string. For `AwsSolutions-IAM5` wildcards, scope with `applies_to=["Resource::*"]` or a specific pattern and explain *why* the wildcard is unavoidable. |
| 30 | + |
| 31 | +**Critical local-vs-CI gap**: `make test-cdk` uses `Template.from_stack()`, which synthesizes the stack but does **NOT** raise on cdk-nag Aspect errors. The CI `cdk-check` job runs `cdk synth '**'` via the CLI which does. Passing local tests is necessary but not sufficient — a clean local run can still ship a cdk-nag-failing commit. Either start Docker and run `make cdk-synth` locally before pushing IAM-touching code, or expect to iterate against CI. |
| 32 | + |
| 33 | +## Encryption posture |
| 34 | + |
| 35 | +Every data-bearing resource that supports a per-resource customer-managed key uses the project's CMK: DynamoDB, Lambda env vars, all log groups, the frontend S3 bucket, AppConfig hosted configuration content, SQS DLQs, and CloudTrail trail log files (per-object SSE-KMS into an SSE-S3 bucket). Account/region-wide encryption settings (X-Ray, Glue Data Catalog) are deliberately out of scope — they'd mutate state shared with other apps in the account. |
| 36 | + |
| 37 | +Service-principal grants on CMKs must be confused-deputy-guarded with `aws:SourceAccount` + `aws:SourceArn`. See `grant_logs_service_to_key` / `grant_guardduty_service_to_key` in `hello_world/nag_utils.py` for the canonical pattern. |
| 38 | + |
| 39 | +## Dangling-resource cleanup pattern |
| 40 | + |
| 41 | +Services that create supporting resources outside CloudFormation (CloudWatch log groups, dashboards) don't get cleaned up by `cdk destroy`. Two cleanup `AwsCustomResource` patterns ship in this repo: |
| 42 | + |
| 43 | +- `AppInsightsDashboardCleanup` — deletes the auto-created Application Insights dashboard |
| 44 | +- `RumLogGroupCleanup` — deletes the auto-created `/aws/vendedlogs/RUMService_*` log group |
| 45 | + |
| 46 | +When adding services that create supporting AWS resources outside CFN, mirror the pattern: Lambda-backed `cr.AwsCustomResource` with an `on_delete` SDK call, IAM scoped to the specific resource ARN, `ignore_error_codes_matching="ResourceNotFoundException"` for the case where the resource never materialized. |
| 47 | + |
| 48 | +## Conventional Commits + git-cliff drive `CHANGELOG.md` |
| 49 | + |
| 50 | +Commit prefix grammar (see README "Commit message convention"): |
| 51 | + |
| 52 | +`feat:` / `fix:` / `docs:` / `chore:` / `ci:` / `test:` / `refactor:` / `build:` |
| 53 | + |
| 54 | +`cliff.toml` maps these to Keep-a-Changelog groups. Regenerate with `git cliff -o CHANGELOG.md`. Dependabot bumps and `Merge pull request` commits are filtered out by design. Release recipe in README "Cutting a release" — driven by `git cliff --bumped-version`, `make lock`, annotated tag, `gh release create --notes-from-tag --latest --verify-tag`. |
| 55 | + |
| 56 | +## Behaviors to avoid |
| 57 | + |
| 58 | +- **No `Co-Authored-By:` trailer on commits.** Personal preference. |
| 59 | +- **Don't introduce account/region-wide CDK constructs** without flagging them explicitly. `glue.CfnDataCatalogEncryptionSettings`, `xray.UpdateEncryptionConfig`, and similar mutate state shared with other apps in the deploying account. Forks dropping this stack into an existing AWS account would silently override neighbor teams' settings. |
| 60 | +- **Don't commit `cdk.out/`, `report.html`, `htmlcov/`, `.coverage`, or `site/`.** Reproducible from source; gitignored already. |
| 61 | + |
| 62 | +## Forking this template |
| 63 | + |
| 64 | +When a fork is spawned from this template via GitHub's "Use this template": |
| 65 | + |
| 66 | +1. **Edit this CLAUDE.md's "Project" section** to describe the fork's workload, and add a workload-specific guidance section near the bottom (see [nba-data-api/CLAUDE.md](https://github.com/timpugh/nba-data-api/blob/main/CLAUDE.md) for an example). |
| 67 | +2. **Run the post-template setup steps**: enable GitHub Pages (`gh api repos/<owner>/<repo>/pages -X POST -f build_type=workflow` — requires the repo to be public on the free plan), bootstrap CDK in the target account+region (`cdk bootstrap aws://<account>/us-east-1`), and walk through the Production readiness checklist in `TODO.md` before customer traffic. |
| 68 | +3. **Don't drift from this template silently.** If you fix something here that other forks would benefit from, push it back upstream. If you change something that diverges intentionally (different encryption posture, different observability stack), document the *why* in your fork's CLAUDE.md so future contributors don't try to reconcile. |
0 commit comments