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

┌─────────────────────────────────────────────────────────────────┐
│                     Verification Request                        │
└───────────────────────────────┬─────────────────────────────────┘
                                │
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│  Tier 1: In-Memory Failure Cache                                │
│  ├─ Purpose: Prevent repeated requests to unreachable instances │
│  ├─ TTL (network errors): 5 minutes                             │
│  ├─ TTL (persistent errors): 1 hour                             │
│  └─ LRU eviction when capacity exceeded                         │
└───────────────────────────────┬─────────────────────────────────┘
                                │ (cache miss or expired)
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│  Tier 2: SQLite Key Cache                                       │
│  ├─ Purpose: Persistent cache of successful key fetches         │
│  ├─ Key: (issuer, key_id)                                       │
│  ├─ Stores: public key + expiration timestamp                   │
│  └─ If valid & not expired: verify signature immediately        │
└───────────────────────────────┬─────────────────────────────────┘
                                │ (cache miss or expired)
                                ▼
┌─────────────────────────────────────────────────────────────────┐
│  Tier 3: HTTP Fetch from Remote                                 │
│  ├─ Endpoint: GET https://cl-o.{issuer}/api/me                  │
│  ├─ Find matching key by key_id                                 │
│  ├─ On success: cache in SQLite, clear failure cache            │
│  └─ 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

verify_action_token(token):
    1. Decode JWT without verifying (extract issuer, key_id)

    2. Check failure cache:
       if failure_cache.has(issuer, key_id) and not expired:
           return Error(CachedFailure)

    3. Check SQLite cache:
       if key_cache.has(issuer, key_id):
           key = key_cache.get(issuer, key_id)
           if key.expires_at > now:
               return verify_signature(token, key.public_key)

    4. Fetch from remote:
       response = HTTP GET https://cl-o.{issuer}/api/me
       if error:
           failure_cache.record(issuer, key_id, error_type)
           return Error(KeyFetchFailed)

       public_key = find_key_by_id(response.keys, key_id)
       if not found:
           failure_cache.record(issuer, key_id, NotFound)
           return Error(KeyNotFound)

       key_cache.store(issuer, key_id, public_key)
       failure_cache.clear(issuer, key_id)

       return verify_signature(token, public_key)

See Also