@jaypie/mcp 0.8.159 → 0.8.160

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/dist/assets.js CHANGED
@@ -55,11 +55,11 @@ function getMcpAssetPaths() {
55
55
  */
56
56
  function getMcpBuildInfo() {
57
57
  return {
58
- commit: "4e7a07fe"
58
+ commit: "b6d2f24e"
59
59
  ,
60
- version: "0.8.159"
60
+ version: "0.8.160"
61
61
  ,
62
- versionString: "@jaypie/mcp@0.8.159#4e7a07fe"
62
+ versionString: "@jaypie/mcp@0.8.160#b6d2f24e"
63
63
  ,
64
64
  };
65
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/mcp",
3
- "version": "0.8.159",
3
+ "version": "0.8.160",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,67 @@
1
+ ---
2
+ version: 1.2.88
3
+ date: 2026-09-15
4
+ summary: Datadog forwarder invoke permissions, hardened log buckets, origin access control, WAF header redaction, and JaypieMigration log props
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **Datadog forwarder invoke permissions.** `resolveDatadogForwarderFunction`
10
+ and `JaypieDatadogForwarder` import the forwarder with
11
+ `fromFunctionAttributes({ sameEnvironment: true })`. `fromFunctionArn` on a
12
+ token ARN yields an unresolved environment, so `addPermission()` was a silent
13
+ no-op and every trigger deployed without invoke permission (#560).
14
+ `JaypieDatadogForwarder` acknowledges
15
+ `@aws-cdk/aws-events:ruleUnresolvedEnvironment` on its own events rule.
16
+ - **Hardened default log buckets.** The CloudFront access log bucket and WAF log
17
+ bucket that `JaypieDistribution` and `JaypieWebDeploymentBucket` create now
18
+ block all public access, enforce SSL, are versioned, use SSE-S3, expire
19
+ objects after `logRetention` (365 days by default, new prop), and carry
20
+ `RemovalPolicy.RETAIN` with no auto-delete. The WAF bucket policy declares the
21
+ `delivery.logs.amazonaws.com` statements so CloudFormation owns it (#561).
22
+ - **`JaypieDistribution` origin access control.** `originAccessControl: true`
23
+ creates an `AWS_IAM` Function URL, uses
24
+ `FunctionUrlOrigin.withOriginAccessControl`, and adds the
25
+ `lambda:InvokeFunction` permission Lambda requires alongside
26
+ `lambda:InvokeFunctionUrl`. Opt-in; the default stays `authType: NONE` (#562).
27
+ - **`JaypieWebDeploymentBucket` origin access control.**
28
+ `originAccessControl: true` serves a private bucket through the S3 REST
29
+ endpoint with OAC, `BLOCK_ALL`, `enforceSSL`, no website configuration, and
30
+ `defaultRootObject: "index.html"`. Opt-in; the default stays the public S3
31
+ website bucket (#563).
32
+ - **WAF log redaction.** `authorization`, `cookie`, `x-amz-content-sha256`, and
33
+ `x-api-key` are always redacted from WAF logs. The `waf` config accepts
34
+ `redactedHeaders`, which merges with that default rather than replacing it,
35
+ and a `redactedFields` passthrough for query strings, URI paths, and JSON body
36
+ fields (#564).
37
+ - **`waf.logRetention`** overrides the construct's `logRetention` for the WAF
38
+ log bucket alone.
39
+ - **`JaypieDistribution` warns** (`@jaypie/constructs:originAccessControlIgnored`)
40
+ when `originAccessControl` is set with a handler that supplies its own
41
+ Function URL or origin, where the prop has no effect.
42
+ - **`JaypieMigration` rejects `reservedConcurrentExecutions: 0`** with a
43
+ `ConfigurationError`. The migration Lambda is both `onEventHandler` and
44
+ `isCompleteHandler`, so a reservation of 0 means it is never invoked.
45
+ - **`JaypieDatadogForwarder` throws `ConfigurationError`** rather than a vanilla
46
+ `Error` when the Datadog API key is missing.
47
+ - **`JaypieMigration` log props.** `logGroup`, `logRetention`, and
48
+ `reservedConcurrentExecutions` forward to the wrapped `JaypieLambda` (#565).
49
+
50
+ ## Upgrade
51
+
52
+ - **Log buckets survive stack deletion.** The CloudFront log bucket moves from
53
+ `DESTROY` with auto-delete to `RETAIN`. Deleting a stack now leaves the bucket
54
+ and its objects behind as a billable resource to remove by hand.
55
+ - **Retention rises from 90 to 365 days** on the construct-created log buckets,
56
+ roughly quadrupling retained volume. Pass `logRetention: 90` to keep the old
57
+ value. Versioning is a one-way door on an existing bucket.
58
+ - **The WAF log bucket gains a CloudFormation-owned bucket policy.** Any
59
+ statement added to that bucket out of band is discarded on the next deploy.
60
+ - **Existing WAF logging configurations gain `RedactedFields`** on the next
61
+ deploy. The four default headers are always redacted; there is no opt-out.
62
+ - **Datadog forwarder triggers gain `AWS::Lambda::Permission` resources** on the
63
+ next deploy, and S3 notification configurations that previously failed
64
+ validation now apply.
65
+ - `originAccessControl` is opt-in on both constructs; no existing deployment
66
+ changes without setting it. With it enabled on `JaypieDistribution`, clients
67
+ must send the body SHA-256 in `x-amz-content-sha256` on POST and PUT.
@@ -0,0 +1,19 @@
1
+ ---
2
+ version: 0.8.160
3
+ date: 2026-09-15
4
+ summary: Document forwarder invoke permissions, hardened log buckets, origin access control, WAF redaction, and JaypieMigration log props
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`skill("JaypieDatadogForwarder")`** gains an "Invoke Permissions" section
10
+ covering token-ARN imports and the events-rule warning acknowledgement.
11
+ - **`skill("cdk")`** gains "Origin Access Control" and "Importing a Lambda by
12
+ Token ARN" sections, and the WAF summary lists the new config keys.
13
+ - **`skill("waf")`** documents the hardened log bucket, `waf.logRetention`, and
14
+ the merge semantics of `redactedHeaders` / `redactedFields`.
15
+ - **`skill("web")`** gains an "Origin Access Control" section and documents
16
+ `logRetention` and the hardened log buckets.
17
+ - **`skill("migrations")`** documents `logGroup`, `logRetention`, and
18
+ `reservedConcurrentExecutions`, including the inert-`logRetention` gap
19
+ deferred to 2.0 (#568) and the zero-concurrency throw.
@@ -72,6 +72,21 @@ const forwarderFunction = resolveDatadogForwarderFunction(this);
72
72
 
73
73
  This imports the Lambda via `Fn.importValue("account-datadog-forwarder")` and caches per scope (WeakMap), so multiple calls in the same stack return the same reference.
74
74
 
75
+ ## Invoke Permissions
76
+
77
+ The forwarder ARN is always an unresolved token: a cross-stack export or a nested-stack output. `lambda.Function.fromFunctionArn` on a token ARN produces an import with an unknown environment, and `addPermission()` on such an import is a silent no-op that only emits an `UnclearLambdaEnvironment` warning. Every trigger wired to the forwarder then deploys with no invoke permission, and S3 rejects the notification configuration because it cannot validate the destination.
78
+
79
+ Both import sites use `lambda.Function.fromFunctionAttributes(scope, id, { functionArn, sameEnvironment: true })`. The flag is correct here because the forwarder is always in the same account and region as its consumers.
80
+
81
+ `JaypieDatadogForwarder` acknowledges `@aws-cdk/aws-events:ruleUnresolvedEnvironment` on its own `CloudFormationEventsRule`. Acknowledgements resolve by construct path, so a consumer creating a rule that targets `forwarder.forwarderFunction` sees that warning on their own rule and should acknowledge it with the same justification:
82
+
83
+ ```typescript
84
+ Annotations.of(rule).acknowledgeWarning(
85
+ "@aws-cdk/aws-events:ruleUnresolvedEnvironment",
86
+ "The Datadog forwarder is always in the same account and region",
87
+ );
88
+ ```
89
+
75
90
  ### For Log Subscription Filters
76
91
 
77
92
  Use `resolveDatadogLoggingDestination` to get a `LambdaDestination`:
package/skills/cdk.md CHANGED
@@ -440,8 +440,50 @@ KnownBadInputsRuleSet, IP rate limiting, and WAF logging to S3 with Datadog
440
440
  forwarding.
441
441
 
442
442
  See **`skill("waf")`** for configuration: `rateLimitPerIp`, `webAclArn`,
443
- `logBucket`, `managedRuleOverrides`, `managedRuleScopeDowns`, the `allow`
444
- path-scoped relaxation prop, and the rule-name ↔ label casing trap.
443
+ `logBucket`, `logRetention`, `managedRuleOverrides`, `managedRuleScopeDowns`,
444
+ `redactedFields`, `redactedHeaders`, the `allow` path-scoped relaxation prop,
445
+ and the rule-name ↔ label casing trap.
446
+
447
+ WAF logs always redact `authorization`, `cookie`, `x-amz-content-sha256`, and
448
+ `x-api-key`. A `redactedHeaders` list merges with that default rather than
449
+ replacing it, so adding a header can only ever redact more.
450
+
451
+ ## Origin Access Control
452
+
453
+ `JaypieDistribution` with an `IFunction` handler creates a Function URL with
454
+ `authType: NONE`, so the Lambda is invokable around CloudFront and the WAF.
455
+ `originAccessControl: true` makes CloudFront the only caller:
456
+
457
+ ```typescript
458
+ new JaypieDistribution(this, "Dist", {
459
+ handler,
460
+ originAccessControl: true,
461
+ });
462
+ ```
463
+
464
+ - The Function URL becomes `AWS_IAM`, the origin becomes
465
+ `origins.FunctionUrlOrigin.withOriginAccessControl(...)`, and the construct
466
+ adds the `lambda:InvokeFunction` permission that `withOriginAccessControl`
467
+ omits (Lambda requires both it and `lambda:InvokeFunctionUrl`).
468
+ - **Client requirement:** with OAC, Lambda rejects a POST or PUT unless the
469
+ client sends the body SHA-256 in `x-amz-content-sha256`. For a body carrying
470
+ low-entropy secrets, salt it with a nonce so the hash is not reversible. That
471
+ header is redacted from WAF logs by default.
472
+ - The prop applies only to the `IFunction` path. A caller-supplied
473
+ `IFunctionUrl` or `IOrigin` owns its own auth; setting the prop there warns
474
+ at synth (`@jaypie/constructs:originAccessControlIgnored`).
475
+ - `JaypieWebDeploymentBucket` has its own `originAccessControl` prop for the S3
476
+ case; see `skill("web")`.
477
+
478
+ ## Importing a Lambda by Token ARN
479
+
480
+ `lambda.Function.fromFunctionArn` on an ARN that is a token (`Fn.importValue`,
481
+ a nested-stack output) yields an import with an unresolved environment.
482
+ `addPermission()` on it is a silent no-op that only warns
483
+ (`UnclearLambdaEnvironment`), so every trigger deploys without invoke
484
+ permission. Use
485
+ `lambda.Function.fromFunctionAttributes(scope, id, { functionArn, sameEnvironment: true })`
486
+ whenever the function is known to be in the same account and region.
445
487
 
446
488
  ## Organization Trail Security Baseline
447
489
 
@@ -35,7 +35,10 @@ new JaypieMigration(this, "SeedData", {
35
35
  | `dependencies` | `Construct[]` | `[]` | Constructs that must be created before the migration runs |
36
36
  | `environment` | `Record<string, string> \| (Record<string, string> \| string)[]` | - | Environment variables for the migration Lambda |
37
37
  | `handler` | `string` | `"index.handler"` | Lambda entry point |
38
+ | `logGroup` | `logs.ILogGroup` | - | Log group for the migration Lambda; forwarded to `JaypieLambda`. Supply one to control KMS encryption and retention |
39
+ | `logRetention` | `logs.RetentionDays \| number` | `90` days | Retention for the log group `JaypieLambda` creates. Ignored when `logGroup` is supplied |
38
40
  | `queryInterval` | `cdk.Duration` | `Duration.seconds(60)` | Polling interval between `isCompleteHandler` invocations |
41
+ | `reservedConcurrentExecutions` | `number` | - | Reserved concurrency for the migration Lambda. `0` throws at synth; `1` leaves no headroom if onEvent overlaps a waiter poll |
39
42
  | `secrets` | `SecretsArrayItem[]` | `[]` | Secrets to make available to the Lambda |
40
43
  | `tables` | `dynamodb.ITable[]` | `[]` | DynamoDB tables to grant read/write access |
41
44
  | `timeout` | `cdk.Duration` | `Duration.minutes(15)` | Per-invocation Lambda timeout |
@@ -45,6 +48,9 @@ new JaypieMigration(this, "SeedData", {
45
48
 
46
49
  - **Timeout**: 15 minutes per invocation (Lambda max); `totalTimeout` controls the end-to-end ceiling across all polling invocations (default 2 hours)
47
50
  - **Role**: Tagged as `CDK.ROLE.PROCESSING`
51
+ - **Logging**: `logGroup`, `logRetention`, and `reservedConcurrentExecutions` forward to the wrapped `JaypieLambda`. Pass `logGroup` when a compliance baseline requires a KMS-encrypted group or 12-month retention. The `cr.Provider` framework functions keep service-created log groups
52
+ - **`logRetention` is inert when `logGroup` is supplied.** `JaypieLambda` resolves `logGroup ?? new logs.LogGroup(..., { retention: logRetention })`, so passing both silently drops the retention value. Set retention on the group you pass. Jaypie 2.0 will throw on that combination (#568)
53
+ - **Concurrency**: the migration Lambda is both `onEventHandler` and `isCompleteHandler`. `reservedConcurrentExecutions: 0` blocks every invocation and throws `ConfigurationError` at synth
48
54
  - **Execution**: Uses `cr.Provider` with both `onEventHandler` and `isCompleteHandler` pointing to the same Lambda. The `onEventHandler` returns `PhysicalResourceId` immediately; the migration code runs in `isCompleteHandler` invocations, which are polled by Step Functions until `IsComplete: true`. `Delete` requests skip the migration entirely.
49
55
  - **Dependencies**: Use `dependencies` to ensure tables and other resources exist before the migration executes
50
56
  - **Permissions**: Tables passed via `tables` get data-plane (`grantReadWriteData`), `Query` and `Scan` on `${tableArn}/index/*`, plus `DescribeTable`, `DescribeTimeToLive`, and `DescribeContinuousBackups` scoped to the table ARN and its indexes. Migrations read and write data. They do not change table shape
package/skills/waf.md CHANGED
@@ -53,6 +53,51 @@ new JaypieDistribution(this, "Dist", {
53
53
  Cost: $5/month per WebACL + $1/month per rule + $0.60 per million requests. That
54
54
  cost is why WAF is opt-in; set `waf: true` (or a config object) to opt in.
55
55
 
56
+ ## Log bucket
57
+
58
+ The construct-created WAF log bucket blocks all public access, enforces SSL, is
59
+ versioned, uses SSE-S3, retains objects for `logRetention` (365 days by
60
+ default), and carries `RemovalPolicy.RETAIN` with no auto-delete. It survives
61
+ stack deletion; delete it by hand when tearing an environment down.
62
+
63
+ CloudFormation owns the whole bucket policy, including the two
64
+ `delivery.logs.amazonaws.com` delivery statements. A policy statement added to
65
+ that bucket out of band (console, CLI, another stack) is discarded on the next
66
+ deploy.
67
+
68
+ A consumer-supplied `logBucket` is used as given: it needs its own hardening and
69
+ its own delivery policy.
70
+
71
+ ## Redact headers from WAF logs
72
+
73
+ WAF logs record every request header. Jaypie always redacts
74
+ `authorization`, `cookie`, `x-amz-content-sha256`, and `x-api-key`
75
+ (`DEFAULT_WAF_REDACTED_HEADERS`). `redactedHeaders` names more:
76
+
77
+ ```typescript
78
+ new JaypieDistribution(this, "Dist", {
79
+ handler,
80
+ waf: { name: "api", redactedHeaders: ["x-session-token"] },
81
+ });
82
+ ```
83
+
84
+ - The list **merges** with the defaults rather than replacing them, so adding a
85
+ header can only ever redact more. There is no way to un-redact a default.
86
+ - Names are compared case-insensitively, as WAF matches them, so naming a
87
+ default again does not duplicate it.
88
+ - `x-amz-content-sha256` is in the default because `originAccessControl`
89
+ requires clients to send a SHA-256 of the request body. For a low-entropy body
90
+ that hash is a body fingerprint.
91
+ - `redactedFields` passes `wafv2.CfnLoggingConfiguration.FieldToMatchProperty`
92
+ entries (query string, URI path, JSON body) through and layers on top of the
93
+ redacted headers.
94
+ - WAF caps the combined list at 100 entries; exceeding it throws a
95
+ `ConfigurationError` at synth.
96
+
97
+ `waf.logRetention` overrides the construct's `logRetention` for the WAF log
98
+ bucket alone, so WAF logs and CloudFront access logs can keep different
99
+ retention.
100
+
56
101
  ## Override specific managed rule actions
57
102
 
58
103
  Flip a named sub-rule from `block` to `count` everywhere (e.g. allow large
package/skills/web.md CHANGED
@@ -42,14 +42,16 @@ new JaypieWebDeploymentBucket(this, "Web", {
42
42
  | `defaultBehavior` | `Partial<BehaviorOptions>` | undefined — merged over the construct's default behavior; keys given win |
43
43
  | `destination` | `LambdaDestination \| boolean` | `true` (Datadog forwarder for access-log bucket notifications) |
44
44
  | `logBucket` | `IBucket \| string \| { exportName } \| true` | undefined — creates a new bucket if `destination !== false` |
45
+ | `logRetention` | `Duration \| number` | `Duration.days(365)` — retention for the log buckets the construct creates; `waf.logRetention` overrides it for the WAF bucket |
45
46
  | `name` | `string` | `constructEnvName(component)` |
47
+ | `originAccessControl` | `boolean` | `false` — serve the bucket privately through the S3 REST endpoint with CloudFront OAC |
46
48
  | `responseHeadersPolicy` | `IResponseHeadersPolicy` | undefined — full override; bypasses default security headers |
47
49
  | `roleTag` | `string` | `CDK.ROLE.HOSTING` |
48
50
  | `securityHeaders` | `boolean \| SecurityHeadersOverrides` | `true` |
49
51
  | `spa` | `boolean` | `false` — viewer-request rewrite of extension-less URIs to `/index.html` |
50
52
  | `waf` | `boolean \| JaypieWebDeploymentBucketWafConfig` | `false` (when enabled, WAF name defaults to construct id) |
51
53
 
52
- Also accepts all `s3.BucketProps` — the bucket defaults to `autoDeleteObjects: true`, `publicReadAccess: true`, `websiteIndexDocument: "index.html"`, `websiteErrorDocument: "index.html"` — which renders an SPA deep link but answers 404; see [Single-Page App](#single-page-app).
54
+ Also accepts all `s3.BucketProps`. With `originAccessControl: false` (the default) the bucket is a public S3 website: `autoDeleteObjects: true`, `publicReadAccess: true`, `websiteIndexDocument: "index.html"`, `websiteErrorDocument: "index.html"` — which renders an SPA deep link but answers 404; see [Single-Page App](#single-page-app). With `originAccessControl: true` the bucket is private; see [Origin Access Control](#origin-access-control).
53
55
 
54
56
  ### Bucket Name
55
57
 
@@ -67,6 +69,7 @@ Changing `component` or `name` on a deployed stack renames the bucket, which rep
67
69
  - Default behavior: S3 static website origin, `REDIRECT_TO_HTTPS`. `CACHING_DISABLED` outside production; `CACHING_OPTIMIZED` in production (`isProductionEnv()`), so hashed assets get edge cache. The response headers policy sets `Cache-Control: no-store` so browsers still revalidate.
68
70
  - No `/*` behavior is created. Paths registered afterward with `distribution.addBehavior("/app/*", origin)` are evaluated ahead of the default behavior and match in every environment — a static site and a Lambda surface can share one distribution.
69
71
  - Access logs land in a CloudFront log bucket with Datadog forwarding by default. Set `destination: false` to skip notifications, or pass `logBucket: <existing>` to reuse a bucket.
72
+ - The construct-created log buckets block all public access, enforce SSL, are versioned, use SSE-S3, retain objects for `logRetention` (365 days by default), and carry `RemovalPolicy.RETAIN` with no auto-delete. They survive stack deletion; delete them by hand when tearing an environment down.
70
73
 
71
74
  ```typescript
72
75
  const web = new JaypieWebDeploymentBucket(this, "Web", { host, zone });
@@ -107,6 +110,27 @@ new JaypieWebDeploymentBucket(this, "App", {
107
110
  - Exposed as `.spaFunction`. Works without `host` and `zone`, so a deep link resolves on the CloudFront default domain before DNS is wired.
108
111
  - Combining `spa: true` with a caller-supplied **viewer-request** association throws `ConfigurationError` at synth; CloudFront permits one function per event type. Other event types (for example viewer-response) compose, with the rewrite appended last.
109
112
 
113
+ ### Origin Access Control
114
+
115
+ The default bucket is a public S3 website endpoint. CloudFront cannot use origin access control against a website endpoint, so the bucket stays readable around CloudFront and the WAF.
116
+
117
+ `originAccessControl: true` serves the bucket privately:
118
+
119
+ ```typescript
120
+ new JaypieWebDeploymentBucket(this, "Web", {
121
+ host,
122
+ originAccessControl: true,
123
+ spa: true,
124
+ zone,
125
+ });
126
+ ```
127
+
128
+ - The bucket gets `BlockPublicAccess.BLOCK_ALL` and `enforceSSL: true`, with no ACL, no public read, and no website configuration.
129
+ - The default behavior uses `origins.S3BucketOrigin.withOriginAccessControl(bucket)`, and the distribution sets `defaultRootObject: "index.html"`.
130
+ - Combining it with `publicReadAccess: true`, `websiteIndexDocument`, or `websiteErrorDocument` through `bucketProps` throws `ConfigurationError` at synth. Other bucket props still compose.
131
+ - **Pair it with `spa: true`.** The REST endpoint has no error document, and OAC grants only `s3:GetObject`, so a miss returns `403 AccessDenied` rather than the app shell. The SPA function rewrites extension-less deep links before they reach the origin, so they never miss.
132
+ - `isWebsite` is `false` and `bucketWebsiteUrl` no longer addresses a live endpoint.
133
+
110
134
  ### Security Headers
111
135
 
112
136
  Same defaults as `JaypieDistribution`: HSTS, X-Frame-Options, CSP, Referrer-Policy, Permissions-Policy, Cross-Origin-* headers, and Server header removal (applied via a `ResponseHeadersPolicy` on the default behavior). Override the same way: