cdk-local 0.58.0 → 0.59.0

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/README.md CHANGED
@@ -61,7 +61,7 @@ cdkl list # every runnable target, grouped
61
61
 
62
62
  - **`start-api`** serves one HTTP server per API; a bare `start-api` in a multi-stack app needs `--all-stacks` or `--stack <name>`. Add **`--watch`** to re-synth and hot-reload on CDK source changes ([details](docs/local-emulation.md#hot-reload---watch)).
63
63
  - **`run-task`** / single-replica **`start-service`** publish declared container ports on the host and log `Reach it at 127.0.0.1:<port>` (`--host-port <container>=<host>` remaps; handy for privileged ports on macOS).
64
- - **`start-alb`** stands up the ECS service(s) behind an ALB plus a host-side front-door on each listener port, honoring all six listener-rule conditions, weighted forwards, redirect / fixed-response actions, and mixed ECS + Lambda targets ([details](docs/cli-reference.md#cdkl-start-alb-run-an-alb-fronted-service-locally)).
64
+ - **`start-alb`** stands up the ECS service(s) behind an ALB plus a host-side front-door on each listener port, honoring all six listener-rule conditions, weighted forwards, redirect / fixed-response actions, mixed ECS + Lambda targets, and `authenticate-cognito` / `authenticate-oidc` actions (local Bearer-JWT enforcement) ([details](docs/cli-reference.md#cdkl-start-alb-run-an-alb-fronted-service-locally)).
65
65
  - **`invoke-agentcore`** invokes a Bedrock AgentCore Runtime agent locally — container or `fromCodeAsset` / `fromS3` managed runtime, HTTP / SSE / WebSocket / MCP protocols, with `customJwtAuthorizer` and `--sigv4` enforcement ([details](docs/cli-reference.md#cdkl-invoke-agentcore-run-bedrock-agentcore-runtime-agents-locally)).
66
66
  - Non-TTY (CI / pipes): every command except a bare `start-api` needs an explicit target.
67
67
 
@@ -80,7 +80,7 @@ Full flags, precedence, and `--from-cfn-stack` resolution: [docs/cli-reference.m
80
80
  | ECS task definitions | `run-task` |
81
81
  | ECS services | `start-service` |
82
82
  | Cloud Map / Service Connect registry | service discovery between local replicas |
83
- | ALB-fronted ECS / Lambda services | `start-alb` — HTTP / HTTPS listeners, all six listener-rule conditions, weighted forwards, redirect / fixed-response, mixed ECS + Lambda targets |
83
+ | ALB-fronted ECS / Lambda services | `start-alb` — HTTP / HTTPS listeners, all six listener-rule conditions, weighted forwards, redirect / fixed-response, mixed ECS + Lambda targets, authenticate-cognito / authenticate-oidc (local Bearer-JWT enforcement) |
84
84
  | Bedrock AgentCore Runtime agents | `invoke-agentcore` — container + `fromCodeAsset` / `fromS3` artifacts, HTTP + MCP |
85
85
 
86
86
  Lambda runs on every current AWS Lambda runtime — Node.js (18/20/22/24), Python (3.11–3.14), Ruby (3.2/3.3), Java (8.al2/11/17/21), .NET (6/8), and the OS-only `provided.al2` / `provided.al2023`. The retired `go1.x` runtime is rejected with a pointer to migrate to `provided.al2023`.
package/dist/cli.js CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as createLocalStartApiCommand } from "./cloud-map-resolver-DjKCYyIq.js";
3
- import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-B-5gO8ht.js";
3
+ import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-ChGfKSkV.js";
4
4
  import { Command } from "commander";
5
5
 
6
6
  //#region src/cli/index.ts
7
7
  const program = new Command();
8
- program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.58.0");
8
+ program.name("cdkl").description("Run AWS CDK stacks locally with Docker.").version("0.59.0");
9
9
  program.addCommand(createLocalInvokeCommand());
10
10
  program.addCommand(createLocalInvokeAgentCoreCommand());
11
11
  program.addCommand(createLocalStartApiCommand());
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/cli/commands/local-invoke.ts","../src/cli/commands/local-invoke-agentcore.ts","../src/cli/commands/local-run-task.ts","../src/local/target-lister.ts","../src/cli/commands/local-start-service.ts","../src/cli/commands/local-start-alb.ts","../src/cli/commands/local-list.ts","../src/local/cfn-local-state-provider.ts"],"mappings":";;;;;UA6IiB,+BAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAi6BA,wBAAA,CAAyB,IAAA,GAAM,+BAAA,GAAuC,OAAA;;;UCz2BrE,wCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBA6wCA,iCAAA,CACd,IAAA,GAAM,wCAAA,GACL,OAAA;;;UC/2Cc,gCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmhBA,yBAAA,CAA0B,IAAA,GAAM,gCAAA,GAAwC,OAAA;;;UCjnBvE,WAAA;EAEf,SAAA;EAEA,SAAA;EAKA,WAAA;EAMA,WAAA;EAOA,IAAA;AAAA;AAAA,UAOe,aAAA;EAEf,OAAA,EAAS,WAAA;EAMT,IAAA,EAAM,WAAA;EAEN,WAAA,EAAa,WAAA;EAEb,kBAAA,EAAoB,WAAA;EAEpB,iBAAA,EAAmB,WAAA;EAEnB,aAAA,EAAe,WAAA;AAAA;AAAA,iBAkID,WAAA,CAAY,MAAA,WAAiB,SAAA,KAAc,aAAA;AAAA,iBAsC3C,YAAA,CAAa,OAAA,EAAS,aAAA;;;UC3MrB,qCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmCA,8BAAA,CACd,IAAA,GAAM,qCAAA,GACL,OAAA;;;UChCc,iCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBA2QA,0BAAA,CAA2B,IAAA,GAAM,iCAAA,GAAyC,OAAA;;;UCvQzE,6BAAA;EAEf,WAAA,GAAc,mBAAA;AAAA;AAAA,UAuCC,0BAAA;EAOf,IAAA;AAAA;AAAA,iBAYc,mBAAA,CACd,OAAA,EAAS,aAAA,EACT,OAAA,UACA,OAAA,GAAS,0BAAA;AAAA,iBAuEK,sBAAA,CAAuB,IAAA,GAAM,6BAAA,GAAqC,OAAA;;;UC/FjE,4BAAA;EAOf,YAAA;EAKA,MAAA;EAiBA,OAAA;AAAA;AAAA,cAGW,qBAAA,YAAiC,kBAAA;EAAA,SAG5B,KAAA;EAAA,iBACC,YAAA;EAAA,iBACA,MAAA;EAAA,QAKT,MAAA;EAAA,QAKA,YAAA;EAAA,QAKA,SAAA;EAAA,iBACS,aAAA;EAAA,QAQT,QAAA;cAEI,IAAA,EAAM,4BAAA;EAAA,QAOV,SAAA;EAAA,QAmBA,eAAA;EAAA,QAaA,YAAA;EA6BK,4BAAA,CACX,QAAA,EAAU,sBAAA,GACT,OAAA,CAAQ,qBAAA;EAsBE,0BAAA,CACX,kBAAA,WACC,OAAA,CAAQ,MAAA;EAmCE,IAAA,CACX,UAAA,UACA,YAAA,uBACC,OAAA,CAAQ,gBAAA;EA2DE,uBAAA,CACX,eAAA,WACC,OAAA,CAAQ,kBAAA;EA8DJ,OAAA,CAAA;AAAA"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/cli/commands/local-invoke.ts","../src/cli/commands/local-invoke-agentcore.ts","../src/cli/commands/local-run-task.ts","../src/local/target-lister.ts","../src/cli/commands/local-start-service.ts","../src/cli/commands/local-start-alb.ts","../src/cli/commands/local-list.ts","../src/local/cfn-local-state-provider.ts"],"mappings":";;;;;UA6IiB,+BAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAi6BA,wBAAA,CAAyB,IAAA,GAAM,+BAAA,GAAuC,OAAA;;;UCz2BrE,wCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBA6wCA,iCAAA,CACd,IAAA,GAAM,wCAAA,GACL,OAAA;;;UC/2Cc,gCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmhBA,yBAAA,CAA0B,IAAA,GAAM,gCAAA,GAAwC,OAAA;;;UCjnBvE,WAAA;EAEf,SAAA;EAEA,SAAA;EAKA,WAAA;EAMA,WAAA;EAOA,IAAA;AAAA;AAAA,UAOe,aAAA;EAEf,OAAA,EAAS,WAAA;EAMT,IAAA,EAAM,WAAA;EAEN,WAAA,EAAa,WAAA;EAEb,kBAAA,EAAoB,WAAA;EAEpB,iBAAA,EAAmB,WAAA;EAEnB,aAAA,EAAe,WAAA;AAAA;AAAA,iBAkID,WAAA,CAAY,MAAA,WAAiB,SAAA,KAAc,aAAA;AAAA,iBAsC3C,YAAA,CAAa,OAAA,EAAS,aAAA;;;UC3MrB,qCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBAmCA,8BAAA,CACd,IAAA,GAAM,qCAAA,GACL,OAAA;;;UChCc,iCAAA;EACf,mBAAA,GAAsB,mBAAA;EAEtB,WAAA,GAAc,mBAAA;AAAA;AAAA,iBA6QA,0BAAA,CAA2B,IAAA,GAAM,iCAAA,GAAyC,OAAA;;;UCzQzE,6BAAA;EAEf,WAAA,GAAc,mBAAA;AAAA;AAAA,UAuCC,0BAAA;EAOf,IAAA;AAAA;AAAA,iBAYc,mBAAA,CACd,OAAA,EAAS,aAAA,EACT,OAAA,UACA,OAAA,GAAS,0BAAA;AAAA,iBAuEK,sBAAA,CAAuB,IAAA,GAAM,6BAAA,GAAqC,OAAA;;;UC/FjE,4BAAA;EAOf,YAAA;EAKA,MAAA;EAiBA,OAAA;AAAA;AAAA,cAGW,qBAAA,YAAiC,kBAAA;EAAA,SAG5B,KAAA;EAAA,iBACC,YAAA;EAAA,iBACA,MAAA;EAAA,QAKT,MAAA;EAAA,QAKA,YAAA;EAAA,QAKA,SAAA;EAAA,iBACS,aAAA;EAAA,QAQT,QAAA;cAEI,IAAA,EAAM,4BAAA;EAAA,QAOV,SAAA;EAAA,QAmBA,eAAA;EAAA,QAaA,YAAA;EA6BK,4BAAA,CACX,QAAA,EAAU,sBAAA,GACT,OAAA,CAAQ,qBAAA;EAsBE,0BAAA,CACX,kBAAA,WACC,OAAA,CAAQ,MAAA;EAmCE,IAAA,CACX,UAAA,UACA,YAAA,uBACC,OAAA,CAAQ,gBAAA;EA2DE,uBAAA,CACX,eAAA,WACC,OAAA,CAAQ,kBAAA;EA8DJ,OAAA,CAAA;AAAA"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { c as getEmbedConfig, l as resetEmbedConfig, u as setEmbedConfig } from "./docker-cmd-voNPrcRh.js";
2
2
  import { En as listTargets, Tn as countTargets, _n as CfnLocalStateProvider, a as createLocalStartApiCommand, an as substituteAgainstStateAsync, dn as createLocalStateProvider, fn as isCfnFlagPresent, gn as resolveCfnStackName, hn as resolveCfnRegion, in as substituteAgainstState, mn as resolveCfnFallbackRegion, on as substituteEnvVarsFromState, pn as rejectExplicitCfnStackWithMultipleStacks, sn as substituteEnvVarsFromStateAsync, un as LocalStateSourceError, vn as collectSsmParameterRefs, yn as resolveSsmParameters } from "./cloud-map-resolver-DjKCYyIq.js";
3
- import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, n as formatTargetListing, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-B-5gO8ht.js";
3
+ import { a as createLocalRunTaskCommand, i as createLocalStartServiceCommand, n as formatTargetListing, o as createLocalInvokeAgentCoreCommand, r as createLocalStartAlbCommand, s as createLocalInvokeCommand, t as createLocalListCommand } from "./local-list-ChGfKSkV.js";
4
4
 
5
5
  export { CfnLocalStateProvider, LocalStateSourceError, collectSsmParameterRefs, countTargets, createLocalInvokeAgentCoreCommand, createLocalInvokeCommand, createLocalListCommand, createLocalRunTaskCommand, createLocalStartAlbCommand, createLocalStartApiCommand, createLocalStartServiceCommand, createLocalStateProvider, formatTargetListing, getEmbedConfig, isCfnFlagPresent, listTargets, rejectExplicitCfnStackWithMultipleStacks, resetEmbedConfig, resolveCfnFallbackRegion, resolveCfnRegion, resolveCfnStackName, resolveSsmParameters, setEmbedConfig, substituteAgainstState, substituteAgainstStateAsync, substituteEnvVarsFromState, substituteEnvVarsFromStateAsync };
@@ -1,5 +1,5 @@
1
1
  import { a as runDockerStreaming, c as getEmbedConfig, r as getDockerCmd, s as getLogger, u as setEmbedConfig } from "./docker-cmd-voNPrcRh.js";
2
- import { $n as applyRoleArnIfSet, $t as derivePartitionAndUrlSuffix, At as waitForRieReady, Bn as resolveAgentCoreTarget, Bt as execEnvForSecrets, Cn as resolveMultiTarget, Dt as AssetManifestLoader, En as listTargets, Et as singleFlight, Ft as buildDockerImage, Gn as matchStacks, Gt as streamLogs, Hn as derivePseudoParametersFromRegion, Ht as pullImage, In as AGENTCORE_MCP_PROTOCOL, It as DockerRunnerError, Jn as resolveCdkPathToLogicalIds, Jt as resolveRuntimeImage, Kn as buildCdkPathIndex, Kt as resolveRuntimeCodeMountPath, Lt as SENSITIVE_ENV_KEYS, Mt as buildContainerImage, Nn as AGENTCORE_A2A_PROTOCOL, Nt as parseEcrUri, Ot as getDockerImageBySourceHash, Pn as AGENTCORE_AGUI_PROTOCOL, Pt as pullEcrImage, Q as verifyJwtViaDiscovery, Qn as withErrorHandling, Qt as checkVolumeHostPath, Rt as appendEnvFlags, Sn as Synthesizer, Tn as countTargets, Tt as writeProfileCredentialsFile, Ut as removeContainer, Vn as resolveLambdaTarget, Vt as pickFreePort, Wt as runDetached, Xn as LocalInvokeBuildError, Xt as TASK_ROLE_ACCOUNT_PLACEHOLDER, Y as createJwksCache, Yn as CdkLocalError, Yt as EcsTaskResolutionError, Zn as LocalStartServiceError, Zt as applyCrossStackResolverToTask, _t as invokeAgentCore, an as substituteAgainstStateAsync, ar as warnIfDeprecatedRegion, at as invokeAgentCoreWs, bn as resolveApp, c as resolveProfileCredentials$1, cn as resolveEnvVars, ct as a2aInvokeOnce, dn as createLocalStateProvider, en as detectEcsImageResolutionNeeds, er as appOptions, ft as mcpInvokeOnce, ht as signAgentCoreInvocation, i as getPublishedHostPort, ir as parseContextOptions, jt as architectureToPlatform, kt as invokeRie, ln as materializeLayerFromArn, lt as MCP_CONTAINER_PORT, mn as resolveCfnFallbackRegion, n as CloudMapRegistry, nn as resolveEcsTaskTarget, nr as contextOptions, ot as A2A_CONTAINER_PORT, pn as rejectExplicitCfnStackWithMultipleStacks, qn as readCdkPathOrUndefined, qt as resolveRuntimeFileExtension, r as getContainerNetworkIp, rn as applyDeployedEnvFallback, rr as deprecatedRegionOption, sn as substituteEnvVarsFromStateAsync, st as A2A_PATH, t as buildCloudMapIndex, tn as parseEcsTarget, tr as commonOptions, ut as MCP_PATH, vt as waitForAgentCorePing, wn as resolveSingleTarget, xt as buildAgentCoreCodeImage, yt as downloadAndExtractS3Bundle, zn as pickAgentCoreCandidateStack, zt as ensureDockerAvailable } from "./cloud-map-resolver-DjKCYyIq.js";
2
+ import { $n as applyRoleArnIfSet, $t as derivePartitionAndUrlSuffix, At as waitForRieReady, Bn as resolveAgentCoreTarget, Bt as execEnvForSecrets, Cn as resolveMultiTarget, Dt as AssetManifestLoader, En as listTargets, Et as singleFlight, Ft as buildDockerImage, Gn as matchStacks, Gt as streamLogs, Hn as derivePseudoParametersFromRegion, Ht as pullImage, In as AGENTCORE_MCP_PROTOCOL, It as DockerRunnerError, Jn as resolveCdkPathToLogicalIds, Jt as resolveRuntimeImage, Kn as buildCdkPathIndex, Kt as resolveRuntimeCodeMountPath, Lt as SENSITIVE_ENV_KEYS, Mt as buildContainerImage, Nn as AGENTCORE_A2A_PROTOCOL, Nt as parseEcrUri, Ot as getDockerImageBySourceHash, Pn as AGENTCORE_AGUI_PROTOCOL, Pt as pullEcrImage, Q as verifyJwtViaDiscovery, Qn as withErrorHandling, Qt as checkVolumeHostPath, Rt as appendEnvFlags, Sn as Synthesizer, Tn as countTargets, Tt as writeProfileCredentialsFile, Ut as removeContainer, Vn as resolveLambdaTarget, Vt as pickFreePort, Wt as runDetached, Xn as LocalInvokeBuildError, Xt as TASK_ROLE_ACCOUNT_PLACEHOLDER, Y as createJwksCache, Yn as CdkLocalError, Yt as EcsTaskResolutionError, Z as verifyJwtAuthorizer, Zn as LocalStartServiceError, Zt as applyCrossStackResolverToTask, _t as invokeAgentCore, an as substituteAgainstStateAsync, ar as warnIfDeprecatedRegion, at as invokeAgentCoreWs, bn as resolveApp, c as resolveProfileCredentials$1, cn as resolveEnvVars, ct as a2aInvokeOnce, dn as createLocalStateProvider, en as detectEcsImageResolutionNeeds, er as appOptions, ft as mcpInvokeOnce, ht as signAgentCoreInvocation, i as getPublishedHostPort, ir as parseContextOptions, jt as architectureToPlatform, kt as invokeRie, ln as materializeLayerFromArn, lt as MCP_CONTAINER_PORT, mn as resolveCfnFallbackRegion, n as CloudMapRegistry, nn as resolveEcsTaskTarget, nr as contextOptions, ot as A2A_CONTAINER_PORT, pn as rejectExplicitCfnStackWithMultipleStacks, qn as readCdkPathOrUndefined, qt as resolveRuntimeFileExtension, r as getContainerNetworkIp, rn as applyDeployedEnvFallback, rr as deprecatedRegionOption, sn as substituteEnvVarsFromStateAsync, st as A2A_PATH, t as buildCloudMapIndex, tn as parseEcsTarget, tr as commonOptions, ut as MCP_PATH, vt as waitForAgentCorePing, wn as resolveSingleTarget, xt as buildAgentCoreCodeImage, yt as downloadAndExtractS3Bundle, zn as pickAgentCoreCandidateStack, zt as ensureDockerAvailable } from "./cloud-map-resolver-DjKCYyIq.js";
3
3
  import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { homedir, tmpdir } from "node:os";
5
5
  import * as path from "node:path";
@@ -3843,25 +3843,67 @@ function handleProxyRequest(req, res, opts) {
3843
3843
  ...req.socket.remoteAddress !== void 0 && { sourceIp: req.socket.remoteAddress }
3844
3844
  });
3845
3845
  if (!action) return reply404(req, res, opts);
3846
- if (action.kind === "redirect" || action.kind === "fixed-response") {
3847
- req.resume();
3848
- if (action.kind === "redirect") writeRedirect(res, action, req, opts.listenerPort, opts.tls ? "https" : "http");
3849
- else writeFixedResponse(res, action);
3850
- return Promise.resolve();
3851
- }
3852
- const picked = pickWeightedTarget(action.pools);
3853
- if (!picked) {
3854
- writeError(res, 502, `No forward target selected behind ${opts.label} (every weighted target has weight 0).`);
3855
- return Promise.resolve();
3846
+ if (action.auth) {
3847
+ const auth = action.auth;
3848
+ return auth.check(req.headers).then((result) => {
3849
+ if (!result.allow) {
3850
+ req.resume();
3851
+ writeUnauthorized(res, auth.realm, result.reason);
3852
+ return;
3853
+ }
3854
+ return serveAction(req, res, action, opts);
3855
+ }).catch((err) => {
3856
+ getLogger().child("front-door").debug(`auth gate error: ${err instanceof Error ? err.message : String(err)}`);
3857
+ req.resume();
3858
+ writeUnauthorized(res, auth.realm, "auth check failed");
3859
+ });
3856
3860
  }
3857
- if ("lambda" in picked) return handleLambdaRequest(req, res, picked.lambda, opts);
3858
- return handlePoolRequest(req, res, picked.pool, opts);
3861
+ return serveAction(req, res, action, opts);
3859
3862
  }
3860
3863
  const target = resolveDispatchTarget(opts, url);
3861
3864
  if (!target) return reply404(req, res, opts);
3862
3865
  if (target.kind === "lambda") return handleLambdaRequest(req, res, target.lambda, opts);
3863
3866
  return handlePoolRequest(req, res, target.pool, opts);
3864
3867
  }
3868
+ /**
3869
+ * Dispatch a resolved {@link RouteAction} — the path the route resolver
3870
+ * returned (after any auth gate). Splits forward (ECS pool or Lambda invoker)
3871
+ * from redirect / fixed-response, mirroring what the inline logic used to do.
3872
+ */
3873
+ function serveAction(req, res, action, opts) {
3874
+ if (action.kind === "redirect" || action.kind === "fixed-response") {
3875
+ req.resume();
3876
+ if (action.kind === "redirect") writeRedirect(res, action, req, opts.listenerPort, opts.tls ? "https" : "http");
3877
+ else writeFixedResponse(res, action);
3878
+ return Promise.resolve();
3879
+ }
3880
+ const picked = pickWeightedTarget(action.pools);
3881
+ if (!picked) {
3882
+ writeError(res, 502, `No forward target selected behind ${opts.label} (every weighted target has weight 0).`);
3883
+ return Promise.resolve();
3884
+ }
3885
+ if ("lambda" in picked) return handleLambdaRequest(req, res, picked.lambda, opts);
3886
+ return handlePoolRequest(req, res, picked.pool, opts);
3887
+ }
3888
+ /**
3889
+ * Write a 401 with `WWW-Authenticate: Bearer realm="..."`. ALB itself would
3890
+ * redirect to the IdP's authorize endpoint; for local dev parity we deny
3891
+ * loudly so the user notices and either supplies `--bearer-token` / passes a
3892
+ * fresh Authorization header / disables the guard with `--no-verify-auth`.
3893
+ */
3894
+ function writeUnauthorized(res, realm, reason) {
3895
+ const body = reason && reason !== "" ? reason : "Unauthorized";
3896
+ res.writeHead(401, {
3897
+ "content-type": "text/plain; charset=utf-8",
3898
+ "content-length": String(Buffer.byteLength(`${body}\n`)),
3899
+ "www-authenticate": `Bearer realm="${escapeRealmQuotes(realm)}"`
3900
+ });
3901
+ res.end(`${body}\n`);
3902
+ }
3903
+ /** Escape `"` in the realm so the `WWW-Authenticate` header parses cleanly. */
3904
+ function escapeRealmQuotes(realm) {
3905
+ return realm.replace(/"/g, "\\\"");
3906
+ }
3865
3907
  /** Reply 404 — an ALB listener with no matching rule and no default action. */
3866
3908
  function reply404(req, res, opts) {
3867
3909
  writeError(res, 404, `No listener rule matched '${req.url ?? "/"}' on ${opts.label}, and the listener has no default action forwarding to a local target.`);
@@ -4730,6 +4772,98 @@ function createFrontDoorLambdaRunner(lambda, opts) {
4730
4772
  };
4731
4773
  }
4732
4774
 
4775
+ //#endregion
4776
+ //#region src/local/front-door-auth.ts
4777
+ /**
4778
+ * Build the front-door's auth-check callback for an
4779
+ * `authenticate-cognito` / `authenticate-oidc` guard.
4780
+ *
4781
+ * Local-dev parity model: the cloud-side ALB authenticates the user via a
4782
+ * full OAuth roundtrip (redirect to the IdP's authorize endpoint, callback,
4783
+ * AWSELBAuthSessionCookie issuance). The local front-door does NOT reproduce
4784
+ * the roundtrip — it accepts EITHER a Bearer JWT (verified against the
4785
+ * Cognito JWKS / OIDC discovery URL just like API Gateway's JWT authorizer)
4786
+ * OR an `AWSELBAuthSessionCookie-*` cookie pass-through (the user is acting
4787
+ * as if already signed in via the deployed ALB — `--bearer-token` makes the
4788
+ * Bearer-JWT path the headline path; the cookie pass-through is convenience
4789
+ * for hitting the local front-door from a browser session that already
4790
+ * authenticated through the deployed ALB).
4791
+ *
4792
+ * `--no-verify-auth` (the `noVerifyAuth` flag) short-circuits everything to
4793
+ * `allow: true` — explicitly off-switching the guard for local dev where
4794
+ * you do not want to mint a Bearer token at all.
4795
+ *
4796
+ * `--bearer-token <jwt>` (the `bearerToken` arg) makes the supplied token
4797
+ * the default `Authorization` value when the inbound request has none.
4798
+ */
4799
+ function buildAuthCheck(guard, jwksCache, opts = {}) {
4800
+ const realm = guard.label;
4801
+ if (opts.noVerifyAuth === true) return {
4802
+ realm,
4803
+ check: async () => ({ allow: true })
4804
+ };
4805
+ const warned = opts.warned ?? /* @__PURE__ */ new Set();
4806
+ const sessionCookiePrefix = guard.sessionCookieName;
4807
+ const injectedBearer = opts.bearerToken;
4808
+ return {
4809
+ realm,
4810
+ check: async (headers) => {
4811
+ const cookieHeader = headerValue(headers["cookie"]);
4812
+ if (cookieHeader && cookieHasSessionPrefix(cookieHeader, sessionCookiePrefix)) return { allow: true };
4813
+ let authorization = headerValue(headers["authorization"]);
4814
+ if ((!authorization || authorization === "") && injectedBearer !== void 0) authorization = `Bearer ${injectedBearer}`;
4815
+ if (!authorization || authorization === "") return {
4816
+ allow: false,
4817
+ reason: "No Bearer token presented. Supply Authorization: Bearer <jwt> or pass --bearer-token <jwt>."
4818
+ };
4819
+ if (!authorization.toLowerCase().startsWith("bearer ")) return {
4820
+ allow: false,
4821
+ reason: "Authorization scheme is not Bearer; the ALB authenticate-* guard only accepts Bearer JWTs."
4822
+ };
4823
+ try {
4824
+ if ((await verifyJwtAuthorizer({
4825
+ kind: "jwt",
4826
+ logicalId: guard.label,
4827
+ declaredAt: "start-alb authenticate-* action",
4828
+ issuer: guard.issuer,
4829
+ audience: [guard.audience],
4830
+ ...guard.region !== void 0 && { region: guard.region },
4831
+ ...guard.userPoolId !== void 0 && { userPoolId: guard.userPoolId }
4832
+ }, authorization, jwksCache, { warned })).allow) return { allow: true };
4833
+ return {
4834
+ allow: false,
4835
+ reason: "Bearer token rejected (signature / iss / aud / exp check failed)."
4836
+ };
4837
+ } catch (err) {
4838
+ getLogger().child("front-door-auth").debug(`auth check threw: ${err instanceof Error ? err.message : String(err)}`);
4839
+ return {
4840
+ allow: false,
4841
+ reason: "Auth check failed."
4842
+ };
4843
+ }
4844
+ }
4845
+ };
4846
+ }
4847
+ function headerValue(raw) {
4848
+ if (raw === void 0) return void 0;
4849
+ if (Array.isArray(raw)) return raw[0];
4850
+ return raw;
4851
+ }
4852
+ /**
4853
+ * True iff the `Cookie` header contains a cookie whose name starts with
4854
+ * `<sessionCookiePrefix>` (ALB suffixes the cookie with `-0` / `-1` / ...
4855
+ * when the auth session payload exceeds the per-cookie size limit, so we
4856
+ * match the prefix, not an exact name).
4857
+ */
4858
+ function cookieHasSessionPrefix(cookieHeader, sessionCookiePrefix) {
4859
+ for (const pair of cookieHeader.split(";")) {
4860
+ const eq = pair.indexOf("=");
4861
+ const name = (eq === -1 ? pair : pair.slice(0, eq)).trim();
4862
+ if (name === sessionCookiePrefix || name.startsWith(`${sessionCookiePrefix}-`)) return true;
4863
+ }
4864
+ return false;
4865
+ }
4866
+
4733
4867
  //#endregion
4734
4868
  //#region src/cli/commands/ecs-service-emulator.ts
4735
4869
  /**
@@ -5038,9 +5172,20 @@ async function buildFrontDoor(plan, options, logger) {
5038
5172
  certPath: options.tlsCert,
5039
5173
  keyPath: options.tlsKey
5040
5174
  }) : void 0;
5175
+ const jwksCache = createJwksCache();
5176
+ const sharedWarned = /* @__PURE__ */ new Set();
5177
+ const authForGuard = (guard) => buildAuthCheck(guard, jwksCache, {
5178
+ ...options.verifyAuth === false && { noVerifyAuth: true },
5179
+ ...options.bearerToken !== void 0 && { bearerToken: options.bearerToken },
5180
+ warned: sharedWarned
5181
+ });
5182
+ const attachAuth = (action, guard) => guard ? {
5183
+ ...action,
5184
+ auth: authForGuard(guard)
5185
+ } : action;
5041
5186
  try {
5042
5187
  for (const listener of plan.listeners) {
5043
- const defaultRoute = listener.defaultAction ? toRouteAction(listener.defaultAction) : void 0;
5188
+ const defaultRoute = listener.defaultAction ? attachAuth(toRouteAction(listener.defaultAction), listener.defaultAuthGuard) : void 0;
5044
5189
  const ruleRoutes = listener.rules.map((r) => ({
5045
5190
  priority: r.priority,
5046
5191
  pathPatterns: r.pathPatterns,
@@ -5049,7 +5194,7 @@ async function buildFrontDoor(plan, options, logger) {
5049
5194
  httpRequestMethods: r.httpRequestMethods,
5050
5195
  queryStringConditions: r.queryStringConditions,
5051
5196
  sourceIpCidrs: r.sourceIpCidrs,
5052
- target: toRouteAction(r.action)
5197
+ target: attachAuth(toRouteAction(r.action), r.authGuard)
5053
5198
  }));
5054
5199
  const route = (req) => matchAlbPathRule(req, ruleRoutes) ?? defaultRoute;
5055
5200
  const tls = listener.protocol === "HTTPS" ? tlsMaterials : void 0;
@@ -5367,9 +5512,19 @@ function createLocalStartServiceCommand(opts = {}) {
5367
5512
  * mix ECS and Lambda targets. HTTPS listeners are served — local TLS
5368
5513
  * termination uses a user-supplied or auto-generated self-signed cert/key
5369
5514
  * pair (the deployed `Listener.Certificates[]` ACM ARNs are not fetched,
5370
- * because ACM private keys are not retrievable by design). Skipped with a
5371
- * warning: TLS listeners (NLB-style, not ALB) and `authenticate-cognito` /
5372
- * `authenticate-oidc` actions.
5515
+ * because ACM private keys are not retrievable by design).
5516
+ *
5517
+ * `authenticate-cognito` / `authenticate-oidc` actions ARE served — they are
5518
+ * lifted from the `Actions[]` array into an `authGuard` attached to the
5519
+ * terminal action they wrap. The front-door enforces a Bearer-JWT check
5520
+ * locally (signature + `iss` + `exp` + `aud`) using the existing JWKS-cached
5521
+ * verifier; missing / invalid token -> 401 with `WWW-Authenticate: Bearer`.
5522
+ * Out of scope: the full OAuth roundtrip (authorize-endpoint redirect +
5523
+ * callback). The local-dev parity is "I have a JWT, let me through" — the
5524
+ * `--bearer-token <jwt>` flag is the convenience escape hatch; the
5525
+ * `--no-verify-auth` flag disables the guard entirely.
5526
+ *
5527
+ * Skipped with a warning: TLS listeners (NLB-style, not ALB).
5373
5528
  */
5374
5529
  const ALB_TYPE = "AWS::ElasticLoadBalancingV2::LoadBalancer";
5375
5530
  const LISTENER_TYPE = "AWS::ElasticLoadBalancingV2::Listener";
@@ -5401,7 +5556,7 @@ function resolveAlbFrontDoor(stack, albLogicalId) {
5401
5556
  continue;
5402
5557
  }
5403
5558
  if (protocol === "HTTPS" && Array.isArray(props["Certificates"]) && props["Certificates"].length > 0) warnings.push(`Listener '${listenerLogicalId}' on port ${port} declares ACM Certificates which are not retrievable locally. The front-door terminates TLS with --tls-cert/--tls-key or an auto-generated self-signed cert.`);
5404
- const defaultAction = resolveAction(props["DefaultActions"], resources, tgToService, stackName, `Listener '${listenerLogicalId}' (port ${port}) default action`, warnings);
5559
+ const defaultResolved = resolveAction(props["DefaultActions"], resources, tgToService, stackName, `Listener '${listenerLogicalId}' (port ${port}) default action`, warnings);
5405
5560
  const rules = [];
5406
5561
  for (const { ruleLogicalId, ruleProps } of rulesByListener.get(listenerLogicalId) ?? []) {
5407
5562
  const priority = parsePriority(ruleProps["Priority"]);
@@ -5410,8 +5565,8 @@ function resolveAlbFrontDoor(stack, albLogicalId) {
5410
5565
  if (!parsed) continue;
5411
5566
  const { pathPatterns, hostPatterns, httpHeaderConditions, httpRequestMethods, queryStringConditions, sourceIpCidrs } = parsed;
5412
5567
  if (!(pathPatterns.length > 0 || hostPatterns.length > 0 || httpHeaderConditions.length > 0 || httpRequestMethods.length > 0 || queryStringConditions.length > 0 || sourceIpCidrs.length > 0)) continue;
5413
- const action = resolveAction(ruleProps["Actions"], resources, tgToService, stackName, `${ruleLabel} action`, warnings);
5414
- if (!action) continue;
5568
+ const ruleResolved = resolveAction(ruleProps["Actions"], resources, tgToService, stackName, `${ruleLabel} action`, warnings);
5569
+ if (!ruleResolved) continue;
5415
5570
  rules.push({
5416
5571
  priority,
5417
5572
  pathPatterns,
@@ -5420,15 +5575,17 @@ function resolveAlbFrontDoor(stack, albLogicalId) {
5420
5575
  httpRequestMethods,
5421
5576
  queryStringConditions,
5422
5577
  sourceIpCidrs,
5423
- action
5578
+ action: ruleResolved.action,
5579
+ ...ruleResolved.authGuard ? { authGuard: ruleResolved.authGuard } : {}
5424
5580
  });
5425
5581
  }
5426
- if (!defaultAction && rules.length === 0) continue;
5582
+ if (!defaultResolved && rules.length === 0) continue;
5427
5583
  listeners.push({
5428
5584
  listenerPort: port,
5429
5585
  listenerProtocol: protocol,
5430
5586
  listenerLogicalId,
5431
- ...defaultAction ? { defaultAction } : {},
5587
+ ...defaultResolved ? { defaultAction: defaultResolved.action } : {},
5588
+ ...defaultResolved?.authGuard ? { defaultAuthGuard: defaultResolved.authGuard } : {},
5432
5589
  rules
5433
5590
  });
5434
5591
  }
@@ -5444,15 +5601,16 @@ function isApplicationLoadBalancer(resource) {
5444
5601
  return type === void 0 || type === "application";
5445
5602
  }
5446
5603
  /**
5447
- * Resolve a listener / rule `Actions` (or `DefaultActions`) array to the single
5448
- * action the local front-door serves, or `undefined` when it is not resolvable
5449
- * (a warning is emitted for the cases worth surfacing). ALB allows exactly one
5450
- * non-authenticate terminal action per action set, optionally preceded by
5451
- * authenticate-* actions (which the local front-door does not enforce — they
5452
- * are skipped with a warning and the terminal action is honored).
5604
+ * Resolve a listener / rule `Actions` (or `DefaultActions`) array. ALB allows
5605
+ * any number of `authenticate-cognito` / `authenticate-oidc` entries followed
5606
+ * by exactly one terminal action (`forward` / `redirect` / `fixed-response`).
5607
+ * The first parseable authenticate-* (last wins if multiple) becomes the
5608
+ * returned `authGuard`; the terminal action becomes `action`. Returns
5609
+ * `undefined` when no terminal action is resolvable.
5453
5610
  */
5454
5611
  function resolveAction(actions, resources, tgToService, stackName, label, warnings) {
5455
5612
  if (!Array.isArray(actions)) return void 0;
5613
+ let authGuard;
5456
5614
  let sawAuthenticate = false;
5457
5615
  for (const action of actions) {
5458
5616
  if (!action || typeof action !== "object") continue;
@@ -5460,18 +5618,21 @@ function resolveAction(actions, resources, tgToService, stackName, label, warnin
5460
5618
  const type = a["Type"];
5461
5619
  if (type === "authenticate-cognito" || type === "authenticate-oidc") {
5462
5620
  sawAuthenticate = true;
5621
+ const parsed = parseAuthenticateAction(a, type, label, warnings);
5622
+ if (parsed) authGuard = parsed;
5463
5623
  continue;
5464
5624
  }
5465
- if (type === "forward") return resolveForwardAction(a, resources, tgToService, stackName, label, warnings);
5466
- if (type === "redirect") {
5467
- const redirect = resolveRedirectAction(a, label, warnings);
5468
- if (redirect) return redirect;
5469
- continue;
5470
- }
5471
- if (type === "fixed-response") return resolveFixedResponseAction(a);
5472
- if (typeof type === "string") warnings.push(`${label} uses an unsupported action type '${type}'. The local ALB front-door supports forward / redirect / fixed-response actions only. Skipping it.`);
5625
+ let terminal;
5626
+ if (type === "forward") terminal = resolveForwardAction(a, resources, tgToService, stackName, label, warnings);
5627
+ else if (type === "redirect") terminal = resolveRedirectAction(a, label, warnings);
5628
+ else if (type === "fixed-response") terminal = resolveFixedResponseAction(a);
5629
+ else if (typeof type === "string") warnings.push(`${label} uses an unsupported action type '${type}'. The local ALB front-door supports forward / redirect / fixed-response actions only. Skipping it.`);
5630
+ if (terminal) return authGuard ? {
5631
+ action: terminal,
5632
+ authGuard
5633
+ } : { action: terminal };
5473
5634
  }
5474
- if (sawAuthenticate) warnings.push(`${label} is an authenticate-* action with no local-servable terminal action. The local ALB front-door does not enforce authenticate-cognito / authenticate-oidc; skipping it.`);
5635
+ if (sawAuthenticate) warnings.push(`${label} is an authenticate-* action with no local-servable terminal action; skipping it.`);
5475
5636
  }
5476
5637
  /**
5477
5638
  * Resolve a `forward` action into one or more weighted targets. Each target
@@ -5877,6 +6038,75 @@ function parsePriority(raw) {
5877
6038
  if (typeof raw === "string" && /^\d+$/.test(raw)) return parseInt(raw, 10);
5878
6039
  return Number.MAX_SAFE_INTEGER;
5879
6040
  }
6041
+ /** ALB's default session cookie name prefix (suffixed `-0` / `-1` / ... by ALB). */
6042
+ const DEFAULT_ALB_SESSION_COOKIE = "AWSELBAuthSessionCookie";
6043
+ /**
6044
+ * Cognito User Pool ARN shape:
6045
+ * `arn:aws:cognito-idp:<region>:<account>:userpool/<pool-id>`
6046
+ * The pool id itself contains a `_`, but never a `/`, so anchoring on the
6047
+ * `userpool/` segment is robust.
6048
+ */
6049
+ const COGNITO_USERPOOL_ARN = /^arn:[^:]+:cognito-idp:([^:]+):[^:]+:userpool\/([^/]+)$/;
6050
+ /**
6051
+ * Parse an `authenticate-cognito` / `authenticate-oidc` ALB action into a
6052
+ * resolvable {@link FrontDoorAuthGuard}, or `undefined` when the config is
6053
+ * unresolvable (a Ref / intrinsic in a required field, a malformed
6054
+ * UserPoolArn, etc.) — a warning is emitted in that case so the user knows
6055
+ * the guard was dropped (the terminal action will still serve unguarded).
6056
+ */
6057
+ function parseAuthenticateAction(action, type, label, warnings) {
6058
+ if (type === "authenticate-cognito") {
6059
+ const cfg = action["AuthenticateCognitoConfig"];
6060
+ if (!cfg || typeof cfg !== "object") {
6061
+ warnings.push(`${label}: authenticate-cognito missing AuthenticateCognitoConfig; skipping guard.`);
6062
+ return;
6063
+ }
6064
+ const c = cfg;
6065
+ const userPoolArn = c["UserPoolArn"];
6066
+ const userPoolClientId = c["UserPoolClientId"];
6067
+ if (typeof userPoolArn !== "string" || typeof userPoolClientId !== "string") {
6068
+ warnings.push(`${label}: authenticate-cognito UserPoolArn / UserPoolClientId must be literal strings (Ref / intrinsics cannot be resolved by the local front-door); skipping guard.`);
6069
+ return;
6070
+ }
6071
+ const match = COGNITO_USERPOOL_ARN.exec(userPoolArn);
6072
+ if (!match) {
6073
+ warnings.push(`${label}: authenticate-cognito UserPoolArn '${userPoolArn}' is not in the expected arn:...:cognito-idp:<region>:<account>:userpool/<pool-id> shape; skipping guard.`);
6074
+ return;
6075
+ }
6076
+ const region = match[1];
6077
+ const userPoolId = match[2];
6078
+ const sessionCookieName = typeof c["SessionCookieName"] === "string" && c["SessionCookieName"] !== "" ? c["SessionCookieName"] : DEFAULT_ALB_SESSION_COOKIE;
6079
+ return {
6080
+ kind: "authenticate-cognito",
6081
+ issuer: `https://cognito-idp.${region}.amazonaws.com/${userPoolId}`,
6082
+ audience: userPoolClientId,
6083
+ region,
6084
+ userPoolId,
6085
+ sessionCookieName,
6086
+ label: `authenticate-cognito (UserPool=${userPoolId})`
6087
+ };
6088
+ }
6089
+ const cfg = action["AuthenticateOidcConfig"];
6090
+ if (!cfg || typeof cfg !== "object") {
6091
+ warnings.push(`${label}: authenticate-oidc missing AuthenticateOidcConfig; skipping guard.`);
6092
+ return;
6093
+ }
6094
+ const c = cfg;
6095
+ const issuer = c["Issuer"];
6096
+ const clientId = c["ClientId"];
6097
+ if (typeof issuer !== "string" || typeof clientId !== "string") {
6098
+ warnings.push(`${label}: authenticate-oidc Issuer / ClientId must be literal strings (Ref / intrinsics cannot be resolved by the local front-door); skipping guard.`);
6099
+ return;
6100
+ }
6101
+ const sessionCookieName = typeof c["SessionCookieName"] === "string" && c["SessionCookieName"] !== "" ? c["SessionCookieName"] : DEFAULT_ALB_SESSION_COOKIE;
6102
+ return {
6103
+ kind: "authenticate-oidc",
6104
+ issuer: issuer.replace(/\/+$/, ""),
6105
+ audience: clientId,
6106
+ sessionCookieName,
6107
+ label: `authenticate-oidc (Issuer=${issuer})`
6108
+ };
6109
+ }
5880
6110
 
5881
6111
  //#endregion
5882
6112
  //#region src/cli/commands/local-start-alb.ts
@@ -6018,6 +6248,7 @@ function albStrategy(options) {
6018
6248
  hostPort,
6019
6249
  protocol: listener.listenerProtocol,
6020
6250
  ...listener.defaultAction ? { defaultAction: qualify(listener.defaultAction) } : {},
6251
+ ...listener.defaultAuthGuard ? { defaultAuthGuard: listener.defaultAuthGuard } : {},
6021
6252
  rules: listener.rules.map((r) => ({
6022
6253
  priority: r.priority,
6023
6254
  pathPatterns: r.pathPatterns,
@@ -6026,7 +6257,8 @@ function albStrategy(options) {
6026
6257
  httpRequestMethods: r.httpRequestMethods,
6027
6258
  queryStringConditions: r.queryStringConditions,
6028
6259
  sourceIpCidrs: r.sourceIpCidrs,
6029
- action: qualify(r.action)
6260
+ action: qualify(r.action),
6261
+ ...r.authGuard ? { authGuard: r.authGuard } : {}
6030
6262
  }))
6031
6263
  });
6032
6264
  }
@@ -6055,7 +6287,7 @@ function albStrategy(options) {
6055
6287
  */
6056
6288
  function createLocalStartAlbCommand(opts = {}) {
6057
6289
  setEmbedConfig(opts.embedConfig);
6058
- return addCommonEcsServiceOptions(new Command("start-alb").description("Run an Application Load Balancer locally: name the ALB, and cdk-local boots the ECS service(s) behind its listeners and stands up a local front-door on each listener port that round-robins across the running replicas and routes its listener rules across the backing services — a stable host endpoint, like behind a real load balancer. The symmetric ALB counterpart of `start-api`. Each <target> accepts a CDK display path (MyStack/MyAlb) or stack-qualified logical ID; single-stack apps may omit the stack prefix. Supports HTTP and HTTPS listeners (TLS terminated locally with --tls-cert/--tls-key or an auto-generated self-signed cert); all six ALB rule-condition fields (path-pattern / host-header / http-header / http-request-method / query-string / source-ip); forward (single and weighted), redirect, and fixed-response actions; and ECS or Lambda targets (a Lambda target group is invoked locally via the Lambda RIE). authenticate-cognito / authenticate-oidc actions are skipped with a warning. Omit <targets> in an interactive terminal to multi-select the load balancers from a list.").argument("[targets...]", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ElasticLoadBalancingV2::LoadBalancer resources to run (omit to multi-select interactively in a TTY)").addOption(new Option("--lb-port <listenerPort=hostPort...>", "Bind the local front-door on a specific host port (e.g. 80=8080); repeatable. Default: host port == ALB listener port. Use this on macOS to remap a privileged listener port (< 1024) to a non-privileged host port.")).addOption(new Option("--tls-cert <path>", "PEM-encoded server certificate for HTTPS front-door listeners. Must be set together with --tls-key. Omit both flags to auto-generate a self-signed cert (cached under $XDG_CACHE_HOME/cdk-local/alb-https/, default ~/.cache/cdk-local/alb-https/); requires openssl on PATH. The deployed Listener Certificates[] are NOT fetched (ACM private keys are not retrievable by design). The auto-generated cert lists DNS:localhost,IP:127.0.0.1 as SubjectAltName, so a client validating a non-loopback --container-host will fail the SAN check — pass --tls-cert / --tls-key with a SAN covering that host instead.")).addOption(new Option("--tls-key <path>", "PEM-encoded server private key matching --tls-cert. Must be set together with --tls-cert.")).action(withErrorHandling(async (targets, options) => {
6290
+ return addCommonEcsServiceOptions(new Command("start-alb").description("Run an Application Load Balancer locally: name the ALB, and cdk-local boots the ECS service(s) behind its listeners and stands up a local front-door on each listener port that round-robins across the running replicas and routes its listener rules across the backing services — a stable host endpoint, like behind a real load balancer. The symmetric ALB counterpart of `start-api`. Each <target> accepts a CDK display path (MyStack/MyAlb) or stack-qualified logical ID; single-stack apps may omit the stack prefix. Supports HTTP and HTTPS listeners (TLS terminated locally with --tls-cert/--tls-key or an auto-generated self-signed cert); all six ALB rule-condition fields (path-pattern / host-header / http-header / http-request-method / query-string / source-ip); forward (single and weighted), redirect, and fixed-response actions; and ECS or Lambda targets (a Lambda target group is invoked locally via the Lambda RIE). authenticate-cognito / authenticate-oidc actions enforce a local Bearer-JWT check (or AWSELBAuthSessionCookie pass-through) against the same JWKS / OIDC discovery URL the deployed ALB would; use --bearer-token <jwt> to inject a default token or --no-verify-auth to disable the guard. Omit <targets> in an interactive terminal to multi-select the load balancers from a list.").argument("[targets...]", "One or more CDK display paths or stack-qualified logical IDs of the AWS::ElasticLoadBalancingV2::LoadBalancer resources to run (omit to multi-select interactively in a TTY)").addOption(new Option("--lb-port <listenerPort=hostPort...>", "Bind the local front-door on a specific host port (e.g. 80=8080); repeatable. Default: host port == ALB listener port. Use this on macOS to remap a privileged listener port (< 1024) to a non-privileged host port.")).addOption(new Option("--tls-cert <path>", "PEM-encoded server certificate for HTTPS front-door listeners. Must be set together with --tls-key. Omit both flags to auto-generate a self-signed cert (cached under $XDG_CACHE_HOME/cdk-local/alb-https/, default ~/.cache/cdk-local/alb-https/); requires openssl on PATH. The deployed Listener Certificates[] are NOT fetched (ACM private keys are not retrievable by design). The auto-generated cert lists DNS:localhost,IP:127.0.0.1 as SubjectAltName, so a client validating a non-loopback --container-host will fail the SAN check — pass --tls-cert / --tls-key with a SAN covering that host instead.")).addOption(new Option("--tls-key <path>", "PEM-encoded server private key matching --tls-cert. Must be set together with --tls-cert.")).addOption(new Option("--no-verify-auth", "Disable local enforcement of authenticate-cognito / authenticate-oidc actions. Every request is served as if the auth check passed. Useful for local dev where you do not want to mint a Bearer token at all.")).addOption(new Option("--bearer-token <jwt>", "Default Bearer JWT injected as Authorization: Bearer <jwt> when the inbound request has none. Verified against the same JWKS / OIDC discovery URL the deployed ALB would (signature + iss + aud + exp). Local-dev convenience; cookie pass-through (AWSELBAuthSessionCookie-*) also works.")).action(withErrorHandling(async (targets, options) => {
6059
6291
  await runEcsServiceEmulator(targets, options, albStrategy(options), opts.extraStateProviders);
6060
6292
  })));
6061
6293
  }
@@ -6132,4 +6364,4 @@ function createLocalListCommand(opts = {}) {
6132
6364
 
6133
6365
  //#endregion
6134
6366
  export { createLocalRunTaskCommand as a, createLocalStartServiceCommand as i, formatTargetListing as n, createLocalInvokeAgentCoreCommand as o, createLocalStartAlbCommand as r, createLocalInvokeCommand as s, createLocalListCommand as t };
6135
- //# sourceMappingURL=local-list-B-5gO8ht.js.map
6367
+ //# sourceMappingURL=local-list-ChGfKSkV.js.map