Hello, I'm Shrijith Venkatramana, and I'm building LiveReview — a blast-radius aware AI code review built for your business-critical systems. Star us to help devs discover the project, give it a try, and share your feedback to help improve the product.
Agent memory sounds like an LLM problem until you actually deploy an agent.
A useful agent needs to retain things such as:
project conventions
previous decisions
tool results
failed approaches
user preferences
investigation history
Now put that agent inside a temporary CI runner or Lambda or sandbox.
Where does all of that state live?
A file works well until you need the agent to move to another machine. A remote database solves portability, but introduces a network dependency for every read and write.
chDB Durable occupies the space between the two.
It keeps the database embedded and local, while using object storage as the durable backing layer.
This becomes common as agents move between laptops, CI runners, sandboxes, containers, and short-lived cloud machines. A local agent can accumulate weeks of useful state: project conventions, user preferences, tool failures, corrected assumptions, previous decisions, and the evidence behind those decisions.
A SQLite file can preserve some of this.
A remote Postgres or ClickHouse server can preserve much more.
But there is a useful middle ground:
Keep the database local for fast reads, while making its state durable in object storage.
That is the idea behind the new chDB Durable Layer.
1. Agent memory is becoming a database problem
A useful way to think about an agent is as a loop:
observe
-> reason
-> act
-> observe
-> reason
-> act
-> ...
Memory sits across this loop.
The simplest implementation is often a list of messages:
messages = [
"User prefers uv over Poetry",
"The checkout service runs in eu-west-1",
"Do not modify migrations during deployment",
]
Eventually that becomes insufficient.
Suppose an agent originally learns:
Deploy checkout-api to eu-west-1.
Later, someone changes the infrastructure:
Deploy checkout-api to eu-central-1.
What should the agent store?
Just the new answer?
Or also:
old belief
new belief
who changed it
when it changed
why it changed
what evidence supported it
which agent used the old belief
This is where the problem starts looking much more like a database than a prompt.
The research literature has been moving in this direction for several years. MemGPT, for example, explicitly treats an LLM agent as a system with multiple memory tiers and a mechanism for moving information between them. The underlying idea is familiar from operating systems: fast working memory is valuable, but larger persistent state must exist somewhere else.
Agent memory is therefore partly a data placement problem.
The interesting question becomes:
Where should the agent's durable state live?
2. The SQLite-to-server spectrum has a missing middle
There are several obvious answers.
Local SQLite
For a small agent, SQLite is excellent.
It is embedded, transactional, mature, and requires no server.
An agent can checkpoint its state:
agent process
|
v
SQLite file
The problem appears when memory becomes historical and analytical.
You start wanting queries such as:
SELECT project, count()
FROM tool_events
GROUP BY project;
Or:
SELECT *
FROM memory_history
WHERE memory_id = 'deployment-region'
ORDER BY version;
Or:
SELECT model, sum(tokens), sum(cost)
FROM events
WHERE created_at >= today() - 7
GROUP BY model;
That is a different workload from "save my current checkpoint."
SQLite plus replication
You can replicate SQLite's WAL to object storage.
That solves the single-disk durability problem.
But now there is another component in the deployment:
agent
|
SQLite
|
replication process
|
object storage
There is more operational machinery, and the underlying database is still fundamentally a transactional row store.
Remote database
The other solution is to put memory into Postgres, pgvector, ClickHouse, or a managed memory platform.
This solves portability and shared access.
The architecture becomes:
agent
|
network
|
database server
Now every recall is potentially a remote operation.
For a conventional application, this is often perfectly reasonable.
For an agent, the distinction matters because one user request can generate many sequential tool calls.
If an agent makes 10 data-access operations and each introduces 50 ms of network latency:
10 * 50 ms = 500 ms
Half a second appears before counting model inference.
And that is the optimistic case. Retries, connection failures, authentication, network jitter, and service saturation add another dimension.
Embedded analytical database
Now consider chDB.
It puts the ClickHouse query engine inside the application process.
agent process
|
chDB
|
local database
This gives the agent SQL, columnar storage, analytical queries, and local execution without requiring a database server.
But one problem remains:
The database is still attached to the machine.
This was the problem the chDB Durable Layer was designed to address.
3. The idea: local working copy, durable object
The architecture is simple enough to draw on a whiteboard:
object storage
authoritative state
|
flush / checkpoint
|
v
+---------------------------------------+
| Agent process |
| |
| chDB + local MergeTree |
| |
| recall -> local SQL query |
| writes -> local WAL buffer |
| |
+---------------------------------------+
The crucial distinction is between the working copy and the authoritative copy.
The agent queries its local chDB database.
Object storage holds the durable state.
When the agent needs to publish changes, it calls:
obj.flush()
When it wants to create a new base snapshot and shorten future recovery:
obj.checkpoint()
The result is an interesting deployment shape:
No database server
No persistent volume requirement
No replication sidecar
No network call for every recall
The durable object has an identity.
For example:
s3://my-agent-state/agent-memory
└── acme-checkout-api
The namespace is the storage root.
The object is one complete chDB database.
That object can later be opened on another machine.
This is particularly useful for agents because their compute environment is increasingly disposable.
Imagine:
Laptop
|
| develop
v
CI runner
|
| test
v
sandbox
|
| reproduce bug
v
another machine
The runtime can disappear.
The agent's accumulated state does not have to.
4. What actually happens during durability?
The interesting engineering is underneath the small API.
There are four concepts to keep in your head.
execute(): mutate locally
An operation such as:
obj.execute(
"INSERT INTO memories VALUES (...)"
)
changes the local working database.
The mutation enters the WAL buffer.
It has not necessarily become durable yet.
flush(): make a promise real
obj.flush()
is the durability boundary.
When it returns, the writes covered by that flush have reached object storage.
That gives application code an important semantic rule:
execute()
= "I changed my local memory."
flush()
= "I promise this memory survived the machine."
For an agent API, this distinction matters.
Consider a tool:
remember("The deployment region is eu-central-1")
Returning success immediately after execute() means the tool may claim to have remembered something that disappears when the sandbox disappears.
Returning success only after flush() gives the tool an actual durability guarantee.
checkpoint(): create a new base
Between checkpoints, the durable state consists conceptually of:
base snapshot
+
WAL segments
Recovery therefore looks like:
restore base
+
replay committed WAL
=
current database
As the WAL grows, recovery has more work to do.
A checkpoint folds the current state into a new base.
So there is a tradeoff:
frequent checkpoint
-> more transfer work
-> shorter recovery
infrequent checkpoint
-> less transfer work
-> more WAL replay
This is a familiar database systems tradeoff appearing inside an agent architecture.
head.json: who owns the database?
There is another problem.
Suppose two workers open:
acme-checkout-api
at the same time.
Both could believe:
"I am the writer."
That is dangerous.
Durable uses a head record with conditional writes to establish lease and fencing semantics.
At a high level:
read current head = version 17
writer A:
CAS(17 -> 18) succeeds
writer B:
CAS(17 -> 18) fails
Only one process becomes the current writer.
This gives Durable a deliberate constraint:
one writer per object.
That is a useful property for a per-user or per-project agent brain.
It is a poor fit for a team database in which ten independent processes continuously update the same object.
5. Store memory as history, not as one giant document
This is where using an analytical database becomes interesting.
A naive memory system might have:
memory.json
A more useful design has several relations:
memories
memory_history
raw_transcripts
events
For example:
CREATE TABLE memory_history
(
memory_id String,
version UInt32,
op LowCardinality(String),
content String CODEC(ZSTD(3)),
edited_by String,
edited_at DateTime64(3, 'UTC'),
prev_id String,
note String
)
ENGINE = MergeTree
ORDER BY (memory_id, version, edited_at);
Now a revision does not destroy the original fact.
Instead:
version 1:
Deploy checkout-api to eu-west-1.
version 2:
Deploy checkout-api to eu-central-1.
You can query the current state.
You can also query the history.
You can even ask:
Why did the agent believe this?
Which evidence produced the belief?
When did the belief change?
How often was this memory recalled?
Did those recalls actually help?
That last class of questions is important.
A memory system should eventually become capable of analyzing its own memory.
That is naturally expressed with SQL.
Raw evidence and actual memory are different
One especially useful distinction in the ClickMem design is between evidence and memory.
A transcript may contain:
User:
"Maybe the service is in eu-west-1?"
Tool:
"Deployment failed."
Agent:
"Let's inspect the infrastructure."
None of that necessarily deserves promotion into long-term memory.
Instead:
raw_transcripts
= searchable evidence
memories
= curated beliefs
This reduces a common failure mode of memory systems:
every conversation
-> memory
-> future behavior
A transient mistake then becomes persistent behavior.
The alternative is:
conversation
-> evidence
-> evaluation
-> memory
That resembles the observation/retrieval/reflection pattern explored in early generative-agent research, but gives the resulting state a conventional data model.
6. The math, storage economics, and operational tradeoffs
There are two pieces of mathematics worth understanding.
Semantic recall
Suppose every memory has an embedding vector m, and the current task has query vector q.
A common semantic score is cosine similarity:
score(q, m) = (q dot m) / (||q|| ||m||)
The intuition is simple:
same direction
-> high similarity
different direction
-> low similarity
A memory query can therefore look like:
1. Filter project
2. Filter active memories
3. Generate semantic candidates
4. Rank candidates
5. Return top K
For a brute-force scan with:
N memories
D embedding dimensions
the rough amount of dot-product work is:
N * D
For example:
N = 100,000 memories
D = 1,536
N * D
= 153,600,000
That is roughly 154 million multiply-add terms for a full scan.
Whether that is acceptable depends on hardware, query frequency, and filtering. The useful observation is that memory retrieval is a data-processing problem, so the choice between prefiltering, vector indexes, approximate nearest-neighbor methods, and brute-force scans becomes a database engineering decision.
chDB already provides the local analytical machinery around the embedding column; the durability layer does not need to know whether your recall logic is lexical, vector-based, temporal, or some combination.
Storage is often cheaper than the memory architecture makes it look
ClickHouse published an instructive experiment using a 1.45 GB Claude Code transcript sample containing 213,721 rows.
Loaded into MergeTree:
ZSTD(3): 521 MiB
LZ4: 991 MiB
So the ZSTD representation is roughly:
521 / 1,383 ~= 0.38
or about a 62% reduction relative to the original binary size.
There was another useful observation:
The screenshots represented only about 0.8% of rows, but roughly two-thirds of the bytes.
That leads to a practical rule:
textual history -> compress in the database
large binary objects
-> keep outside the table
-> store object keys in the database
Otherwise your screenshots, audio, or video become part of every checkpoint.
A rough storage-cost calculation
Suppose an agent eventually accumulates:
0.5 GiB compressed durable state
Using an illustrative S3 Standard rate of:
$0.023 / GB-month
the raw storage component is approximately:
0.5 * 0.023
= $0.0115/month
about one cent per month.
That is not a complete cloud bill. PUT/GET requests, transfer, replication, and the exact storage class and region all matter.
But the calculation exposes something useful:
For small per-agent memory stores, storage bytes are unlikely to be the expensive part.
The architectural costs are more likely to come from:
inference
network calls
database operations
operational complexity
recovery behavior
This is why a one-bucket architecture can be attractive.
Instead of paying continuously for:
database server
connection pool
database operations
persistent volumes
replication sidecars
you retain a local process and pay for durable state only at explicit boundaries.
The tradeoff is that you now own the lifecycle:
IAM
bucket policies
encryption
object naming
retention
flush policy
checkpoint policy
That is a much smaller operational surface than operating a database cluster, but it is still an engineering responsibility.
7. When should developers use Durable?
The cleanest decision rule is based on the shape of the workload.
Use plain local chDB when:
the state is disposable
or
the state never leaves one machine
Use a transactional store when:
you mainly need checkpoints
and
you do little historical analysis
Use a server database when:
many writers
many clients
centralized governance
large working sets
continuous shared access
The Durable Layer fits the middle:
one logical owner
+
local analytical queries
+
append-heavy history
+
portable durable state
A useful deployment might look like:
S3 / GCS / Azure
|
durable agent object
|
+--------------+--------------+
| |
laptop CI runner
| |
chDB chDB
| |
local SQL local SQL
| |
recall recall
The database moves.
The query engine stays embedded.
The service layer disappears.
And the boundary between "memory" and "data engineering" becomes much thinner.
There is also a nice historical symmetry here.
ClickHouse itself began at Yandex under Alexey Milovidov and colleagues as an attempt to generate real-time analytical reports from continuously arriving, non-aggregated data. Its early production system eventually became the engine behind Yandex.Metrica.
Years later, the same broad philosophy is useful for agents:
keep the raw history
store it efficiently
query it later
derive answers from the history
The difference is that the data is now an agent's experiences rather than web analytics.
That may be one of the more useful ways to think about agent memory.
Memory is a database that happens to influence future decisions.
And once you view it that way, persistence, provenance, revision history, compression, query latency, leases, checkpoints, and recovery stop looking like infrastructure details. They become part of the agent's cognitive architecture.
The chDB Durable Layer is interesting because it gives developers a missing deployment primitive:
embedded analytical state
+
portable durability
+
object storage
+
no database server
The result is a local agent brain that can survive the machine that created it.
What do you think the right primitive for long-lived agent memory should be: a vector store, a transactional database, or an analytical database with durable local state?
Your team's attention is limited, and the deluge of AI-generated code is making it harder to keep production reliable and secure without slowing you down.
I'm building LiveReview, a blast-radius aware AI code review built for your business-critical systems.
Instead of presenting every diff with equal emphasis, LiveReview scores each change by blast radius — how far its impact reaches through your call graph — so you can focus attention where it actually matters.
Spend code review effort where business risk is highest — not spread evenly across every diff.
⭐ Star it on GitHub:
HexmosTech
/
LiveReview
Blast-Radius Aware AI Code Review for Business-Critical Systems
LiveReview: Blast-Radius Aware AI Code Review for Business-Critical Systems
LiveReview is an AI code reviewer that scores every hunk of a diff by blast radius: how far a change reaches through your call graph, how much persistent state it touches, and how well-tested it is. A 3-line change to a shared auth check can outrank a 300-line UI tweak. Your team's attention goes to the highest-risk code first, not spread evenly across every diff.
blast-radius-demo.mp4
LiveReview's Blast Radius & Review Priority scoring, live in the diff viewer.
Here's the goal:
- A 3-line fix in a function used by 40 other files, that also writes to a database, should score high.
- A 300-line UI change in one file, fully covered by…
Click below to try LiveReview with your codebase:





Top comments (0)