diff --git a/sdk-feature-matrix.md b/sdk-feature-matrix.md index a1f64e9..fad50c7 100644 --- a/sdk-feature-matrix.md +++ b/sdk-feature-matrix.md @@ -56,6 +56,20 @@ > โฐ TypeScript Arrow was listed ๐Ÿ”œ Planned from 2026-06-06 with no code, stub, or tracking issue behind it; corrected to โŒ, with LAB-524 owning the implement-or-decline decision. Orjson and Arrow live behind cachekit-py's `[json]` / `[data]` extras (`pyproject.toml:73-81`). +### Namespace semantics (per-SDK divergence) + +*Re-verified 2026-09-19 (LAB-646) against py `cachekit-py/src/cachekit/key_generator.py`, rs `cachekit-rs/crates/cachekit/src/client.rs`, ts `cachekit-ts/packages/cachekit/src/serialization/key-generator.ts` + `types/cache.ts` + `cache-core.ts` โ€” all public SDK repos, `main`. Server-side behavior is cited via the public spec only; PHP has no namespace implementation to audit. Descriptive only, no new key-format requirement.* + +| Semantic | Python | Rust | TypeScript | +| :--- | :--- | :--- | :--- | +| Key-prefix shape (auto mode) | `ns:{namespace}:func:...` โ€” a dedicated `ns:` token (`spec/cache-key-format.md:36`, `key_generator.py:94-95`) | N/A โ€” no auto-mode key format; `get`/`set` take caller-supplied keys and the `#[cachekit]` macro mints interop/v1 keys only (see [Compliance Status](#compliance-status) note ยนโด) | `{namespace}:{blake2b-hex}` โ€” one colon, no `ns:` token (`key-generator.ts:33-44`) | +| Key-prefix shape (caller-supplied key) | N/A โ€” decorator-only public API, no key-taking `get`/`set` | `{namespace}:{key}` when `.namespace()` is set on the builder, else the bare key โ€” one colon, no `ns:` token (`client.rs:246-250`) | `get`/`set`/`delete` hand the key to the backend verbatim โ€” no prefix applied (`cache-core.ts:683,829,856`); the `namespace` set-option only groups the entry for L1 invalidation (`types/cache.ts:47-48`, `cache-core.ts:768`) | +| Default / unset namespace | Falsy (`None`/`""`) omits the `ns:` segment entirely (`key_generator.py:94-95`) | No SDK-level default โ€” `namespace: Option`, `None` โ†’ bare key (`client.rs:246-250`); `Some("")` is rejected at `build()` (`client.rs:1090-1092`) | Required, non-optional field with nothing to fall back to (`types/cache.ts:56`); an empty string still reaches `generateKey` unchecked (`key-generator.ts:44`) | +| Charset validation on the namespace value | None โ€” spliced in as-is (`key_generator.py:94-95`); only the unrelated `func:` segment is sanitized (`key_generator.py:364-381`) | Setter accepts any `impl Into` (`client.rs:918-922`); `build()` rejects empty, >255-byte, or non-printable-ASCII values (`client.rs:1088-1101`) โ€” `:` is printable ASCII, so an `ns:`-shaped value passes | None in auto mode (`key-generator.ts:44`); interop mode's namespace/operation segments are validated against a fixed pattern at wrap time (`validateInteropSegment`, `cache-core.ts:922-923`) | + +> [!NOTE] +> Authorization on the namespace is a per-API-key grant โ€” a request the key isn't permitted for is refused with `403` (`spec/saas-api.md:417`) โ€” and is separate from tenant isolation, which comes from authentication rather than key parsing (`spec/interop-mode.md:375-376`). + --- ## Encryption