From 98d78235df5e48a7f472e531cac4af7f82ffb42e Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Fri, 23 Oct 2026 20:22:15 +0000 Subject: [PATCH 1/2] docs(spec): mark the Rust default tenant conformant from cachekit-rs 0.8.0 The Rust cell said from_env() derived under a deployment namespace. No published release does: CacheKit::from_env() falls back to "default" because CachekitConfig::from_env() never sets the namespace it reads, and both secure presets read the default_tenant_interop vector in the SDK's own tests. Checked against every published .crate from 0.8.0 to 0.11.1. --- changelog.d/20261023_lab-4667.md | 8 ++++++++ spec/intent-presets.md | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 changelog.d/20261023_lab-4667.md diff --git a/changelog.d/20261023_lab-4667.md b/changelog.d/20261023_lab-4667.md new file mode 100644 index 0000000..01dde23 --- /dev/null +++ b/changelog.d/20261023_lab-4667.md @@ -0,0 +1,8 @@ +### Intent presets — Rust default tenant reads ✅ (LAB-4667) + +- `spec/intent-presets.md` § SDK Conformance: the Rust cell of "Default `tenant_id` is `"default"`, identical for HKDF + and AAD" flips from ❌ to ✅ from `cachekit-rs` 0.8.0. It had said `from_env()` derived under a deployment + namespace; no published release does. `CacheKit::secure` and `CacheKit::secure_from_env` read the + `default_tenant_interop` vector in the SDK's tests, and through 0.11.x `CacheKit::from_env()` with + `CACHEKIT_MASTER_KEY` set falls back to `"default"` because nothing sets the namespace it reads. Verified against + each published `.crate` from 0.8.0 to 0.11.1. diff --git a/spec/intent-presets.md b/spec/intent-presets.md index 1573038..a7edb77 100644 --- a/spec/intent-presets.md +++ b/spec/intent-presets.md @@ -456,7 +456,7 @@ carry no line cite, because this document's line refs are pinned to `cachekit-rs | `secure` takes a hex key and falls back to `CACHEKIT_MASTER_KEY` | ✅ ≥ 32 B (`validation.py:95`) | ✅ hex `CacheKit::secure(url, master_key_hex)`, ≥ 32 B, and `CacheKit::secure_from_env(url)` reads `CACHEKIT_MASTER_KEY` — `cachekit-rs` 0.8.0+ (0.7.0 and earlier: `encrypted(url, &[u8])`, raw bytes, no env fallback); [Master Key Input](#master-key-input) rule 4: the raw-bytes entry points `encryption_from_bytes`, `encryption_from_bytes_with_previous`, `EncryptionLayer::new` and `EncryptionLayer::with_previous_keys` require exactly 32 B, current and previous keys alike — `cachekit-rs` 0.9.0+ (0.8.0 and earlier check `len() >= 32` and so accept more than exactly 32 B) | ✅ exactly 32 B (`constants.ts:138`) | | Missing master key fails at construction | ✅ `intent.py:212` | ✅ required argument on `::secure`; `::secure_from_env` with `CACHEKIT_MASTER_KEY` unset or empty → `Err` at construction, before any Redis I/O (`cachekit-rs` 0.8.0+); short or non-hex key → `Err` | ✅ `intents-core.ts:257` | | `secure` companion env constructor honours `CACHEKIT_PREVIOUS_MASTER_KEYS` ([Explicit Configuration](#explicit-configuration) rule 4) | N/A — no companion constructor; the preset's optional key argument falls back itself | ✅ `::secure_from_env` reads `CACHEKIT_MASTER_KEY` and `CACHEKIT_PREVIOUS_MASTER_KEYS`, validating the previous keys as `CachekitConfig::from_env()` does, before any Redis I/O — `cachekit-rs` 0.9.0+ (0.8.0 reads `CACHEKIT_MASTER_KEY` only and ignores `CACHEKIT_PREVIOUS_MASTER_KEYS` without an error) | N/A — no companion constructor; the preset's optional `masterKey` falls back itself | -| Default `tenant_id` is `"default"`, identical for HKDF and AAD | ✅ `cachekit` 0.20.0+ — `DEFAULT_TENANT_ID` (LAB-4666); explicit `deployment_uuid` / `CACHEKIT_DEPLOYMENT_UUID` override only, no machine-local fallback | ❌ `"default"` via `::secure` (`::encrypted` through 0.7.0), deployment namespace via `from_env()` — inconsistent by constructor — LAB-4667 | ❌ HKDF `'default'` (`manager-core.ts:206`) but AAD `''` (`manager-core.ts:375`) — mismatched within one SDK — LAB-4668 | +| Default `tenant_id` is `"default"`, identical for HKDF and AAD | ✅ `cachekit` 0.20.0+ — `DEFAULT_TENANT_ID` (LAB-4666); explicit `deployment_uuid` / `CACHEKIT_DEPLOYMENT_UUID` override only, no machine-local fallback | ✅ `cachekit-rs` 0.8.0+ — every path that encrypts with no caller-supplied tenant derives the key and builds the AAD under `"default"`: `CacheKit::secure` and `CacheKit::secure_from_env`, each read against `default_tenant_interop` by `hex_path_decrypts_default_tenant_vector` and `env_path_decrypts_default_tenant_vector`, and, through 0.11.x, `CacheKit::from_env()` with `CACHEKIT_MASTER_KEY` set, whose tenant is a namespace that `CachekitConfig::from_env()` never sets, so always `"default"`. Every other encryption entry point takes the tenant as an argument. Verified 2026-10-23 against each published `.crate` from 0.8.0 to 0.11.1 | ❌ HKDF `'default'` (`manager-core.ts:206`) but AAD `''` (`manager-core.ts:375`) — mismatched within one SDK — LAB-4668 | | `CACHEKIT_MASTER_KEY` does not activate encryption on `minimal` / `production` / `io` | ✅ `cachekit` 0.21.0+ — the key activates no preset but `secure` (its key fallback), and `local` never encrypts. In `cachekit` 0.21.0, migration step 3's one-release gate also makes `minimal`, `production`, `io` and every other preset except `secure` and `local` fail at construction with `ConfigurationError`, naming the explicit spellings, when a key is present and no intent is stated (no `encryption=`, or an `EncryptionConfig` without `enabled=`), `backend=None` included | ❌ `from_env()` activates from key presence alone (`config.rs:112`) — no constructor is exempt under the revised rule 2 — LAB-4669 | ✅ `secure()` only (`intents-core.ts:255`) | | Explicit encryption option encrypts every operation (activation rule 1) | ✅ `cachekit` 0.20.0+ — `encryption=True` wraps the serializer on the backend path, and `backend=None` with encryption raises `ConfigurationError` at decoration | ✅ `cachekit-rs` 0.10.0+ ([cachekit-rs#133](https://github.com/cachekit-io/cachekit-rs/pull/133)) — every builder encryption method (`.encryption()`, `.encryption_from_bytes()`, `.encryption_from_bytes_with_previous()`) and the `secure` / `secure_from_env` presets encrypt every value read and write: `get`, `set`, `set_with_ttl`, `interop_get`, `interop_get_swr` and `#[cachekit]` with or without `secure`. With the `encryption` feature off, the three builder methods return `CachekitError::Config`. Through 0.9.0 only the `SecureCache` handle encrypted, and with the feature off the builder methods returned `Ok(self)` | ✅ `if (this.encryption)` on both paths (`cache-core.ts:558`, `:823`) | | Encrypted preset is spelled `secure` | ✅ `@cache.secure` | ✅ `CacheKit::secure(url, key)` — `cachekit-rs` 0.8.0+ (`CacheKit::encrypted` through 0.7.0) | ✅ `createCache.secure()` | From b818eec3f019db75315a9be1af11a3dc76383e4b Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Fri, 23 Oct 2026 21:14:19 +0000 Subject: [PATCH 2/2] docs(spec): bound the Rust tenant cell's entry-point claim to 0.11.x The claim that every other encryption entry point takes the tenant as an argument sat under an open 0.8.0+ floor, and an unreleased builder method that reads the key from the environment encrypts with no tenant argument. Bound it to 0.11.x like the from_env() clause, and name config.namespace so the fallback does not read as a namespace tenant. --- spec/intent-presets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/intent-presets.md b/spec/intent-presets.md index a7edb77..44b850b 100644 --- a/spec/intent-presets.md +++ b/spec/intent-presets.md @@ -456,7 +456,7 @@ carry no line cite, because this document's line refs are pinned to `cachekit-rs | `secure` takes a hex key and falls back to `CACHEKIT_MASTER_KEY` | ✅ ≥ 32 B (`validation.py:95`) | ✅ hex `CacheKit::secure(url, master_key_hex)`, ≥ 32 B, and `CacheKit::secure_from_env(url)` reads `CACHEKIT_MASTER_KEY` — `cachekit-rs` 0.8.0+ (0.7.0 and earlier: `encrypted(url, &[u8])`, raw bytes, no env fallback); [Master Key Input](#master-key-input) rule 4: the raw-bytes entry points `encryption_from_bytes`, `encryption_from_bytes_with_previous`, `EncryptionLayer::new` and `EncryptionLayer::with_previous_keys` require exactly 32 B, current and previous keys alike — `cachekit-rs` 0.9.0+ (0.8.0 and earlier check `len() >= 32` and so accept more than exactly 32 B) | ✅ exactly 32 B (`constants.ts:138`) | | Missing master key fails at construction | ✅ `intent.py:212` | ✅ required argument on `::secure`; `::secure_from_env` with `CACHEKIT_MASTER_KEY` unset or empty → `Err` at construction, before any Redis I/O (`cachekit-rs` 0.8.0+); short or non-hex key → `Err` | ✅ `intents-core.ts:257` | | `secure` companion env constructor honours `CACHEKIT_PREVIOUS_MASTER_KEYS` ([Explicit Configuration](#explicit-configuration) rule 4) | N/A — no companion constructor; the preset's optional key argument falls back itself | ✅ `::secure_from_env` reads `CACHEKIT_MASTER_KEY` and `CACHEKIT_PREVIOUS_MASTER_KEYS`, validating the previous keys as `CachekitConfig::from_env()` does, before any Redis I/O — `cachekit-rs` 0.9.0+ (0.8.0 reads `CACHEKIT_MASTER_KEY` only and ignores `CACHEKIT_PREVIOUS_MASTER_KEYS` without an error) | N/A — no companion constructor; the preset's optional `masterKey` falls back itself | -| Default `tenant_id` is `"default"`, identical for HKDF and AAD | ✅ `cachekit` 0.20.0+ — `DEFAULT_TENANT_ID` (LAB-4666); explicit `deployment_uuid` / `CACHEKIT_DEPLOYMENT_UUID` override only, no machine-local fallback | ✅ `cachekit-rs` 0.8.0+ — every path that encrypts with no caller-supplied tenant derives the key and builds the AAD under `"default"`: `CacheKit::secure` and `CacheKit::secure_from_env`, each read against `default_tenant_interop` by `hex_path_decrypts_default_tenant_vector` and `env_path_decrypts_default_tenant_vector`, and, through 0.11.x, `CacheKit::from_env()` with `CACHEKIT_MASTER_KEY` set, whose tenant is a namespace that `CachekitConfig::from_env()` never sets, so always `"default"`. Every other encryption entry point takes the tenant as an argument. Verified 2026-10-23 against each published `.crate` from 0.8.0 to 0.11.1 | ❌ HKDF `'default'` (`manager-core.ts:206`) but AAD `''` (`manager-core.ts:375`) — mismatched within one SDK — LAB-4668 | +| Default `tenant_id` is `"default"`, identical for HKDF and AAD | ✅ `cachekit` 0.20.0+ — `DEFAULT_TENANT_ID` (LAB-4666); explicit `deployment_uuid` / `CACHEKIT_DEPLOYMENT_UUID` override only, no machine-local fallback | ✅ `cachekit-rs` 0.8.0+ — every path that encrypts with no caller-supplied tenant derives the key and builds the AAD under `"default"`: `CacheKit::secure` and `CacheKit::secure_from_env`, each read against `default_tenant_interop` by `hex_path_decrypts_default_tenant_vector` and `env_path_decrypts_default_tenant_vector`, and, through 0.11.x, `CacheKit::from_env()` with `CACHEKIT_MASTER_KEY` set, whose tenant is `config.namespace`, which `CachekitConfig::from_env()` never sets, so it falls back to `"default"`. Through 0.11.x, every other encryption entry point takes the tenant as an argument. Verified 2026-10-23 against each published `.crate` from 0.8.0 to 0.11.1 | ❌ HKDF `'default'` (`manager-core.ts:206`) but AAD `''` (`manager-core.ts:375`) — mismatched within one SDK — LAB-4668 | | `CACHEKIT_MASTER_KEY` does not activate encryption on `minimal` / `production` / `io` | ✅ `cachekit` 0.21.0+ — the key activates no preset but `secure` (its key fallback), and `local` never encrypts. In `cachekit` 0.21.0, migration step 3's one-release gate also makes `minimal`, `production`, `io` and every other preset except `secure` and `local` fail at construction with `ConfigurationError`, naming the explicit spellings, when a key is present and no intent is stated (no `encryption=`, or an `EncryptionConfig` without `enabled=`), `backend=None` included | ❌ `from_env()` activates from key presence alone (`config.rs:112`) — no constructor is exempt under the revised rule 2 — LAB-4669 | ✅ `secure()` only (`intents-core.ts:255`) | | Explicit encryption option encrypts every operation (activation rule 1) | ✅ `cachekit` 0.20.0+ — `encryption=True` wraps the serializer on the backend path, and `backend=None` with encryption raises `ConfigurationError` at decoration | ✅ `cachekit-rs` 0.10.0+ ([cachekit-rs#133](https://github.com/cachekit-io/cachekit-rs/pull/133)) — every builder encryption method (`.encryption()`, `.encryption_from_bytes()`, `.encryption_from_bytes_with_previous()`) and the `secure` / `secure_from_env` presets encrypt every value read and write: `get`, `set`, `set_with_ttl`, `interop_get`, `interop_get_swr` and `#[cachekit]` with or without `secure`. With the `encryption` feature off, the three builder methods return `CachekitError::Config`. Through 0.9.0 only the `SecureCache` handle encrypted, and with the feature off the builder methods returned `Ok(self)` | ✅ `if (this.encryption)` on both paths (`cache-core.ts:558`, `:823`) | | Encrypted preset is spelled `secure` | ✅ `@cache.secure` | ✅ `CacheKit::secure(url, key)` — `cachekit-rs` 0.8.0+ (`CacheKit::encrypted` through 0.7.0) | ✅ `createCache.secure()` |