Claude Agent SDK Session Persistence: External Store Design
Local session files are convenient for one developer machine, but autoscaled workers and serverless functions need an explicit persistence boundary. The key design questions are what state is durable, how a session is keyed, how concurrent appends are ordered, and how credentials and retention are handled.
This article presents an external-store design pattern for session transcripts. It is intentionally version-scoped: verify the exact @anthropic-ai/claude-agent-sdk release and its supported session APIs before treating any interface below as official. The examples compare S3, Redis, and PostgreSQL storage models and show how to test a persistence adapter. For environment security guidelines, see Running Claude Code Safely and Claude Code Security Auditing.
Define the persistence contract before choosing a backend
An external transcript store needs a stable key, ordered append, load, retention, and concurrency behavior. The following is illustrative application code—not a claim that every SDK release exports this exact interface:
1type SessionKey = { projectKey: string; sessionId: string };2type TranscriptEntry = { id: string; createdAt: string; payload: unknown };3 4type TranscriptStore = {5 append(key: SessionKey, entries: TranscriptEntry[]): Promise<void>;6 load(key: SessionKey): Promise<TranscriptEntry[]>;7};A project identifier should be stable across workers without exposing a raw filesystem path. A session identifier must be unguessable, and every append needs an idempotency key or sequence number so retries cannot duplicate transcript entries. Validate the real SDK contract separately before wiring this abstraction into query().
Quick reference
- Keep tenant or user ownership in the key and authorization check, not only in application memory.
- Version the serialized entry schema so old sessions remain readable after deployments.
- Reject malformed or out-of-order entries before they become context for a new run.
Remember this
Persistence is a contract: key ownership, ordering, retries, schema version, and retention must be explicit.
Compare S3, Redis, and PostgreSQL by failure behavior
These are implementation patterns, not provider-supplied adapters. S3 works well for append-as-objects and long retention, but listing and ordering parts adds complexity. Redis supports fast list operations, but memory pressure, persistence configuration, and eviction policy become part of correctness. PostgreSQL makes ordering, uniqueness, and transactional writes explicit, but transcript growth requires indexing and retention jobs.
Choose based on the recovery requirement rather than a universal speed ranking. A transcript that must survive a worker restart needs durable writes and a replay test; a short-lived development session may only need an in-memory implementation.
| Backend | Useful model | Main operational risk |
|---|---|---|
| S3 | Versioned objects plus lifecycle policy | List/order/replay complexity |
| Redis | List or stream plus idempotency key | Memory, persistence, and eviction settings |
| PostgreSQL | Rows with unique sequence and JSONB payload | Indexes, connection limits, and retention jobs |
Quick reference
- S3 is a good fit when retention and replay matter more than interactive append latency.
- Redis requires explicit persistence and eviction settings before it can be treated as durable state.
- PostgreSQL makes uniqueness and transactional ordering easy to test, but it is not free storage.
Remember this
Select the backend whose failure and recovery behavior matches the session’s durability requirement.
Choose synchronous or asynchronous persistence deliberately
A worker can write locally and mirror asynchronously, write to the external store before acknowledging progress, or use a queue between execution and persistence. Asynchronous mirroring preserves responsiveness but can lose the latest entries; synchronous persistence improves durability but makes storage outages visible in the agent path.
A resume flow should load the transcript, verify its schema and ordering, establish credentials separately, and record a new append checkpoint. Do not assume that an SDK automatically creates or cleans a temporary configuration directory unless the versioned documentation says so.
Quick reference
- Retries need bounded backoff, idempotency, and an observable dead-letter path.
- Resume workers should validate schema version, ordering, tenant ownership, and retention before loading context.
- Document whether local checkpoints and external persistence are authoritative or merely a cache.
Remember this
Durability is a product decision: synchronous writes expose outages early, while asynchronous writes trade durability for responsiveness.
Test retries, isolation, and retention before production
Build a local contract suite for ordering, retries, duplicate delivery, malformed entries, tenant isolation, and deletion. A successful happy-path resume proves very little if two workers can append the same turn or a deleted tenant can still load it.
Retention is part of the data contract. Define deletion, export, legal-hold, and encryption behavior for S3 lifecycle rules, Redis TTLs, or scheduled SQL cleanup. Record failures with enough metadata to replay safely without logging secrets or full prompts.
Quick reference
- Use an idempotency key or append sequence enforced by the storage backend.
- Test a network timeout after the backend commits but before the worker receives the response.
- Keep transcript access auditable and separate credentials from session payloads.
Remember this
The adapter is production-ready only after duplicate delivery, partial failure, and deletion tests pass.
Hands-on Practice: Prove resume and duplicate-write behavior
Implement the small TranscriptStore interface from the first section with an in-memory map. Append two entries, load them in order, then retry the second append with the same entry ID. Your store should keep one copy.
Add a schema-version field and reject an entry with an unsupported version. This practice proves ordered session resumption and idempotency under retry conditions.
Testing Durability Under Retries
The practice exercise validates two guarantees that matter in production: first, that entries are stored in append order and loaded in that same order (proving session replay is safe), and second, that retrying an append with the same idempotency key does not duplicate the entry. These constraints seem obvious in theory, but in distributed systems they require explicit testing. Start with the happy path (two appends, one load), then deliberately introduce failure scenarios: retry the same append twice, attempt to load with an invalid key, append an entry with an unsupported schema version. Each test should either pass cleanly (idempotent append, ordered load) or fail with a clear error (invalid schema), never silently succeeding or corrupting state.
Quick reference
- Expected success: load returns the two entries in their original order.
- Intentional failure: replay the second append or corrupt its schema version.
- Recovery: enforce idempotency, return a clear validation error, and rerun the test.
Remember this
Pass when ordered resume works, duplicate delivery is harmless, and malformed entries fail before loading.
Key takeaway
External persistence is an architecture boundary, not automatically a built-in SDK guarantee. Verify the installed SDK version, define ownership and recovery semantics, and test the adapter under retries before moving transcripts to S3, Redis, or PostgreSQL.
Polo Khan
Lead Author & Systems ArchitectSoftware engineer and distributed systems architect specializing in backend scalability, cloud-native infrastructure, databases, and AI engineering workflows. Author and maintainer of Core Concept Learning.
Related Articles
Explore this topic