From c97dad073d59c63384362fca67dc312f74c5a455 Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Sat, 7 Nov 2026 07:53:51 +0000 Subject: [PATCH 1/2] refactor(client): __interop_read awaits one shared read, opens ciphertext synchronously An encrypting client's CacheKit::__interop_read awaited SecureCache::__interop_read before its own read, which repeated the reject, key resolution and read_bytes. That kept a secure future in the state of the generic #[cachekit] poll function every plain read shares, the coupling interop_read_swr already dropped. Both clients now reject, resolve and await read_bytes once; an encrypting client then calls SecureCache::open synchronously on the bytes. SecureCache::__interop_read is deleted. No AAD, key, key-format or wire change. --- crates/cachekit/src/client.rs | 38 +++++++++++++---------------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/crates/cachekit/src/client.rs b/crates/cachekit/src/client.rs index 75053a2..b29729c 100644 --- a/crates/cachekit/src/client.rs +++ b/crates/cachekit/src/client.rs @@ -1254,16 +1254,23 @@ impl CacheKit { &self, key: &str, ) -> Result, CachekitError> { - #[cfg(feature = "encryption")] - if let Some(secure) = self.encrypting() { - return secure.__interop_read(key).await; - } self.reject_namespaced_interop()?; let full_key = self.resolve_key(key)?; - match self.read_bytes(&full_key, None).await? { - Some((payload, by)) => Ok(Some((crate::interop::deserialize_payload(&payload)?, by))), - None => Ok(None), + let Some((payload, by)) = self.read_bytes(&full_key, None).await? else { + return Ok(None); + }; + // Branch after the await, synchronously, as `interop_read_swr` does. + #[cfg(feature = "encryption")] + if let Some(secure) = self.encrypting() { + let value = secure.open( + &payload, + by, + &full_key, + crate::interop::deserialize_decrypted, + )?; + return Ok(Some((value, by))); } + Ok(Some((crate::interop::deserialize_payload(&payload)?, by))) } /// [`Self::interop_get_swr`] for a `#[cachekit]` call, uncounted like @@ -1619,23 +1626,6 @@ impl SecureCache<'_> { Ok(read) } - /// The secure twin of [`CacheKit::__interop_read`], which hands an - /// encrypting client's reads here. - async fn __interop_read( - &self, - key: &str, - ) -> Result, CachekitError> { - self.client.reject_namespaced_interop()?; - let full_key = self.client.resolve_key(key)?; - match self.client.read_bytes(&full_key, None).await? { - Some((ct, by)) => Ok(Some(( - self.open(&ct, by, &full_key, crate::interop::deserialize_decrypted)?, - by, - ))), - None => Ok(None), - } - } - /// [`Self::open`] over `read`, what an encrypting client's /// [`CacheKit::__interop_read_swr`] or /// [`CacheKit::__interop_read_swr_leading`] read for `full_key`. From b4a64d547df5691feaeb6b952f9bb5f48e3fbf42 Mon Sep 17 00:00:00 2001 From: Ray Walker Date: Sat, 7 Nov 2026 08:04:43 +0000 Subject: [PATCH 2/2] docs(client): encrypting() states the branch-before-encode/decode rule the interop reads follow Both interop reads now resolve and read before they branch on encrypting(), so 'every value method routes through it first' no longer held. What holds, and what the protocol rule needs, is that every value method branches on it before it encodes or decodes a value. --- crates/cachekit/src/client.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/cachekit/src/client.rs b/crates/cachekit/src/client.rs index b29729c..59bf488 100644 --- a/crates/cachekit/src/client.rs +++ b/crates/cachekit/src/client.rs @@ -1390,9 +1390,9 @@ impl CacheKit { } /// The encrypting handle when encryption is configured. Every value - /// method routes through it first, so a configured key leaves no - /// plaintext path (protocol `spec/intent-presets.md` § Encryption - /// Activation, rule 1). + /// method branches on it before it encodes or decodes a value, so a + /// configured key leaves no plaintext path (protocol + /// `spec/intent-presets.md` § Encryption Activation, rule 1). #[cfg(feature = "encryption")] fn encrypting(&self) -> Option> { self.encryption.as_deref().map(|encryption| SecureCache {