cachekit 0.17.1__tar.gz → 0.19.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. {cachekit-0.17.1 → cachekit-0.19.0}/Cargo.lock +4 -3
  2. {cachekit-0.17.1 → cachekit-0.19.0}/PKG-INFO +49 -22
  3. {cachekit-0.17.1 → cachekit-0.19.0}/README.md +43 -18
  4. {cachekit-0.17.1 → cachekit-0.19.0}/pyproject.toml +47 -19
  5. {cachekit-0.17.1 → cachekit-0.19.0}/rust/Cargo.toml +7 -3
  6. {cachekit-0.17.1 → cachekit-0.19.0}/rust/README.md +43 -18
  7. {cachekit-0.17.1 → cachekit-0.19.0}/rust/TEST_EXPANSION_SUMMARY.md +2 -2
  8. cachekit-0.19.0/rust/src/lib.rs +84 -0
  9. cachekit-0.19.0/rust/src/msgpack_bounds.rs +87 -0
  10. cachekit-0.19.0/rust/src/python_bindings.rs +636 -0
  11. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/__init__.py +8 -7
  12. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/base.py +13 -7
  13. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/cachekitio/backend.py +72 -20
  14. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/cachekitio/error_handler.py +11 -6
  15. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/errors.py +11 -4
  16. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/file/backend.py +118 -34
  17. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/memcached/backend.py +4 -1
  18. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/memcached/error_handler.py +18 -8
  19. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/provider.py +10 -8
  20. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/redis/backend.py +5 -5
  21. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/redis/error_handler.py +12 -7
  22. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/redis/provider.py +25 -11
  23. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/cache_handler.py +250 -109
  24. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/config/decorator.py +0 -6
  25. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/config/nested.py +0 -2
  26. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/config/settings.py +170 -2
  27. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/decorators/intent.py +5 -1
  28. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/decorators/orchestrator.py +26 -9
  29. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/decorators/session.py +16 -8
  30. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/decorators/wrapper.py +294 -106
  31. cachekit-0.19.0/src/cachekit/hash_utils.py +146 -0
  32. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/hiredis_compat.py +5 -3
  33. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/interop.py +2 -4
  34. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/key_generator.py +14 -8
  35. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/l1_cache.py +18 -205
  36. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/logging.py +30 -129
  37. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/monitoring/pool_monitor.py +5 -5
  38. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/object_cache.py +1 -1
  39. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/reliability/async_metrics.py +4 -2
  40. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/reliability/metrics_collection.py +47 -32
  41. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/reliability/profiles.py +0 -14
  42. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/serializers/__init__.py +10 -3
  43. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/serializers/auto_serializer.py +237 -122
  44. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/serializers/base.py +119 -0
  45. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/serializers/encryption_wrapper.py +245 -16
  46. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/serializers/orjson_serializer.py +1 -1
  47. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/serializers/standard_serializer.py +17 -7
  48. cachekit-0.17.1/rust/src/lib.rs +0 -61
  49. cachekit-0.17.1/rust/src/python_bindings.rs +0 -400
  50. cachekit-0.17.1/rust/supply-chain/audits.toml +0 -4
  51. cachekit-0.17.1/rust/supply-chain/config.toml +0 -1341
  52. cachekit-0.17.1/rust/supply-chain/imports.lock +0 -2
  53. cachekit-0.17.1/src/cachekit/hash_utils.py +0 -50
  54. {cachekit-0.17.1 → cachekit-0.19.0}/Cargo.toml +0 -0
  55. {cachekit-0.17.1 → cachekit-0.19.0}/LICENSE +0 -0
  56. {cachekit-0.17.1 → cachekit-0.19.0}/rust/Makefile +0 -0
  57. {cachekit-0.17.1 → cachekit-0.19.0}/rust/tsan_suppressions.txt +0 -0
  58. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/__init__.py +0 -0
  59. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/base_config.py +0 -0
  60. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/cachekitio/__init__.py +0 -0
  61. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/cachekitio/client.py +0 -0
  62. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/cachekitio/config.py +0 -0
  63. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/cachekitio/session.py +0 -0
  64. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/file/__init__.py +0 -0
  65. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/file/config.py +0 -0
  66. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/memcached/__init__.py +0 -0
  67. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/memcached/config.py +0 -0
  68. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/redis/__init__.py +0 -0
  69. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/redis/client.py +0 -0
  70. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/backends/redis/config.py +0 -0
  71. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/config/__init__.py +0 -0
  72. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/config/singleton.py +0 -0
  73. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/config/validation.py +0 -0
  74. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/decorators/__init__.py +0 -0
  75. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/decorators/local_wrapper.py +0 -0
  76. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/decorators/main.py +0 -0
  77. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/decorators/stats_context.py +0 -0
  78. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/decorators/tenant_context.py +0 -0
  79. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/decorators/utils/__init__.py +0 -0
  80. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/di.py +0 -0
  81. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/health.py +0 -0
  82. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/imports.py +0 -0
  83. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/monitoring/__init__.py +0 -0
  84. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/monitoring/correlation_tracking.py +0 -0
  85. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/monitoring/protocols.py +0 -0
  86. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/py.typed +0 -0
  87. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/reliability/__init__.py +0 -0
  88. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/reliability/circuit_breaker.py +0 -0
  89. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/reliability/error_classification.py +0 -0
  90. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/reliability/load_control.py +0 -0
  91. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/serializers/arrow_serializer.py +0 -0
  92. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/serializers/interop_serializer.py +0 -0
  93. {cachekit-0.17.1 → cachekit-0.19.0}/src/cachekit/serializers/wrapper.py +0 -0
