Conversation
…uard, spec alignment ## Security Fixes ### B-01: Policy Decision Cache — cross-request identity collision - Expand _PolicyDecisionCache key from (entry_id, policies) to (user, agent, task, policies, context_items) — decisions are now strictly isolated per identity tuple - Add KEST_POLICY_CACHE_TTL env var (default 5.0s); set to 0 to disable - Expose invalidate_policy_cache() publicly for revocation use cases - Tests: policy_decision_cache_test.py (5 tests: key isolation, TTL=0, invalidation) ### R-02: Baggage purity — decouple _get_baggage() from global lab state - _get_baggage() now reads exclusively from OTel baggage context - _LAB_BAGGAGE_STORE used only by KestHttpxInterceptor for outbound injection - Tests: decorators_baggage_test.py (4 tests) ### R-03: JWT verification guard in KestIdentityMiddleware - Raise RuntimeError on first request if jwks_uri=None and KEST_INSECURE_NO_VERIFY is not set — eliminates silent unverified decode - Tests: ext_test.py (3 tests: raises without jwks, accepts insecure flag, spec key names) ### D-01: Baggage keys aligned with SPEC-v0.3.0 §8.4 - Rename kest.principal_user → kest.user - Rename kest.workload_agent → kest.agent - kest.task replaces inconsistent kest.scope usage ### D-02: Three-tier baggage propagation formalised (kest.passport_z) - Inline → Compressed Inline (kest.passport_z, zlib+base64url) → Claim Check; pushes claim-check threshold from hop ~8 to hop ~52 ## Architecture Research (§5 LEARNINGS.md) ### A-01: asyncio.to_thread unbounded pool risk documented - Improvement proposal: bounded ThreadPoolExecutor via run_in_executor - Tracked: #10 ### A-02: Rust GIL cliff — root cause precisely identified - py.allow_threads releases GIL for canonicalization but Python::with_gil re-acquires for sign_payload callback → 94% cliff - Fix path: port LocalEd25519Provider to ed25519-dalek in Rust (A-02-A) - Tracked: #11 ### A-03: Passport cache inefficiencies documented with fix options - O(n) list-snapshot comparison → O(1) version counter (A-03-I) - Per-read taint accumulation → incremental on add_signature (A-03-II) - __slots__ = True for high-throughput services (A-03-III) - Tracked: #12 ## Spec Updates - SPEC-v0.3.0: add F-CP-07/08 (kest.passport_z normative), update §8.6 three-tier priority table - spec/learnings/v0.3.0/LEARNINGS.md: full §5 expansion with improvement proposals, issue cross-references, ARCHIVES.md for resolved findings ## Documentation - README.md: Production Notes section (KEST_BACKEND, cache TTL, JWT guard, baggage key reference table with rolling-upgrade warning) - CHANGELOG.md: Security Hardening (2026-04-11) section under [0.3.0] with B-01/R-02/R-03/D-01/D-02 details; Known Limitations table → #10/11/12 - website/content/developer/04_middleware.md: three-tier baggage table, updated JWT guard note, updated sequence diagram - website/content/developer/08_testing.md: test count 124→136, security hardening test table, T-09 testing pattern note - website/content/reference/02_models.md: fix duplicate para, BaggageManager three-tier table, integer ORIGIN_TRUST_MAP, Passport property table - website/content/reference/04_context.md: extended baggage key table with Set-by column, all three passport tiers, hardening rename warning ## Code Style - ruff-format applied across decorators.py, _core_py.py, ext.py, models.py, identity_test.py, all bench scripts, and test files
Contributor
🔍 Site Preview Deployed
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
This PR delivers two related bodies of work on top of the initial v0.3.0 release:
🔐 Security Fixes
B-01 · Policy Decision Cache — cross-request identity collision
The
_PolicyDecisionCachekey was(entry_id, frozenset(policies)). A policy decision for one identity (user/agent/task) could be served to a different identity within the same TTL window.(user, agent, task, frozenset(policies), frozenset(context))KEST_POLICY_CACHE_TTL(float, default5.0; set0to disable)invalidate_policy_cache()for revocation use casespolicy_decision_cache_test.py— 5 testsR-02 · Baggage purity — decouple
_get_baggage()from global lab state_get_baggage()had a secondary fallback to a module-level_LAB_BAGGAGE_STOREdict, creating a data race under async concurrency (no per-request isolation)._get_baggage()reads exclusively frombaggage.get_baggage()(OTel context)decorators_baggage_test.py— 4 testsR-03 · JWT verification guard in
KestIdentityMiddlewareWithout a
jwks_uri, the middleware silently decoded JWTs withverify=False, making signature verification optional.RuntimeErrorraised on first request unlessKEST_INSECURE_NO_VERIFY=trueis explicitly setext_test.py— 3 tests (raises without jwks, insecure flag accepted, spec key names)D-01 · Baggage key naming aligned with SPEC-v0.3.0 §8.4
kest.principal_userkest.userkest.workload_agentkest.agentkest.scope(inconsistent)kest.taskD-02 · Three-tier baggage propagation formalised (
kest.passport_z)Compressed inline baggage was an undocumented implementation extension. Now normative in SPEC-v0.3.0 §8.6:
kest.passportkest.passport_zkest.claim_checkA 10-hop chain (~5 KB raw) compresses to ~1.5 KB, pushing the claim-check threshold from hop ~8 to hop ~52.
🔬 Architecture Research (§5 LEARNINGS.md)
A-01 ·
asyncio.to_thread— unbounded pool riskasync_wrapperusesasyncio.to_threadwhich spawns threads without bound. Under spike load this can exhaust OS thread limits.ThreadPoolExecutorvialoop.run_in_executor(pool, ...)A-02 · Rust backend GIL cliff — root cause identified
The Rust
sign_entrydoespy.allow_threadsfor canonicalization but immediately callsPython::with_gilforsign_payload(Python callback). Under 8-thread contention this causes ~94% throughput degradation.LocalEd25519Providertoed25519-dalekin Rust → singleallow_threadsblock, zero re-acquisitionsKEST_BACKEND=pythonin production until perf(kest-core/rust): eliminate GIL re-acquisition in sign_entry by porting LocalEd25519Provider to Rust (A-02-A) #11 is resolvedA-03 · Passport cache — algorithmic inefficiencies
Three compounding issues: O(n) list-snapshot comparison on every property read, per-read taint accumulation, no
__slots__.add_signature(A-03-II),slots=True(A-03-III)📝 Documentation
README.mdCHANGELOG.mdwebsite/content/developer/04_middleware.mdwebsite/content/developer/08_testing.mdwebsite/content/reference/02_models.mdORIGIN_TRUST_MAP,Passportproperty tablewebsite/content/reference/04_context.mdspec/learnings/v0.3.0/LEARNINGS.mdspec/learnings/v0.3.0/ARCHIVES.md✅ Test Coverage
policy_decision_cache_test.pydecorators_baggage_test.pyext_test.py(additions)Total unit tests: 124 → 136 ✅ All passing.
🔗 Related Issues
ed25519-dalek(A-02-A)