diff --git a/CLAUDE.md b/CLAUDE.md index aaa4e5e..89473ea 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -167,12 +167,16 @@ Features: ### Infrastructure -- All services communicate via HTTPS using Caddy's internal CA -- Caddy handles routing via `*.localtest.me` subdomains -- Default endpoints: +- All services communicate via HTTPS using Caddy's internal CA (local) or Heroku's SSL (production) +- **Local development**: Caddy handles routing via `*.localtest.me` subdomains - Auth: `https://id.localtest.me` (port 3001) - API: `https://api.localtest.me` (port 3003) - APP: `https://app.localtest.me` (port 3004) +- **Production (Heroku)**: Deployed as Docker containers to Heroku apps with custom domains + - Auth: `https://auth.plaidypus.dev` + - API: `https://api.plaidypus.dev` + - App: `https://app.plaidypus.dev` + - See `docs/heroku-setup.md` for full setup instructions - Environment variables in `.env` control service configuration - TypeScript with ESM modules across all apps - Shared TypeScript configuration via `tsconfig.base.json` @@ -247,7 +251,7 @@ The authorization server uses JWKS (JSON Web Key Set) to sign JWT tokens: - Proper cryptographic key rotation - Unique key IDs for debugging (e.g., `key-abc123def456`) -**Configuration location:** `apps/auth/src/index.ts` (lines 68-89) loads JWKS from environment and logs warnings if not set +**Configuration location:** `apps/auth/src/index.ts` (lines 110-129) loads JWKS from environment and logs warnings if not set ## Sensitive Data Handling @@ -263,6 +267,7 @@ All sensitive configuration is managed through environment variables: | `COOKIE_SECRET` | Session cookie signing | High - Never commit | | `JWKS` | Token signing keys (contains private key) | Critical - Never commit | | `OIDC_CLIENTS` | Multiple client configurations | High - Never commit | +| `POST_LOGOUT_REDIRECT_URI` | Post-logout redirect URL | Low - Configurable per environment | ### Template Configuration Files @@ -340,6 +345,7 @@ This project includes automated CI/CD pipelines and containerization support for | -------- | ------- | ------- | | `ci.yml` | PRs, push to main | Lint, build, security audit | | `security.yml` | Weekly, dependency changes | CodeQL analysis, Docker image scanning | +| `deploy-heroku.yml` | Push to main, manual | Build and deploy all services to Heroku | | `deploy-*.yml` | Push to paths | Deploy individual services to VM | #### CI Workflow (`ci.yml`) @@ -388,6 +394,7 @@ docker compose up --build - Non-root user for security - Health checks for container orchestration - Production-only dependencies +- Heroku PORT compatibility (maps dynamic `PORT` to service-specific port vars) ### Docker Compose diff --git a/apps/api/package.json b/apps/api/package.json index 2b94dea..4095959 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -18,7 +18,7 @@ }, "devDependencies": { "@types/express": "^5.0.6", - "@types/node": "^25.3.0", + "@types/node": "^25.3.1", "tsx": "^4.21.0", "typescript": "^5.9.3" } diff --git a/apps/app/package.json b/apps/app/package.json index 27531ff..0169b9f 100644 --- a/apps/app/package.json +++ b/apps/app/package.json @@ -27,7 +27,7 @@ "@types/cookie-parser": "^1.4.10", "@types/ejs": "^3.1.5", "@types/express": "^5.0.6", - "@types/node": "^25.3.0", + "@types/node": "^25.3.1", "concurrently": "^9.2.1", "tailwindcss": "^4.2.1", "tsx": "^4.21.0", diff --git a/apps/auth/package.json b/apps/auth/package.json index c021a2e..ee5e62c 100644 --- a/apps/auth/package.json +++ b/apps/auth/package.json @@ -23,7 +23,7 @@ "@tailwindcss/cli": "^4.2.1", "@types/ejs": "^3.1.5", "@types/express": "^5.0.6", - "@types/node": "^25.3.0", + "@types/node": "^25.3.1", "concurrently": "^9.2.1", "tailwindcss": "^4.2.1", "tsx": "^4.21.0", diff --git a/apps/shared/package.json b/apps/shared/package.json index 7124b74..66f3c5a 100644 --- a/apps/shared/package.json +++ b/apps/shared/package.json @@ -53,7 +53,7 @@ }, "devDependencies": { "@types/express": "^5.0.6", - "@types/node": "^25.3.0", + "@types/node": "^25.3.1", "typescript": "^5.9.3" }, "peerDependencies": { diff --git a/package.json b/package.json index af9e10a..249d0a0 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "core-exchange-node-example", "private": true, "version": "1.0.1", - "packageManager": "pnpm@10.28.2+sha512.41872f037ad22f7348e3b1debbaf7e867cfd448f2726d9cf74c08f19507c31d2c8e7a11525b983febc2df640b5438dee6023ebb1f84ed43cc2d654d2bc326264", + "packageManager": "pnpm@10.30.2", "type": "module", "author": "David Neal (https://reverentgeek.com)", "contributors": [], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 078d2e7..519afac 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,8 +65,8 @@ importers: specifier: ^5.0.6 version: 5.0.6 '@types/node': - specifier: ^25.3.0 - version: 25.3.0 + specifier: ^25.3.1 + version: 25.3.1 tsx: specifier: ^4.21.0 version: 4.21.0 @@ -120,8 +120,8 @@ importers: specifier: ^5.0.6 version: 5.0.6 '@types/node': - specifier: ^25.3.0 - version: 25.3.0 + specifier: ^25.3.1 + version: 25.3.1 concurrently: specifier: ^9.2.1 version: 9.2.1 @@ -169,8 +169,8 @@ importers: specifier: ^5.0.6 version: 5.0.6 '@types/node': - specifier: ^25.3.0 - version: 25.3.0 + specifier: ^25.3.1 + version: 25.3.1 concurrently: specifier: ^9.2.1 version: 9.2.1 @@ -206,8 +206,8 @@ importers: specifier: ^5.0.6 version: 5.0.6 '@types/node': - specifier: ^25.3.0 - version: 25.3.0 + specifier: ^25.3.1 + version: 25.3.1 typescript: specifier: ^5.9.3 version: 5.9.3 @@ -673,8 +673,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@25.3.0': - resolution: {integrity: sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==} + '@types/node@25.3.1': + resolution: {integrity: sha512-hj9YIJimBCipHVfHKRMnvmHg+wfhKc0o4mTtXh9pKBjC8TLJzz0nzGmLi5UJsYAUgSvXFHgb0V2oY10DUFtImw==} '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -783,6 +783,9 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} @@ -791,6 +794,9 @@ packages: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + brace-expansion@5.0.3: resolution: {integrity: sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==} engines: {node: 18 || 20 || >=22} @@ -1046,9 +1052,8 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} - filelist@1.0.5: - resolution: {integrity: sha512-ct/ckWBV/9Dg3MlvCXsLcSUyoWwv9mCKqlhLNB2DAuXR/NZolSXlQqP5dyy6guWlPXBhodZyZ5lGPQcbQDxrEQ==} - engines: {node: 20 || >=22} + filelist@1.0.6: + resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} finalhandler@2.1.1: resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} @@ -1354,6 +1359,10 @@ packages: resolution: {integrity: sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==} engines: {node: 18 || 20 || >=22} + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -2014,11 +2023,11 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 25.3.0 + '@types/node': 25.3.1 '@types/connect@3.4.38': dependencies: - '@types/node': 25.3.0 + '@types/node': 25.3.1 '@types/cookie-parser@1.4.10(@types/express@5.0.6)': dependencies: @@ -2032,7 +2041,7 @@ snapshots: '@types/express-serve-static-core@5.1.1': dependencies: - '@types/node': 25.3.0 + '@types/node': 25.3.1 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -2047,7 +2056,7 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@25.3.0': + '@types/node@25.3.1': dependencies: undici-types: 7.18.2 @@ -2057,12 +2066,12 @@ snapshots: '@types/send@1.2.1': dependencies: - '@types/node': 25.3.0 + '@types/node': 25.3.1 '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 25.3.0 + '@types/node': 25.3.1 '@typescript-eslint/eslint-plugin@8.56.1(@typescript-eslint/parser@8.56.1(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.2(jiti@2.6.1))(typescript@5.9.3)': dependencies: @@ -2188,6 +2197,8 @@ snapshots: atomic-sleep@1.0.0: {} + balanced-match@1.0.2: {} + balanced-match@4.0.4: {} body-parser@2.2.2: @@ -2204,6 +2215,10 @@ snapshots: transitivePeerDependencies: - supports-color + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + brace-expansion@5.0.3: dependencies: balanced-match: 4.0.4 @@ -2493,9 +2508,9 @@ snapshots: dependencies: flat-cache: 4.0.1 - filelist@1.0.5: + filelist@1.0.6: dependencies: - minimatch: 10.2.4 + minimatch: 5.1.9 finalhandler@2.1.1: dependencies: @@ -2629,7 +2644,7 @@ snapshots: jake@10.9.4: dependencies: async: 3.2.6 - filelist: 1.0.5 + filelist: 1.0.6 picocolors: 1.1.1 jiti@2.6.1: {} @@ -2761,6 +2776,10 @@ snapshots: dependencies: brace-expansion: 5.0.3 + minimatch@5.1.9: + dependencies: + brace-expansion: 2.0.2 + minimist@1.2.8: {} mri@1.2.0: {}