Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions changelog.d/20261102_lab-11096.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
### Feature matrix — TypeScript cache keys hash through the Rust bindings from npm 0.2.2 (LAB-11096)

- The **Blake2b-256 key generation** TypeScript cell said "via @noble/hashes". From
`@cachekit-io/cachekit` 0.2.2, keys hash with Rust `blake2` through the NAPI binding on
Node and the wasm binding on Workers, once a cache there first instantiates the module.
@noble/hashes stays as the fallback and gives the same digest. Checked by running the
published 0.2.2 and its pinned `cachekit-core-ts` 0.2.1 and `cachekit-core-wasm` 0.2.1.
- Note ¹'s Workers binding sizes were those of the first wasm build. The published
`cachekit-core-wasm` 0.2.1 is 186,188 B (66,940 B gzipped) plus 27,654 B of JS glue,
built with wasm-opt `-O3` and simd128, not `-Oz`.
4 changes: 2 additions & 2 deletions sdk-feature-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
| OrjsonSerializer (fast JSON) | ✅ `[json]` extra | N/A | N/A | N/A |
| ArrowSerializer (columnar) | ✅ `[data]` extra | N/A | ❌ declined⁰ | ❌ |
| ByteStorage (LZ4 + xxHash3-64) | ✅ via Rust FFI | ✅ canonical (cachekit-core) | ✅ via NAPI (Rust) | 🔜 Planned |
| Blake2b-256 key generation | ✅ | ✅ interop mode only — N/A auto mode (see [Compliance Status](#compliance-status) note ¹⁴) | ✅ via @noble/hashes | 🔜 Planned |
| Blake2b-256 key generation | ✅ | ✅ interop mode only — N/A auto mode (see [Compliance Status](#compliance-status) note ¹⁴) | ✅ `@cachekit-io/cachekit` 0.2.2+ — Rust `blake2` through the bindings ([cachekit-ts#259](https://github.com/cachekit-io/cachekit-ts/pull/259)): NAPI on Node, wasm on Workers once a cache first instantiates the module (compression or encryption on). @noble/hashes, same digest, before that on Workers, for Workers hash input over 64 KiB, and for a binding that lacks `blake2b256Hex`, fails its self-test or throws (`setKeyHashBinding` in `serialization/blake2b.ts`, `initWasm` in `workers/runtime.ts`; `test/workers/key-hash.workers.test.ts`, `test/node-entry/older-core.test.ts`). 0.2.2 pins `cachekit-core-ts` 0.2.1 and `cachekit-core-wasm` 0.2.1; the three published artifacts were run on 2026-11-02. 0.2.1 and earlier: @noble/hashes only | 🔜 Planned |

> ⁰ TypeScript Arrow was listed 🔜 Planned from 2026-06-06 with no code, stub, or tracking issue behind it; corrected to ❌, then **declined** on 2026-09-23. No Node columnar use case has been reported or found, and cross-SDK values are interop/v1 MessagePack only, so a TypeScript Arrow serializer would serve single-SDK Node workloads alone. Have one? Open an issue on [cachekit-ts](https://github.com/cachekit-io/cachekit-ts/issues). Orjson and Arrow live behind cachekit-py's `[json]` / `[data]` extras (`pyproject.toml`, `[project.optional-dependencies]`).

Expand Down Expand Up @@ -121,7 +121,7 @@
| Cache API (Cloudflare) | N/A | ❌ | ✅ `workersCacheAPI` on the `/workers` entry (LAB-750)¹ | N/A |
| DynamoDB | ❌² | ❌ | ❌ | ❌ |

> ¹ Shipped in LAB-595 (2026-07-24), following spike LAB-431's GO verdict: the `@cachekit-io/cachekit/workers` subpath (also the `workerd` condition on the root export) runs crypto and the ByteStorage envelope on a **wasm32 build of cachekit-core** (`@cachekit-io/cachekit-core-wasm`, 137 KB raw / ~55 KB gzipped + ~10 KB JS glue, wasm-bindgen `--target web` + wasm-opt `-Oz`) — counter nonces and the envelope carry over unchanged and the crypto stays single-touchpoint in the Rust core; byte-verified against the Python-ground-truth `test-vectors/encryption.json` and `wire-format.json` suites inside real workerd (`wire-format.json` 1.4.0 `vectors`, `constructed_vectors` including the `bin16` → `bin32` edge, and, since npm 0.2.1, all fourteen `reject_vectors` and both `payload_reject_vectors` on the envelope read path — [cachekit-ts#198](https://github.com/cachekit-io/cachekit-ts/pull/198), 1.4.0 in [cachekit-ts#229](https://github.com/cachekit-io/cachekit-ts/pull/229); see the Wire format row). Backend surface (phase 2, LAB-750): CachekitIO (pure `fetch`), **Workers KV** (`workersKV({ kv })` over a `KVNamespace` binding; native `expirationTtl` — KV rejects values under its 60s minimum, so the SDK clamps shorter TTLs up to 60s before the `put`, and by an SDK rule (not KV's) `ttl <= 0` stores without expiry; eventually consistent ~60s), the **Cache API** (`workersCacheAPI()` over `caches.default`/named caches; per-data-center only, `Cache-Control: max-age` to the second, best-effort eviction, keys mapped to synthetic never-fetched URLs), or a custom `Backend` instance — all storage transports over the unchanged opaque ByteStorage payload (encryption above the backend; secure caches store ciphertext only). Backend instances are accepted by the `minimal`/`production`/`secure` intents (`{ backend }` in place of `{ url }`, both entrypoints); Redis-URL intents, Redis Pub/Sub invalidation, and Prometheus metrics stay Node-only and are excluded from the edge bundle (CI-guarded: no `node:*`, no NAPI, no ioredis/prom-client — no `nodejs_compat` flag needed); SWR background refresh requires binding the request's `ExecutionContext` — `cache.withExecutionContext(ctx)` returns a cheap per-request view whose refreshes ride `ctx.waitUntil`, because workerd cancels fire-and-forget work at response return; a read on a cache with no bound context fails safe to a plain (no-SWR) L1 get (LAB-751, the module doc comment of `packages/cachekit/src/workers/index.ts`; `workersRuntime.swrRequiresWaitUntil` and `WorkersCache.withExecutionContext` in `workers/runtime.ts`). Semantics delta: keys live in wasm linear memory (a host-readable ArrayBuffer), weaker isolation than NAPI's Rust heap but ~JS-heap-equivalent on Workers where the host is your own isolate; zeroized deterministically on `dispose()`. WebCrypto (AES-256-GCM + HKDF-SHA256, random-nonce fallback per [encryption.md → Nonce Generation](spec/encryption.md#nonce-generation)) remains the documented fallback if the wasm path ever hits a wall.
> ¹ Shipped in LAB-595 (2026-07-24), following spike LAB-431's GO verdict: the `@cachekit-io/cachekit/workers` subpath (also the `workerd` condition on the root export) runs crypto and the ByteStorage envelope on a **wasm32 build of cachekit-core** (`@cachekit-io/cachekit-core-wasm`; the published 0.2.1 is a 186,188 B `.wasm`, 66,940 B at `gzip -9`, plus 27,654 B of JS glue, 6,084 B gzipped, measured 2026-11-02; wasm-bindgen `--target web` + wasm-opt `-O3` with simd128) — counter nonces and the envelope carry over unchanged and the crypto stays single-touchpoint in the Rust core; byte-verified against the Python-ground-truth `test-vectors/encryption.json` and `wire-format.json` suites inside real workerd (`wire-format.json` 1.4.0 `vectors`, `constructed_vectors` including the `bin16` → `bin32` edge, and, since npm 0.2.1, all fourteen `reject_vectors` and both `payload_reject_vectors` on the envelope read path — [cachekit-ts#198](https://github.com/cachekit-io/cachekit-ts/pull/198), 1.4.0 in [cachekit-ts#229](https://github.com/cachekit-io/cachekit-ts/pull/229); see the Wire format row). Backend surface (phase 2, LAB-750): CachekitIO (pure `fetch`), **Workers KV** (`workersKV({ kv })` over a `KVNamespace` binding; native `expirationTtl` — KV rejects values under its 60s minimum, so the SDK clamps shorter TTLs up to 60s before the `put`, and by an SDK rule (not KV's) `ttl <= 0` stores without expiry; eventually consistent ~60s), the **Cache API** (`workersCacheAPI()` over `caches.default`/named caches; per-data-center only, `Cache-Control: max-age` to the second, best-effort eviction, keys mapped to synthetic never-fetched URLs), or a custom `Backend` instance — all storage transports over the unchanged opaque ByteStorage payload (encryption above the backend; secure caches store ciphertext only). Backend instances are accepted by the `minimal`/`production`/`secure` intents (`{ backend }` in place of `{ url }`, both entrypoints); Redis-URL intents, Redis Pub/Sub invalidation, and Prometheus metrics stay Node-only and are excluded from the edge bundle (CI-guarded: no `node:*`, no NAPI, no ioredis/prom-client — no `nodejs_compat` flag needed); SWR background refresh requires binding the request's `ExecutionContext` — `cache.withExecutionContext(ctx)` returns a cheap per-request view whose refreshes ride `ctx.waitUntil`, because workerd cancels fire-and-forget work at response return; a read on a cache with no bound context fails safe to a plain (no-SWR) L1 get (LAB-751, the module doc comment of `packages/cachekit/src/workers/index.ts`; `workersRuntime.swrRequiresWaitUntil` and `WorkersCache.withExecutionContext` in `workers/runtime.ts`). Semantics delta: keys live in wasm linear memory (a host-readable ArrayBuffer), weaker isolation than NAPI's Rust heap but ~JS-heap-equivalent on Workers where the host is your own isolate; zeroized deterministically on `dispose()`. WebCrypto (AES-256-GCM + HKDF-SHA256, random-nonce fallback per [encryption.md → Nonce Generation](spec/encryption.md#nonce-generation)) remains the documented fallback if the wasm path ever hits a wall.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
>
> ² DynamoDB has never shipped in any SDK. The previous Python ✅ traced to the [custom-backend tutorial](https://github.com/cachekit-io/cachekit-py/blob/main/docs/backends/custom.md), which shows how a *user* can implement the backend protocol against DynamoDB — that is an extension point, not shipped support (LAB-273).
>
Expand Down
Loading