@@ -245,9 +245,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
245
245
 
246
246
  [[package]]
247
247
  name = "cachekit-core"
248
- version = "0.4.0"
248
+ version = "0.5.0"
249
249
  source = "registry+https://github.com/rust-lang/crates.io-index"
250
- checksum = "6aba1513135a7b92a124ad6983f7e80e5f5c78c4f9c74384079efa3fbf491eab"
250
+ checksum = "12089baacc5ff661a62d2071588c895973bc48e42ed359178afaee22decb5559"
251
251
  dependencies = [
252
252
  "aes",
253
253
  "aes-gcm",
@@ -271,7 +271,7 @@ dependencies = [
271
271
 
272
272
  [[package]]
273
273
  name = "cachekit-rs"
274
- version = "0.17.1"
274
+ version = "0.19.0"
275
275
  dependencies = [
276
276
  "cachekit-core",
277
277
  "criterion",
@@ -282,6 +282,7 @@ dependencies = [
282
282
  "pprof",
283
283
  "proptest",
284
284
  "pyo3",
285
+ "zeroize",
285
286
  ]
286
287
 
287
288
  [[package]]
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cachekit
3
- Version: 0.17.1
4
- Classifier: Development Status :: 3 - Alpha
3
+ Version: 0.19.0
4
+ Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
7
7
  Classifier: Operating System :: OS Independent
@@ -21,13 +21,15 @@ Classifier: Framework :: AsyncIO
21
21
  Classifier: Typing :: Typed
22
22
  Requires-Dist: redis[hiredis]>=4.0.0
23
23
  Requires-Dist: pydantic>=2.0.0
24
- Requires-Dist: pydantic-settings>=2.0.0
24
+ Requires-Dist: pydantic-settings>=2.7.0
25
25
  Requires-Dist: prometheus-client>=0.22.1
26
26
  Requires-Dist: psutil>=7.0.0
27
27
  Requires-Dist: blake3>=1.0.5
28
28
  Requires-Dist: msgpack>=1.2.1
29
29
  Requires-Dist: xxhash>=3.5.0
30
30
  Requires-Dist: httpx[http2]>=0.28.1
31
+ Requires-Dist: anyio>=4.14.2
32
+ Requires-Dist: h2>=4.4.1
31
33
  Requires-Dist: numpy>=2.0.2 ; extra == 'data'
32
34
  Requires-Dist: pandas>=1.3.0 ; extra == 'data'
33
35
  Requires-Dist: pyarrow>=21.0.0 ; extra == 'data'
@@ -37,7 +39,7 @@ Provides-Extra: data
37
39
  Provides-Extra: json
38
40
  Provides-Extra: memcached
39
41
  License-File: LICENSE
40
- Summary: Production-ready Redis caching for Python with intelligent reliability features and Rust-powered performance
42
+ Summary: Backend-agnostic caching for Python — intent-based decorators with circuit breaker, distributed locking, Prometheus metrics, and optional zero-knowledge AES-256-GCM encryption, on a Rust-powered core. Zero-config L1 in-memory; scales to Redis, Memcached, File, or CachekitIO.
41
43
  Keywords: redis,cache,caching,decorator,rust,performance,reliability,production,encryption,security,circuit-breaker,prometheus,messagepack,distributed-locking
42
44
  Home-Page: https://github.com/cachekit-io/cachekit-py
43
45
  Author-email: cachekit Contributors <noreply@cachekit.io>
@@ -57,7 +59,7 @@ Project-URL: Repository, https://github.com/cachekit-io/cachekit-py.git
57
59
 
58
60
  > **Python caching, batteries included**
59
61
 
60
- Production-ready caching for Python with intelligent reliability features and Rust-powered performance.
62
+ Backend-agnostic caching with intent-based decorators circuit breaker, distributed locking, Prometheus metrics, and optional zero-knowledge encryption on a Rust-powered core. Zero config to start, any backend when you scale.
61
63
 
62
64
  [![PyPI Version][pypi-badge]][pypi-url]
63
65
  [![Python Versions][python-badge]][pypi-url]
@@ -68,20 +70,16 @@ Production-ready caching for Python with intelligent reliability features and Ru
68
70
 
69
71
  ---
70
72
 
71
- > [!WARNING]
72
- > **Alpha Software** — cachekit is under active development. While we've been building and testing for ~6 months, the API is not yet stable and **breaking changes may occur** between releases. We're committed to making this library rock-solid, but we need your help!
73
- >
74
- > 🐛 **Found a bug?** Please [open an issue][issues-url] — even small ones help us improve.
75
- >
76
- > 💡 **Something feel off?** We want to hear about rough edges, confusing APIs, or missing features.
73
+ > [!NOTE]
74
+ > **Status: beta** — CacheKit is in closed beta ahead of 1.0. APIs are stabilising; minor breaking changes may still occur between 0.x releases.
77
75
  >
78
- > Your feedback directly shapes the path to 1.0. Cheers!
76
+ > 🐛 **Found a bug or a rough edge?** Please [open an issue][issues-url] — your feedback directly shapes the path to 1.0.
79
77
 
80
78
  ---
81
79
 
82
80
  ## Why cachekit?
83
81
 
84
- **Simple to use, production-ready out of the box.**
82
+ **Simple to use, works out of the box.**
85
83
 
86
84
  ```python
87
85
  from cachekit import cache
@@ -125,7 +123,7 @@ backend that fits your infrastructure — they're peers behind the same `@cache`
125
123
  | Backend | Best for | Select with |
126
124
  |---------|----------|-------------|
127
125
  | Redis | Self-hosted, full control | `REDIS_URL` / `CACHEKIT_REDIS_URL` |
128
- | CachekitIO | Managed, zero-ops (alpha) | `CACHEKIT_API_KEY` |
126
+ | CachekitIO | Managed, zero-ops (beta) | `CACHEKIT_API_KEY` |
129
127
  | Memcached | High-throughput, existing infra | `CACHEKIT_MEMCACHED_SERVERS` |
130
128
  | File / L1-only | Local dev, tests, no external deps | `CACHEKIT_FILE_CACHE_DIR` / `backend=None` |
131
129
 
@@ -148,7 +146,7 @@ def expensive_api_call(user_id: int):
148
146
  ### More Backends
149
147
 
150
148
  <details>
151
- <summary><strong>CachekitIO — Managed SaaS (Alpha)</strong></summary>
149
+ <summary><strong>CachekitIO — Managed SaaS (Beta)</strong></summary>
152
150
 
153
151
  ```python notest
154
152
  import os
@@ -162,7 +160,7 @@ def expensive_api_call(user_id: int):
162
160
  return fetch_user_data(user_id)
163
161
  ```
164
162
 
165
- *cachekit.io is in closed alpha — [request access](https://cachekit.io) to get started.*
163
+ *cachekit.io is in closed beta — [request access](https://cachekit.io) to get started.*
166
164
 
167
165
  </details>
168
166
 
@@ -188,9 +186,9 @@ def expensive_api_call(user_id: int):
188
186
 
189
187
  ---
190
188
 
191
- > **CachekitIO Cloud (Alpha)**
189
+ > **CachekitIO Cloud (Beta)**
192
190
  > Managed caching with zero infrastructure. L1+L2 caching, circuit breaker, and automatic failover — no Redis to manage.
193
- > *cachekit.io is in closed alpha — [request access](https://cachekit.io) to get started.*
191
+ > *cachekit.io is in closed beta — [request access](https://cachekit.io) to get started.*
194
192
 
195
193
  ---
196
194
 
@@ -221,9 +219,8 @@ def get_user_profile(user_id: int):
221
219
  | Backpressure | ✅ | ✅ | - | ✅ | ✅ |
222
220
  | Integrity Checking | - | ✅ | - | ✅ | ✅ 🔒 |
223
221
  | Encryption | - | - | - | - | ✅ Required |
224
- | L1 SWR | - | ✅ | - | ✅ | ✅ |
222
+ | L1 SWR (L1-only mode) | - | ✅ | - | ✅ | ✅ |
225
223
  | L1 Invalidation | - | - | - | ✅ | ✅ |
226
- | L1 Namespace Index | - | - | - | ✅ | ✅ |
227
224
  | Prometheus Metrics | - | - | - | ✅ | ✅ |
228
225
  | Tracing | - | ✅ | - | ✅ | ✅ |
229
226
  | Structured Logging | - | ✅ | - | ✅ | ✅ |
@@ -231,6 +228,8 @@ def get_user_profile(user_id: int):
231
228
 
232
229
  > 🔒 `@cache.secure` forces `integrity_checking=True` — it cannot be overridden.
233
230
  >
231
+ > **L1 SWR** (within-TTL background refresh) runs only in L1-only mode (`backend=None`) — with a backend configured it has no effect. `@cache.io` additionally ships past-TTL SWR via `stale_ttl` ([docs](docs/configuration.md#stale-while-revalidate-stale_ttl)).
232
+ >
234
233
  > **`@cache.io()`** mirrors `@cache.production` (full reliability + observability) but routes to the managed CachekitIO SaaS backend instead of Redis. **`@cache.local()`** is a separate in-process path backed by `ObjectCache` (raw object references, entry-count LRU, no serialization) — the reliability and encryption features listed above do not apply to it.
235
234
 
236
235
  <details>
@@ -291,6 +290,7 @@ def test_cached_function():
291
290
  - Connection pooling with thread affinity (+28% throughput)
292
291
  - Distributed locking prevents cache stampedes
293
292
  - Pluggable backend abstraction (Redis, CachekitIO, File, Memcached, custom)
293
+ - Untrusted-decode bounds: nesting depth and header-declared allocation are capped on every cache read (a forged entry is a bounded cache miss), verified against the protocol's shared [`decode-bounds.json`](https://github.com/cachekit-io/protocol/blob/2d56cce231e193141f09df9316f9afac17a1538e/test-vectors/decode-bounds.json) vectors
294
294
 
295
295
  > [!NOTE]
296
296
  > All reliability features are **enabled by default** with `@cache.production`. Use `@cache.minimal` to disable them for maximum throughput.
@@ -346,9 +346,15 @@ def get_patient_data(hospital_id: int):
346
346
  > [!CAUTION]
347
347
  > When handling PII, medical, or financial data, always use `@cache.secure` to enforce encryption.
348
348
 
349
+ **Zero-downtime key rotation**: promote a new `CACHEKIT_MASTER_KEY` and keep the
350
+ retiring key readable via `CACHEKIT_PREVIOUS_MASTER_KEYS` (comma-separated hex,
351
+ max 3 decrypt-only keys). Entries are selected by exact key fingerprint — never
352
+ trial decryption — and old entries age out via TTL, no cache flush required. See
353
+ [Zero-Knowledge Encryption](docs/features/zero-knowledge-encryption.md#key-rotation-pattern).
354
+
349
355
  cachekit employs comprehensive security tooling:
350
356
 
351
- - **Supply Chain Security**: cargo-deny for license compliance + RustSec scanning
357
+ - **Dependency Security**: cargo-deny for license compliance + cargo-audit for RustSec scanning
352
358
  - **Formal Verification**: Kani proves correctness of compression, checksums, encryption
353
359
  - **Runtime Analysis**: Miri + sanitizers for memory safety
354
360
  - **Fuzzing**: Coverage-guided testing with >80% code coverage
@@ -408,6 +414,15 @@ exposition setup.
408
414
  <details>
409
415
  <summary><strong>Thread Safety Details</strong></summary>
410
416
 
417
+ **Free-threaded CPython (3.14t):** the core suites run green on
418
+ free-threaded 3.14 with the GIL verified disabled (CI job
419
+ `test-freethreaded`), and the Rust extension declares free-threaded safety
420
+ (`gil_used = false`). Free-threaded wheels are **not yet published** and
421
+ free-threaded builds are not officially supported — blocked on upstream
422
+ wheels (orjson, hiredis; numpy/pandas/pyarrow for `[data]`). See
423
+ [measured performance results](docs/free-threading.md#measured-performance) and the
424
+ full concurrency audit: [docs/free-threading.md](docs/free-threading.md).
425
+
411
426
  **Per-Function Statistics:**
412
427
  - Statistics tracked per function identity (`module.qualname`), shared across all calls and across re-decorations of the same function
413
428
  - Thread-safe via RLock (all methods safe for concurrent access)
@@ -443,6 +458,7 @@ info = expensive_func.cache_info()
443
458
  | [Comparison Guide][comparison-url] | How cachekit compares to lru_cache, aiocache, cachetools |
444
459
  | [Getting Started][getting-started-url] | Progressive tutorial from basics to advanced |
445
460
  | [API Reference][api-reference-url] | Complete API documentation |
461
+ | [Skyline (live example)][skyline-url] | Canonical example project: this SDK ingests the Bluesky firehose and writes the analytics entries a TypeScript edge Worker serves live, on one shared interop namespace |
446
462
 
447
463
  ### Feature Deep Dives
448
464
 
@@ -453,6 +469,11 @@ info = expensive_func.cache_info()
453
469
  | [Distributed Locking][distributed-locking-url] | Cache stampede prevention |
454
470
  | [Prometheus Metrics][prometheus-url] | Built-in observability |
455
471
  | [Zero-Knowledge Encryption][encryption-url] | Client-side security |
472
+ | [Interop Mode][interop-url] | Cross-SDK cache sharing with cachekit-ts/rs |
473
+ | [L1 Invalidation & SWR][l1-invalidation-url] | Process-local invalidation, stale-while-revalidate |
474
+ | [Reference Caching][reference-caching-url] | `@cache.local()` for non-serializable objects |
475
+ | [Rust Serialization][rust-serialization-url] | ByteStorage layer: LZ4, xxHash3, AES-256-GCM |
476
+ | [SSRF Protection][ssrf-url] | URL allowlisting for the CachekitIO backend |
456
477
 
457
478
  ---
458
479
 
@@ -465,7 +486,7 @@ info = expensive_func.cache_info()
465
486
  CACHEKIT_REDIS_URL="redis://localhost:6379" # Primary (preferred)
466
487
  REDIS_URL="redis://localhost:6379" # Fallback
467
488
 
468
- # CachekitIO SaaS Backend (closed alpha — request access at cachekit.io)
489
+ # CachekitIO SaaS Backend (closed beta — request access at cachekit.io)
469
490
  CACHEKIT_API_KEY="your-api-key" # Required for @cache.io() # pragma: allowlist secret
470
491
  CACHEKIT_API_URL="https://api.cachekit.io" # Default SaaS endpoint
471
492
 
@@ -528,6 +549,7 @@ MIT License - see [LICENSE][license-file-url] for details.
528
549
  [uv-url]: https://github.com/astral-sh/uv
529
550
  [security-url]: SECURITY.md
530
551
  [comparison-url]: docs/comparison.md
552
+ [skyline-url]: https://github.com/cachekit-io/bluesky-thinking
531
553
  [getting-started-url]: docs/getting-started.md
532
554
  [api-reference-url]: docs/api-reference.md
533
555
  [serializer-guide-url]: docs/serializers/index.md
@@ -535,6 +557,11 @@ MIT License - see [LICENSE][license-file-url] for details.
535
557
  [distributed-locking-url]: docs/features/distributed-locking.md
536
558
  [prometheus-url]: docs/features/prometheus-metrics.md
537
559
  [encryption-url]: docs/features/zero-knowledge-encryption.md
560
+ [interop-url]: docs/features/interop-mode.md
561
+ [l1-invalidation-url]: docs/features/l1-invalidation.md
562
+ [reference-caching-url]: docs/features/reference-caching.md
563
+ [rust-serialization-url]: docs/features/rust-serialization.md
564
+ [ssrf-url]: docs/features/ssrf-protection.md
538
565
  [contributing-url]: CONTRIBUTING.md
539
566
  [license-file-url]: LICENSE
540
567
  [github-url]: https://github.com/cachekit-io/cachekit-py
@@ -4,7 +4,7 @@
4
4
 
5
5
  > **Python caching, batteries included**
6
6
 
7
- Production-ready caching for Python with intelligent reliability features and Rust-powered performance.
7
+ Backend-agnostic caching with intent-based decorators circuit breaker, distributed locking, Prometheus metrics, and optional zero-knowledge encryption on a Rust-powered core. Zero config to start, any backend when you scale.
8
8
 
9
9
  [![PyPI Version][pypi-badge]][pypi-url]
10
10
  [![Python Versions][python-badge]][pypi-url]
@@ -15,20 +15,16 @@ Production-ready caching for Python with intelligent reliability features and Ru
15
15
 
16
16
  ---
17
17
 
18
- > [!WARNING]
19
- > **Alpha Software** — cachekit is under active development. While we've been building and testing for ~6 months, the API is not yet stable and **breaking changes may occur** between releases. We're committed to making this library rock-solid, but we need your help!
20
- >
21
- > 🐛 **Found a bug?** Please [open an issue][issues-url] — even small ones help us improve.
22
- >
23
- > 💡 **Something feel off?** We want to hear about rough edges, confusing APIs, or missing features.
18
+ > [!NOTE]
19
+ > **Status: beta** — CacheKit is in closed beta ahead of 1.0. APIs are stabilising; minor breaking changes may still occur between 0.x releases.
24
20
  >
25
- > Your feedback directly shapes the path to 1.0. Cheers!
21
+ > 🐛 **Found a bug or a rough edge?** Please [open an issue][issues-url] — your feedback directly shapes the path to 1.0.
26
22
 
27
23
  ---
28
24
 
29
25
  ## Why cachekit?
30
26
 
31
- **Simple to use, production-ready out of the box.**
27
+ **Simple to use, works out of the box.**
32
28
 
33
29
  ```python
34
30
  from cachekit import cache
@@ -72,7 +68,7 @@ backend that fits your infrastructure — they're peers behind the same `@cache`
72
68
  | Backend | Best for | Select with |
73
69
  |---------|----------|-------------|
74
70
  | Redis | Self-hosted, full control | `REDIS_URL` / `CACHEKIT_REDIS_URL` |
75
- | CachekitIO | Managed, zero-ops (alpha) | `CACHEKIT_API_KEY` |
71
+ | CachekitIO | Managed, zero-ops (beta) | `CACHEKIT_API_KEY` |
76
72
  | Memcached | High-throughput, existing infra | `CACHEKIT_MEMCACHED_SERVERS` |
77
73
  | File / L1-only | Local dev, tests, no external deps | `CACHEKIT_FILE_CACHE_DIR` / `backend=None` |
78
74
 
@@ -95,7 +91,7 @@ def expensive_api_call(user_id: int):
95
91
  ### More Backends
96
92
 
97
93
  <details>
98
- <summary><strong>CachekitIO — Managed SaaS (Alpha)</strong></summary>
94
+ <summary><strong>CachekitIO — Managed SaaS (Beta)</strong></summary>
99
95
 
100
96
  ```python notest
101
97
  import os
@@ -109,7 +105,7 @@ def expensive_api_call(user_id: int):
109
105
  return fetch_user_data(user_id)
110
106
  ```
111
107
 
112
- *cachekit.io is in closed alpha — [request access](https://cachekit.io) to get started.*
108
+ *cachekit.io is in closed beta — [request access](https://cachekit.io) to get started.*
113
109
 
114
110
  </details>
115
111
 
@@ -135,9 +131,9 @@ def expensive_api_call(user_id: int):
135
131
 
136
132
  ---
137
133
 
138
- > **CachekitIO Cloud (Alpha)**
134
+ > **CachekitIO Cloud (Beta)**
139
135
  > Managed caching with zero infrastructure. L1+L2 caching, circuit breaker, and automatic failover — no Redis to manage.
140
- > *cachekit.io is in closed alpha — [request access](https://cachekit.io) to get started.*
136
+ > *cachekit.io is in closed beta — [request access](https://cachekit.io) to get started.*
141
137
 
142
138
  ---
143
139
 
@@ -168,9 +164,8 @@ def get_user_profile(user_id: int):
168
164
  | Backpressure | ✅ | ✅ | - | ✅ | ✅ |
169
165
  | Integrity Checking | - | ✅ | - | ✅ | ✅ 🔒 |
170
166
  | Encryption | - | - | - | - | ✅ Required |
171
- | L1 SWR | - | ✅ | - | ✅ | ✅ |
167
+ | L1 SWR (L1-only mode) | - | ✅ | - | ✅ | ✅ |
172
168
  | L1 Invalidation | - | - | - | ✅ | ✅ |
173
- | L1 Namespace Index | - | - | - | ✅ | ✅ |
174
169
  | Prometheus Metrics | - | - | - | ✅ | ✅ |
175
170
  | Tracing | - | ✅ | - | ✅ | ✅ |
176
171
  | Structured Logging | - | ✅ | - | ✅ | ✅ |
@@ -178,6 +173,8 @@ def get_user_profile(user_id: int):
178
173
 
179
174
  > 🔒 `@cache.secure` forces `integrity_checking=True` — it cannot be overridden.
180
175
  >
176
+ > **L1 SWR** (within-TTL background refresh) runs only in L1-only mode (`backend=None`) — with a backend configured it has no effect. `@cache.io` additionally ships past-TTL SWR via `stale_ttl` ([docs](docs/configuration.md#stale-while-revalidate-stale_ttl)).
177
+ >
181
178
  > **`@cache.io()`** mirrors `@cache.production` (full reliability + observability) but routes to the managed CachekitIO SaaS backend instead of Redis. **`@cache.local()`** is a separate in-process path backed by `ObjectCache` (raw object references, entry-count LRU, no serialization) — the reliability and encryption features listed above do not apply to it.
182
179
 
183
180
  <details>
@@ -238,6 +235,7 @@ def test_cached_function():
238
235
  - Connection pooling with thread affinity (+28% throughput)
239
236
  - Distributed locking prevents cache stampedes
240
237
  - Pluggable backend abstraction (Redis, CachekitIO, File, Memcached, custom)
238
+ - Untrusted-decode bounds: nesting depth and header-declared allocation are capped on every cache read (a forged entry is a bounded cache miss), verified against the protocol's shared [`decode-bounds.json`](https://github.com/cachekit-io/protocol/blob/2d56cce231e193141f09df9316f9afac17a1538e/test-vectors/decode-bounds.json) vectors
241
239
 
242
240
  > [!NOTE]
243
241
  > All reliability features are **enabled by default** with `@cache.production`. Use `@cache.minimal` to disable them for maximum throughput.
@@ -293,9 +291,15 @@ def get_patient_data(hospital_id: int):
293
291
  > [!CAUTION]
294
292
  > When handling PII, medical, or financial data, always use `@cache.secure` to enforce encryption.
295
293
 
294
+ **Zero-downtime key rotation**: promote a new `CACHEKIT_MASTER_KEY` and keep the
295
+ retiring key readable via `CACHEKIT_PREVIOUS_MASTER_KEYS` (comma-separated hex,
296
+ max 3 decrypt-only keys). Entries are selected by exact key fingerprint — never
297
+ trial decryption — and old entries age out via TTL, no cache flush required. See
298
+ [Zero-Knowledge Encryption](docs/features/zero-knowledge-encryption.md#key-rotation-pattern).
299
+
296
300
  cachekit employs comprehensive security tooling:
297
301
 
298
- - **Supply Chain Security**: cargo-deny for license compliance + RustSec scanning
302
+ - **Dependency Security**: cargo-deny for license compliance + cargo-audit for RustSec scanning
299
303
  - **Formal Verification**: Kani proves correctness of compression, checksums, encryption
300
304
  - **Runtime Analysis**: Miri + sanitizers for memory safety
301
305
  - **Fuzzing**: Coverage-guided testing with >80% code coverage
@@ -355,6 +359,15 @@ exposition setup.
355
359
  <details>
356
360
  <summary><strong>Thread Safety Details</strong></summary>
357
361
 
362
+ **Free-threaded CPython (3.14t):** the core suites run green on
363
+ free-threaded 3.14 with the GIL verified disabled (CI job
364
+ `test-freethreaded`), and the Rust extension declares free-threaded safety
365
+ (`gil_used = false`). Free-threaded wheels are **not yet published** and
366
+ free-threaded builds are not officially supported — blocked on upstream
367
+ wheels (orjson, hiredis; numpy/pandas/pyarrow for `[data]`). See
368
+ [measured performance results](docs/free-threading.md#measured-performance) and the
369
+ full concurrency audit: [docs/free-threading.md](docs/free-threading.md).
370
+
358
371
  **Per-Function Statistics:**
359
372
  - Statistics tracked per function identity (`module.qualname`), shared across all calls and across re-decorations of the same function
360
373
  - Thread-safe via RLock (all methods safe for concurrent access)
@@ -390,6 +403,7 @@ info = expensive_func.cache_info()
390
403
  | [Comparison Guide][comparison-url] | How cachekit compares to lru_cache, aiocache, cachetools |
391
404
  | [Getting Started][getting-started-url] | Progressive tutorial from basics to advanced |
392
405
  | [API Reference][api-reference-url] | Complete API documentation |
406
+ | [Skyline (live example)][skyline-url] | Canonical example project: this SDK ingests the Bluesky firehose and writes the analytics entries a TypeScript edge Worker serves live, on one shared interop namespace |
393
407
 
394
408
  ### Feature Deep Dives
395
409
 
@@ -400,6 +414,11 @@ info = expensive_func.cache_info()
400
414
  | [Distributed Locking][distributed-locking-url] | Cache stampede prevention |
401
415
  | [Prometheus Metrics][prometheus-url] | Built-in observability |
402
416
  | [Zero-Knowledge Encryption][encryption-url] | Client-side security |
417
+ | [Interop Mode][interop-url] | Cross-SDK cache sharing with cachekit-ts/rs |
418
+ | [L1 Invalidation & SWR][l1-invalidation-url] | Process-local invalidation, stale-while-revalidate |
419
+ | [Reference Caching][reference-caching-url] | `@cache.local()` for non-serializable objects |
420
+ | [Rust Serialization][rust-serialization-url] | ByteStorage layer: LZ4, xxHash3, AES-256-GCM |
421
+ | [SSRF Protection][ssrf-url] | URL allowlisting for the CachekitIO backend |
403
422
 
404
423
  ---
405
424
 
@@ -412,7 +431,7 @@ info = expensive_func.cache_info()
412
431
  CACHEKIT_REDIS_URL="redis://localhost:6379" # Primary (preferred)
413
432
  REDIS_URL="redis://localhost:6379" # Fallback
414
433
 
415
- # CachekitIO SaaS Backend (closed alpha — request access at cachekit.io)
434
+ # CachekitIO SaaS Backend (closed beta — request access at cachekit.io)
416
435
  CACHEKIT_API_KEY="your-api-key" # Required for @cache.io() # pragma: allowlist secret
417
436
  CACHEKIT_API_URL="https://api.cachekit.io" # Default SaaS endpoint
418
437
 
@@ -475,6 +494,7 @@ MIT License - see [LICENSE][license-file-url] for details.
475
494
  [uv-url]: https://github.com/astral-sh/uv
476
495
  [security-url]: SECURITY.md
477
496
  [comparison-url]: docs/comparison.md
497
+ [skyline-url]: https://github.com/cachekit-io/bluesky-thinking
478
498
  [getting-started-url]: docs/getting-started.md
479
499
  [api-reference-url]: docs/api-reference.md
480
500
  [serializer-guide-url]: docs/serializers/index.md
@@ -482,6 +502,11 @@ MIT License - see [LICENSE][license-file-url] for details.
482
502
  [distributed-locking-url]: docs/features/distributed-locking.md
483
503
  [prometheus-url]: docs/features/prometheus-metrics.md
484
504
  [encryption-url]: docs/features/zero-knowledge-encryption.md
505
+ [interop-url]: docs/features/interop-mode.md
506
+ [l1-invalidation-url]: docs/features/l1-invalidation.md
507
+ [reference-caching-url]: docs/features/reference-caching.md
508
+ [rust-serialization-url]: docs/features/rust-serialization.md
509
+ [ssrf-url]: docs/features/ssrf-protection.md
485
510
  [contributing-url]: CONTRIBUTING.md
486
511
  [license-file-url]: LICENSE
487
512
  [github-url]: https://github.com/cachekit-io/cachekit-py
@@ -4,8 +4,8 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "cachekit"
7
- version = "0.17.1"
8
- description = "Production-ready Redis caching for Python with intelligent reliability features and Rust-powered performance"
7
+ version = "0.19.0"
8
+ description = "Backend-agnostic caching for Python — intent-based decorators with circuit breaker, distributed locking, Prometheus metrics, and optional zero-knowledge AES-256-GCM encryption, on a Rust-powered core. Zero-config L1 in-memory; scales to Redis, Memcached, File, or CachekitIO."
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
11
11
  authors = [
@@ -31,7 +31,7 @@ keywords = [
31
31
  "distributed-locking",
32
32
  ]
33
33
  classifiers = [
34
- "Development Status :: 3 - Alpha",
34
+ "Development Status :: 4 - Beta",
35
35
  "Intended Audience :: Developers",
36
36
  "License :: OSI Approved :: MIT License",
37
37
  "Operating System :: OS Independent",
@@ -56,7 +56,7 @@ dependencies = [
56
56
  "redis[hiredis]>=4.0.0",
57
57
  # Configuration and validation
58
58
  "pydantic>=2.0.0",
59
- "pydantic-settings>=2.0.0",
59
+ "pydantic-settings>=2.7.0", # NoDecode (previous_master_keys env parsing) first shipped in 2.7.0
60
60
  # Monitoring and observability
61
61
  "prometheus-client>=0.22.1",
62
62
  "psutil>=7.0.0",
@@ -66,6 +66,22 @@ dependencies = [
66
66
  "xxhash>=3.5.0",
67
67
  # HTTP client for SaaS backend (cachekit.io)
68
68
  "httpx[http2]>=0.28.1",
69
+ # anyio is transitive via the mandatory httpx dependency above, so it ships
70
+ # to EVERY install, not just dev. Declared here rather than as a
71
+ # [tool.uv] constraint because that table is uv-local: it never reaches
72
+ # requires-dist, so `pip install cachekit` would ignore it. 4.14.2 fixes
73
+ # GHSA-82r6-8w77-94w6 / CVE-2026-63374 (IDNA-2003 hostname encoding lets a
74
+ # hijacked connection to an internationalised domain pass TLS certificate
75
+ # validation; CVSS 9.3), GHSA-5p39-cfhj-2xmp / CVE-2026-64847 (undrained
76
+ # process-pool stderr pipe deadlocks the worker) and GHSA-3w57-8xmc-8v26 /
77
+ # CVE-2026-63349 (extra_groups ignored, parent supplementary groups kept).
78
+ "anyio>=4.14.2",
79
+ # h2 arrives via the http2 extra on that same mandatory httpx dependency, so
80
+ # it ships to every install too, and was declared as a [tool.uv] constraint
81
+ # with the same no-op effect. 4.4.1 fixes GHSA-6hr6-w5qg-qmwg (duplicate Host
82
+ # headers forwarded across an HTTP/2 -> HTTP/1.1 downgrade — a request
83
+ # smuggling primitive).
84
+ "h2>=4.4.1",
69
85
  ]
70
86
 
71
87
  [project.optional-dependencies]
@@ -150,7 +166,6 @@ asyncio_default_fixture_loop_scope = "function"
150
166
  addopts = [
151
167
  "--strict-markers",
152
168
  "--verbose",
153
- "--basetemp=/tmp/pytest",
154
169
  "--doctest-modules", # Validate docstring examples
155
170
  "--doctest-continue-on-failure", # Report all doctest failures, not just first
156
171
  "--markdown-docs", # Validate markdown documentation examples
@@ -200,8 +215,12 @@ exclude_lines = [
200
215
  ]
201
216
 
202
217
  [dependency-groups]
203
- dev = [
204
- # Testing
218
+ # Core test toolchain — everything tests/unit + tests/critical need on ANY
219
+ # interpreter, including free-threaded CPython: the free-threaded CI lane
220
+ # installs ONLY this group (LAB-511). A dep may live here only if it ships
221
+ # free-threaded wheels or builds cleanly from source on 3.14t; deps that
222
+ # don't (orjson, numpy, pandas, pyarrow) stay in dev below.
223
+ test = [
205
224
  "fakeredis>=2.21.0",
206
225
  "pytest>=7.0.0",
207
226
  "pytest-asyncio>=0.21.0",
@@ -210,6 +229,19 @@ dev = [
210
229
  "pytest-markdown-docs>=0.6.0",
211
230
  "pytest-redis>=3.0.0",
212
231
  "pymemcache>=4.0.0",
232
+ # Utilities
233
+ "faker>=20.0.0",
234
+ "httpx>=0.28.1",
235
+ "hypothesis>=6.0.0",
236
+ "requests>=2.33.0; python_version >= '3.10'",
237
+ "psutil>=5.9.0",
238
+ "python-dotenv>=1.0.0",
239
+ "pyyaml>=6.0.3",
240
+ "pytest-xdist>=3.8.0",
241
+ "time-machine>=2.19.0",
242
+ ]
243
+ dev = [
244
+ { include-group = "test" },
213
245
  # Competitive comparison suite (tests/competitive/ benchmarks cachekit vs these)
214
246
  "cachetools>=5.3.0",
215
247
  "aiocache>=0.12.0",
@@ -218,14 +250,7 @@ dev = [
218
250
  "ruff>=0.6.0",
219
251
  # Utilities
220
252
  "bashlex>=0.18",
221
- "faker>=20.0.0",
222
- "httpx>=0.28.1",
223
- "hypothesis>=6.0.0",
224
253
  "pip-audit>=2.7.0",
225
- "requests>=2.33.0; python_version >= '3.10'",
226
- "psutil>=5.9.0",
227
- "python-dotenv>=1.0.0",
228
- "pyyaml>=6.0.3",
229
254
  # Data science support (for testing AutoSerializer with numpy/pandas)
230
255
  "numpy>=2.0.2",
231
256
  "pandas>=1.3.0",
@@ -233,15 +258,17 @@ dev = [
233
258
  # OrjsonSerializer support — now the [json] optional extra; kept here so the
234
259
  # orjson tests, doctests, and markdown-docs still resolve it in dev/CI.
235
260
  "orjson>=3.9.0",
236
- "pytest-xdist>=3.8.0",
237
- "time-machine>=2.19.0",
238
261
  ]
239
262
  # Linux CI only - Atheris requires libFuzzer (not available on macOS without building LLVM)
240
263
  fuzz = [
241
264
  "atheris>=2.3.0",
242
265
  ]
243
266
 
244
- # Override vulnerable transitive dependencies
267
+ # Override vulnerable DEV-ONLY transitive dependencies.
268
+ # This table is uv-local: it constrains resolution of this repo's lockfile and
269
+ # never reaches requires-dist, so a floor placed here does NOT protect anyone who
270
+ # runs `pip install cachekit`. A transitive that reaches users belongs in
271
+ # [project] dependencies instead — see the anyio/h2 entries above.
245
272
  [tool.uv]
246
273
  constraint-dependencies = [
247
274
  "urllib3>=2.7.0",
@@ -249,6 +276,7 @@ constraint-dependencies = [
249
276
  "werkzeug>=3.1.4",
250
277
  # pip is a dev-only transitive dep (pip-audit -> pip-api -> pip). 26.1.2 fixes
251
278
  # PYSEC-2026-196 (entry-point path traversal), GHSA-58qw-9mgm-455v (tar/zip
252
- # confusion) and GHSA-jp4c-xjxw-mgf9 (self-update import ordering).
253
- "pip>=26.1.2",
279
+ # confusion) and GHSA-jp4c-xjxw-mgf9 (self-update import ordering); 26.2 fixes
280
+ # PYSEC-2026-3721 (doubly-encoded index URLs install to arbitrary paths).
281
+ "pip>=26.2",
254
282
  ]
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "cachekit-rs"
3
- version = "0.17.1"
3
+ version = "0.19.0"
4
4
  edition = "2021"
5
5
  authors = ["cachekit Contributors"]
6
6
  description = "High-performance storage engine for caching with compression and encryption"
@@ -20,11 +20,15 @@ crate-type = ["cdylib", "rlib"]
20
20
 
21
21
  [dependencies]
22
22
  # Compression, checksums, encryption (https://crates.io/crates/cachekit-core)
23
- cachekit-core = { version = "0.4.0", features = ["compression", "checksum", "messagepack", "encryption"] }
23
+ cachekit-core = { version = "0.5.0", features = ["compression", "checksum", "messagepack", "encryption"] }
24
24
 
25
25
  # Python integration - optional for Rust-only builds
26
26
  pyo3 = { workspace = true, optional = true }
27
27
 
28
+ # Wipe the PyO3-side copies of master key material on drop. Only used behind
29
+ # the encryption feature, so gated the same way pyo3 is.
30
+ zeroize = { version = "1", optional = true }
31
+
28
32
  # Feature flags
29
33
  [features]
30
34
  default = ["python", "compression", "checksum", "messagepack", "encryption"]
@@ -36,7 +40,7 @@ python = ["dep:pyo3"]
36
40
  compression = []
37
41
  checksum = []
38
42
  messagepack = []
39
- encryption = []
43
+ encryption = ["dep:zeroize"]
40
44
 
41
45
  [dev-dependencies]
42
46
  criterion = { version = "0.5", features = ["html_reports"] }