Key Verification & Caching
When receiving federated actions, the server must verify the JWT signature using the issuer’s public key. This involves a 3-tier caching strategy to balance security with performance.
3-Tier Caching Architecture
graph TD
REQ["Verification Request"] --> T1
T1["Tier 1: In-Memory Failure Cache<br/>Prevents repeated requests to unreachable instances<br/>TTL 5 minutes for network errors, 1 hour for persistent errors<br/>LRU eviction when capacity exceeded"]
T1 -->|"cache miss or expired"| T2["Tier 2: SQLite Key Cache<br/>Persistent cache of successful key fetches<br/>Keyed by issuer and key_id<br/>Stores public key plus expiration timestamp<br/>If valid and not expired, verify signature immediately"]
T2 -->|"cache miss or expired"| T3["Tier 3: HTTP Fetch from Remote<br/>GET https://cl-o.ISSUER/api/me<br/>Find matching key by key_id<br/>On success, cache in SQLite and clear failure cache<br/>On failure, record in failure cache"]
Failure Types and TTLs
| Error Type | TTL | Reason |
|---|---|---|
| Network timeout | 5 minutes | May recover quickly |
| Connection refused | 5 minutes | Server may restart |
| 404 Not Found | 1 hour | Key doesn’t exist |
| 403 Forbidden | 1 hour | Permission denied |
| Parse error | 1 hour | Invalid response format |
Verification Flow
flowchart TD
A["Decode JWT without verifying<br/>to extract issuer and key_id"] --> B{"Failure cache hit<br/>and not expired?"}
B -->|"yes"| E1["Error: CachedFailure"]
B -->|"no"| C{"SQLite key cache hit<br/>and not expired?"}
C -->|"yes"| V["verify_signature with cached public key"]
C -->|"no"| F["GET https://cl-o.ISSUER/api/me"]
F -->|"request failed"| E2["Record failure<br/>Error: KeyFetchFailed"]
F -->|"key_id not in response"| E3["Record failure<br/>Error: KeyNotFound"]
F -->|"key found"| S["Store key in SQLite cache<br/>Clear failure cache"]
S --> V
See Also
- Action Delivery - How actions are received
- Security - Trust model and spam prevention
- Identity System - Public key management