Skip to content

cachekit: v0.2.0

Choose a tag to compare

@cachekit-release-bot cachekit-release-bot released this 20 Oct 23:47
a6762aa

0.2.0 (2026-10-16)

⚠ BREAKING CHANGES

  • intents: createCache.minimal(), .production(), .secure() and .io() now throw ConfigurationError at construction for any option they do not apply. encryption, masterKey, previousMasterKeys or tenantId on minimal or production throws: use createCache.secure(), createCache.io() with encryption, or createCache() with encryption. encryption on secure throws: pass masterKey, previousMasterKeys and tenantId at the top level. masterKey, previousMasterKeys or tenantId at the top level of io throws: nest them under encryption. backend, url and keyPrefix on io, metrics and reliability on minimal, stampede on any preset, keyPrefix beside a backend instance, and misspelt options such as defaultTtl also throw. An option set to undefined is still accepted. On every encrypted cache, and in new EncryptionManager(…), a tenantId that is set but is not a string, such as null, throws ConfigurationError.
  • encryption: bind the backend keyPrefix in the AES-GCM AAD (LAB-6396) (#208)
  • l1: L1Cache and createCache (via its l1 option) now reject an l1.maxMemory of Infinity, NaN, 0 or a negative number, throwing ConfigurationError. A caller who passed Infinity for "no memory bound" should pass Number.MAX_SAFE_INTEGER; maxEntries remains the hard bound on L1's size. Every l1 field set to undefined or null now takes its default instead of replacing it.
  • serialization: MessagePackSerializer (and createCache via its serializer option) now rejects maxEncodedSize, maxDecodedSize or maxCollectionSize set to undefined, NaN, Infinity, 0, a negative number or a non-integer, throwing ConfigurationError. A caller who passed Infinity for "no limit" should pass Number.MAX_SAFE_INTEGER.
  • serialization: serializer.maxDepth must now be an integer from 32 to 1024. Any other value, including NaN, Infinity or an explicit undefined, throws ConfigurationError when the cache is created. Previously, values below 32 were honoured, and NaN or undefined silently disabled the depth check.
  • interop: an interop namespace or operation containing .. now throws ConfigurationError at wrap time, on every backend; the exported generateInteropKey throws it at call time. Rename the segment; its keys become a full cache miss.
  • invalidation: L1Config.invalidationEnabled is removed from the exported L1Config interface and from DEFAULT_L1_CONFIG, and the intent presets no longer set it. Nothing read the field. TypeScript consumers that set invalidationEnabled will get a compile error and should delete the property. Runtime behavior does not change. Separately, invalidate('params') without a key now stops before any L1 mutation, backend.delete, or invalidation publish, logs once and resolves; it previously resolved silently and still published a params event with no hash.
  • interop: ns and nsapi are reserved interop namespaces. cache.wrap, generateInteropKey and validateInteropSegment throw ConfigurationError for them, and for non-string namespace or operation segments. Deployments that used ns or nsapi as an interop namespace on non-CachekitIO backends must rename the namespace. The rename makes every existing entry in that namespace a cache miss.
  • encryption: a secure cache created without a tenantId now binds tenant "default" into the AES-GCM AAD instead of "". That is the value HKDF already derived its key from and the value the protocol requires, so a tenant-less cachekit-ts secure cache now shares ciphertext with cachekit-py and cachekit-rs on tenant "default". Caches with an explicit tenantId are unaffected. Entries written by a tenant-less secure cache on @cachekit-io/cachekit 0.1.5 or earlier no longer authenticate, so every read of one fails decryption. To migrate, delete those entries (or switch to a fresh namespace or key prefix) once no pre-upgrade instance is still writing; during a rolling upgrade, old and new instances cannot read each other's entries. Do not wait out the TTL instead. Under the secure preset's default settings, each undecryptable read returns a miss; it is not retried and does not count as a circuit-breaker failure.
  • cache: secure.wrap() fails closed when encryption is not configured (LAB-513) (#123)

Features

  • cachekit: User-Agent, per-runtime probes and a live dev harness (LAB-7059) (#195) (a35bdd7)
  • encryption: previousMasterKeys keyring rotation surface (LAB-685) (#103) (e7d1a8f)
  • encryption: surface hardware-acceleration detection (LAB-523) (#132) (0ae83ba)
  • retry: add retry.deadline, one budget in ms shared by all attempts of an operation; the production, secure and io presets set it to 5000 (LAB-7080) (#200) (f978bbe)

Bug Fixes

  • cache: bound envelope unpack by maxDecodedSize (LAB-2732) (#141) (898d37d)
  • cache: decode a decrypted plaintext as plain MessagePack, never a sniffed envelope (LAB-8182) (#213) (7be7f40)
  • cache: decode after run('get') so decode failures are not retried or counted by the breaker (LAB-7079) (#175) (3b646f6)
  • cachekitio: default to a 5 s per-attempt timeout inside one shared retry deadline (LAB-7080) (#200) (f978bbe)
  • cachekitio: DELETE never reports existence, so a 404 throws (LAB-5580) (#178) (6499bd8)
  • cachekitio: honour X-CacheKit-Freshness and Fresh-For before backfilling L1 (LAB-7883) (#206) (985df71)
  • cachekitio: never follow redirects; tighten API URL validation and send the URL as parsed (LAB-8208) (#216) (6af4b80)
  • cachekitio: reject reserved cache-key segments in request path (CWE-22, LAB-2877) (#118) (c93083b)
  • cachekitio: send X-CacheKit-TTL and reject invalid TTLs per protocol spec (LAB-239) (#110) (0862629)
  • cachekitio: take only a non-empty string lock_id as a lease (LAB-8206) (#215) (f6045cc)
  • cache: log a pack or encrypt failure that degradation absorbs (LAB-7539) (#188) (cc17ba2)
  • cache: pack and encrypt set() values before the reliability executor (LAB-7203) (#185) (f8357e4)
  • cache: read legacy array-encoded envelopes on compression-off caches (LAB-5642) (#163) (d85ec11)
  • cache: secure.wrap() fails closed when encryption is not configured (LAB-513) (#123) (87dee26)
  • cache: serialize set() values before the reliability executor (LAB-5139) (#136) (4d44605)
  • cache: warn on every set() encode rejection, not just size (LAB-4845) (#140) (b11f48e)
  • cache: write L1 in wrap() even when the L2 write fails (LAB-7157) (#173) (969a700)
  • deps: require ioredis ^5.11.1 so the url check matches the parser (LAB-7566) (#191) (74f01fc)
  • encryption: bind the backend keyPrefix in the AES-GCM AAD (LAB-6396) (#208) (490a5a3)
  • encryption: resolve tenant_id once for HKDF and AAD (LAB-4668) (#133) (8bb5269)
  • encryption: throw ConfigurationError for secure-cache keys over the 64 KiB AAD limit (LAB-5142) (#138) (b777ada)
  • envelope: name size-cap, zero-length and ratio rejections; run wire-format reject vectors (LAB-7483) (#198) (2e44a33)
  • envelope: refuse a slot over-claim with the envelope pre-scan's own error (LAB-8577) (#232) (3f81a17)
  • file: expire a File-backend entry at its expiry second (LAB-8073) (#212) (14aa68c)
  • intents: minimal honours explicit L1 flags; reject an empty tenantId at construction (LAB-8225) (#226) (d087944)
  • intents: reject options a createCache preset does not apply (LAB-8218) (#214) (fce2e9b)
  • interop: cap a Set's size before encoding its elements (LAB-7831) (#201) (4ebac78)
  • interop: fire map/object collection cap before key materialisation (LAB-413) (#113) (403b1b3)
  • interop: read proto keys and U+FEFF strings; re-vendor interop-mode 1.3.0 and decode-bounds 1.2.0 (LAB-8308) (#224) (a8e8e0b)
  • interop: reject double-dot interop segments (LAB-5906) (#164) (4ae5c90)
  • interop: reject reserved namespaces ns and nsapi (LAB-5876) (#143) (40835c8)
  • interop: reject Symbol-keyed object properties (LAB-8740) (#233) (d0eaf3e)
  • invalidation: deserializeEvent fails closed with SerializationError on every malformed payload (LAB-3477) (#126) (059ccc6)
  • invalidation: invalidate() reports params no-ops and L2 delete failures; remove dead l1.invalidationEnabled (LAB-4576) (#149) (a788aa2)
  • invalidation: report a non-string key or namespace at the caller (LAB-6400) (#160) (cca8365)
  • L1 TTL cap, loud size rejections, Node-free workers types (LAB-1388) (#98) (13a3345)
  • l1: cap one entry's charge at an eighth of maxMemory (LAB-7810) (#196) (2c20485)
  • l1: charge an overflowing size hint or count as no hint (LAB-7584) (#204) (692e00e)
  • l1: charge maxMemory per container, not just per serialized byte (LAB-7688) (#193) (17c2a85)
  • l1: charge maxMemory per element and entry, not just per object (LAB-7723) (#199) (19b6f80)
  • l1: escape sourceInstance in-library before it reaches the log sink (LAB-4522) (#147) (7d74c6b)
  • l1: hold an unstorable SWR refresh off without taking a refresh slot (LAB-7811) (#197) (88a8a3c)
  • l1: never admit an entry charged above maxMemory (LAB-7729) (#194) (d6e35d4)
  • l1: reject a non-finite or non-positive maxMemory (LAB-8011) (#207) (7155d43)
  • logger: report a rejecting async logger instead of leaking it (LAB-8278) (#220) (cf30cf4)
  • memcached: bound every op with a deadline so a stalled server cannot hang it (LAB-7156) (#174) (ed32184)
  • memcached: reject keys over 250 bytes before sending (LAB-5226) (917cfc1)
  • memcached: report a failed memjs load once per backend (LAB-7548) (#189) (4c2f755)
  • reliability: don't retry or count permanent backend errors toward the breaker (LAB-5215) (#139) (49f0954)
  • reliability: free a half-open probe slot on a success that does not close the breaker (LAB-9775) (f076ac4)
  • security: store ciphertext in L1 for encrypted caches (LAB-238) (#104) (0b1b2f8)
  • serialization: bound msgpack decode at all untrusted call sites (LAB-281) (#111) (19ad90c)
  • serialization: bound msgpack decode nesting depth before allocation (LAB-2487) (#112) (906942d)
  • serialization: cache Uint8Array values as msgpack bin; binary key args hash by bytes (LAB-4839) (#134) (be40638)
  • serialization: reject maxDepth outside the protocol's 32-1024 decode bound (LAB-3478) (#167) (6a4d074)
  • serialization: reject non-integer serializer size bounds; validate config before opening the backend (LAB-6965) (#168) (2b81c3e)
  • workers-kv: always issue kv.delete in delete() (LAB-8103) (#219) (86a2153)
  • workers: keep compression on by default for the Cache API backend (LAB-5126) (#137) (ada34a8)

Performance Improvements

  • cachekit: load ioredis with the Redis backend and prom-client off the op path (LAB-7081) (#186) (a44d32e)
  • core-bindings: return NAPI results as copied Uint8Arrays (LAB-7084) (#171) (460a15f)
  • core-wasm: build at opt-level 3 with simd128 (LAB-7083) (#169) (47d1ea3)
  • l1: O(1) LRU eviction, serialized-size hint, pooled jitter randomness (LAB-7082) (#190) (0648888)
  • lock: skip the uncontended double-check, waitUntil the release, re-check before fall-through (LAB-7119) (#209) (7bd3877)
  • memcached: default memjs to one try per cache attempt (LAB-7078) (#187) (f00b465)