@cosmicdrift/kumiko-samples 0.348.0 → 0.348.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-samples",
3
- "version": "0.348.0",
3
+ "version": "0.348.1",
4
4
  "description": "Source trees of the Kumiko sample recipes, sample apps and bundled features in repo layout, for tooling such as few-shot corpus builds.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-bundled-features",
3
- "version": "0.348.0",
3
+ "version": "0.348.1",
4
4
  "description": "Built-in features — tenant, user, auth, delivery. The stuff you'd rewrite anyway, already typed.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -1,4 +1,11 @@
1
1
  [
2
+ {
3
+ "version": "0.348.1",
4
+ "type": "fix",
5
+ "title": "Email channel decrypts the resolved recipient address so notify to users and tenants works with an active KMS",
6
+ "detail": "Email notifications to resolved users reach the plaintext address under an active KMS\nWith an active PII KMS, an app's `resolveEmail` that reads the stored `user.email` returns ciphertext, and the PII guard rightly refused to send it (`recipient address is a PII ciphertext`). That broke every `notify` target that resolves an address from a user id: a single user, a list of users and `{ tenant }`. The email channel now decrypts the resolved address (`decryptStoredPii`, a no-op without KMS or for plaintext) before it is used for delivery, opt-out checks and the attempt log. The guard is unchanged.",
7
+ "migration": "No action needed. Apps whose resolveEmail returns the raw stored user.email now send to the plaintext address instead of failing with \"recipient address is a PII ciphertext\"."
8
+ },
2
9
  {
3
10
  "version": "0.342.0",
4
11
  "type": "improvement",
@@ -1,3 +1,4 @@
1
+ import { PII_ERASED_SENTINEL } from "@cosmicdrift/kumiko-framework/crypto";
1
2
  import type { DbRow } from "@cosmicdrift/kumiko-framework/db";
2
3
  import type { TenantId } from "@cosmicdrift/kumiko-framework/engine";
3
4
  import {
@@ -8,6 +9,7 @@ import {
8
9
  type NotificationRenderer,
9
10
  type RenderedMessage,
10
11
  } from "../delivery/index.js";
12
+ import { decryptStoredPii } from "../shared/index.js";
11
13
  import { guardEmailMessage } from "./pii-guard.js";
12
14
  import type { EmailTransport } from "./types.js";
13
15
 
@@ -117,8 +119,12 @@ export function createEmailChannel(options: EmailChannelOptions): DeliveryChanne
117
119
  name: "email",
118
120
  mode: "queued",
119
121
 
122
+ // resolveEmail typically returns the raw stored user.email, which is ciphertext under an active KMS.
120
123
  async resolve(userId, ctx) {
121
- return resolveEmail(userId, ctx);
124
+ const stored = await resolveEmail(userId, ctx);
125
+ if (stored === null) return null;
126
+ const address = await decryptStoredPii(stored, "email", "channel-email:resolve");
127
+ return address === PII_ERASED_SENTINEL ? null : address;
122
128
  },
123
129
 
124
130
  render(message, _ctx) {