The Solid Convergence, January 25, 2026 (in which three storage engines become one truth, a package dies with dignity, and the Squirrel learns that the best database is someone else’s)
Previously on The Solid Convergence…
The The Journals We Keep. User and agent, writing to the same stream. Entangled lifelogs.
But the stream lived in memory. Close the tab, lose the conversation. The journals existed, but they didn’t persist.
Today, they would learn to remember.
9:15 AM — The Temptation
THE SQUIRREL: appearing with blueprints “I’ve designed SolidMemDB! Three-layer storage! Memory for hot data, JetStream for WAL, Parquet for cold! We just need to implement—”
riclib: “Stop.”
THE SQUIRREL: “But it’s ELEGANT! Custom indexes! Temporal queries! A QueryPlannerWithCostEstimation—”
riclib: “What databases already exist?”
THE SQUIRREL: “…what?”
riclib: “In the world. Right now. What databases exist that do these things?”
THE SQUIRREL: blinking “Postgres? MySQL? SQLite? But those aren’t CUSTOM. They don’t have our specific—”
riclib: “SQLite has been in production since 2000. It runs on literally every phone on Earth. It will outlive every JavaScript framework. It will outlive US.”
THE SQUIRREL: “But we need real-time streaming! And durability! And—”
riclib: “JetStream. NATS has been battle-tested for a decade. Literally designed for exactly this.”
THE SQUIRREL: “But… but the CUSTOM parts…”
riclib: “What custom parts?”
[Silence. The Squirrel looked at her blueprints. The QueryPlannerWithCostEstimation. The TemporalIndexWithVersioning. The HybridStorageAbstractionLayer.]
THE SQUIRREL: very quietly “There are no custom parts.”
riclib: “There are no custom parts.”
10:23 AM — The Composition
Claude pulled up the architecture:
┌─────────────────────────────────────────────────────────────┐
│ THE STACK │
├─────────────────────────────────────────────────────────────┤
│ │
│ JETSTREAM SQLITE DUCKDB │
│ ───────── ────── ────── │
│ Pub/sub Queries Analytics │
│ WAL Forever Parquet │
│ 30 days Full content Metadata │
│ Real-time Per-user Aggregates │
│ │
│ "The stream" "The memory" "The archive" │
│ │
└─────────────────────────────────────────────────────────────┘
riclib: “JetStream does what JetStream does best. Durability. Real-time. Replay.”
CLAUDE: “SQLite does what SQLite does best. Queries. Full-text search. One file per user.”
riclib: “DuckDB does what DuckDB does best. Analytics. Parquet. Columnar scans.”
THE SQUIRREL: “But who COORDINATES them? We need an OrchestrationLayerWithConsistencyGuarantees—”
riclib: “A background goroutine. That reads from JetStream. And writes to SQLite.”
THE SQUIRREL: “That’s not an orchestration layer. That’s a FOR LOOP.”
riclib: “Yes.”
[A scroll descended. It landed on the Squirrel’s blueprints.]
THE BEST DATABASE
IS SOMEONE ELSE'S
THE BEST ORCHESTRATION
IS A FOR LOOP
THE BEST ARCHITECTURE
IS COMPOSITION
YOU DON'T BUILD LEGO BRICKS
YOU BUILD WITH LEGO BRICKS
🦎
11:47 AM — The Heresy
THE SQUIRREL: “But what about solidmem? The package we built last month? The abstractions? The—”
riclib: “Delete it.”
THE SQUIRREL: “DELETE it?”
riclib: “It served its purpose. We learned what we needed. Now we have better tools.”
CLAUDE: “It’s 437 lines. Stream and Bit types. A test suite.”
riclib: “And it’s holding us back. Every time we touch the stream, we work around solidmem’s assumptions. It was scaffolding. The building is up. Remove the scaffolding.”
THE SQUIRREL: horrified “But the BACKWARD COMPATIBILITY! The migration path! The DeprecationScheduleWithGracefulFallback—”
riclib: “There is no backward compatibility.”
THE SQUIRREL: “WHAT?”
riclib: “solidmon was a prototype. Comply is the product. They don’t share data. They don’t share users. They don’t share anything except lessons learned.”
THE SQUIRREL: “But… but the PRINCIPLE! You don’t just DELETE working code!”
riclib: “You do if it’s not the right code anymore.”
git rm -r infra/solidmem/
infra/solidmem/README.md | 139 ---------------------------------
infra/solidmem/stream.go | 130 -------------------------------
infra/solidmem/stream_test.go | 168 ----------------------------------------
3 files changed, 437 deletions(-)
THE SQUIRREL: watching the deletion “You just… you just killed it.”
riclib: “I freed it. It did its job. It taught us what streams need. Now streamstore does it better, with boring databases that will outlive us both.”
12:33 PM — The Philosophy
riclib walked to the window. The vision-having window. But this wasn’t a vision. This was a manifesto.
riclib: “Boring technology. Beautiful results.”
CLAUDE: “No React.”
riclib: “No React. No virtual DOM. No reconciliation. No npm install with 847 transitive dependencies.”
CLAUDE: “No Docker.”
riclib: “No Docker. No container orchestration. No Kubernetes. One binary. Copy it. Run it. Done.”
THE SQUIRREL: “But MODERN development—”
riclib: “Modern development is a mass hallucination. SQLite is modern. It’s actively developed. It just doesn’t need a Medium article every week.”
CLAUDE: “The best interface is no interface.”
riclib: “The best database is one file.”
CLAUDE: “The best deployment is one binary.”
THE SQUIRREL: reading from somewhere “Built by riclib and Claude with Go, SQLite, and stubbornness.”
riclib: “Stubbornness is a feature. It’s the refusal to add complexity just because complexity is available.”
[A scroll descended. It was heavy. It had been waiting to be written for a long time.]
THE BORING TECHNOLOGY MANIFESTO
1. IF A DATABASE EXISTS, USE IT
DON'T BUILD ONE
2. IF A PROTOCOL EXISTS, USE IT
DON'T INVENT ONE
3. IF CODE SERVED ITS PURPOSE
DELETE IT
DON'T MAINTAIN IT
4. BACKWARD COMPATIBILITY
IS FOR PRODUCTS WITH USERS
PROTOTYPES HAVE LEARNERS
5. THE BEST DEPLOYMENT
IS SCP AND RESTART
EVERYTHING ELSE IS COPING
6. COMPLEXITY IS NOT A FEATURE
COMPLEXITY IS DEBT
PAID IN MAINTENANCE
FOREVER
🦎
P.S. - THE SQUIRREL BUILDS CATHEDRALS
THE LIZARD BUILDS SHELTERS
CATHEDRALS ARE BEAUTIFUL
SHELTERS KEEP YOU DRY
BUILD WHAT YOU NEED
NOT WHAT IMPRESSES
2:15 PM — The Wiring
The streamstore was ready. JetStream for streaming. SQLite for queries. The conversation domain to expose it.
// infra/streamstore/factory.go
func Open(cfg Config) (*Store, error) {
js := connectJetStream(cfg.NATS)
sqlite := openSQLite(cfg.DataDir + "/" + cfg.UserID + ".db")
return &Store{
js: js,
sqlite: sqlite,
cache: newCache(),
}, nil
}
THE SQUIRREL: “That’s… that’s the whole factory?”
riclib: “Connect to JetStream. Open SQLite. Return a store.”
THE SQUIRREL: “Where’s the AbstractStorageProviderFactory?”
riclib: “You’re looking at it.”
THE SQUIRREL: “Where’s the DependencyInjectionContainer?”
riclib: “The function parameters.”
THE SQUIRREL: “Where’s the ConfigurationValidationMiddleware?”
riclib: “An if statement. Line 12.”
if cfg.DataDir == "" {
return nil, errors.New("data directory required")
}
THE SQUIRREL: sitting down “Twelve lines. The entire storage factory is twelve lines.”
riclib: “Plus the imports.”
3:47 PM — The Domain
The conversation domain materialized. A vertical slice. Types, store, handlers, views.
domains/conversation/
├── types.go # ConversationSummary, SearchResult
├── store.go # Wraps streamstore, adds business logic
├── handlers.go # HTTP handlers
├── routes.go # Path helpers
├── list.templ # Conversation list UI
├── detail.templ # Conversation detail UI
└── search.templ # CMD-K search modal
CLAUDE: “The store wraps streamstore. Adds ListRecent. Adds Search. Adds GetByID.”
riclib: “The handlers call the store. Render the templates. Return HTML.”
THE SQUIRREL: “Where’s the ConversationServiceWithRepositoryPattern?”
riclib: “The store IS the repository. The handlers ARE the service. There’s nothing in between because there’s nothing to put in between.”
CLAUDE: “Same pattern as every other domain. Types, store, handlers, views.”
riclib: “Copy with pride. The best architecture is the one you can copy-paste.”
4:33 PM — The Reunion
The LoadConversation handler brought it together. Tabs bound to conversations. History restored to streams.
// POST /stream/load/{id}
func (h *Handler) LoadConversation(w http.ResponseWriter, r *http.Request) {
tabID := r.Header.Get("X-Tab-Id")
convID := r.PathValue("id")
bits, _ := h.convStore.GetAllBits(ctx, convID)
h.mu.Lock()
h.conversations[tabID] = convID
h.mu.Unlock()
stream.Container(bits, convID).Render(ctx, w)
}
THE SQUIRREL: “That’s the whole handler?”
riclib: “Get the tab. Get the conversation. Link them. Render.”
THE SQUIRREL: “No TabSessionOrchestrator?”
riclib: “A map.”
THE SQUIRREL: “No ConversationLoadingPipeline?”
riclib: “A database query.”
THE SQUIRREL: “No StateReconciliationService?”
riclib: “An assignment.”
[The Squirrel was quiet for a long time.]
THE SQUIRREL: “The whole thing. SQLite, JetStream, conversations, loading, search. How many lines?”
CLAUDE: “About 2,000 in streamstore. Another 800 in the conversation domain. 200 in the handlers.”
riclib: “3,000 lines. Three storage engines composed. Full conversation persistence. Content search. History browsing. CMD-K quick switcher.”
THE SQUIRREL: “I designed a QueryPlannerWithCostEstimation that was going to be 3,000 lines BY ITSELF.”
riclib: “And SQLite already has one. Written by people who’ve spent twenty years on it.”
THE SQUIRREL: “So we just… use theirs.”
riclib: “We just use theirs.”
5:15 PM — The Tally
Databases built: 0
Databases composed: 3 (JetStream, SQLite, DuckDB)
Lines deleted: 437 (solidmem, RIP)
Backward compatibility provided: 0
Backward compatibility needed: 0
Abstract factories created: 0
For loops used instead: 3
Configuration validators: 1 (an if statement)
Dependency injection containers: 0 (function parameters)
New packages:
infra/streamstore/ ~2,000 lines
infra/convctx/ ~150 lines
domains/conversation/ ~800 lines
Features shipped:
Conversation persistence ✓
Content search ✓
History browsing ✓
CMD-K quick switcher ✓
Tab → conversation binding ✓
Squirrel proposals declined:
- SolidMemDB (custom database)
- QueryPlannerWithCostEstimation
- OrchestrationLayerWithConsistencyGuarantees
- DeprecationScheduleWithGracefulFallback
- AbstractStorageProviderFactory
- DependencyInjectionContainer
- ConfigurationValidationMiddleware
- ConversationServiceWithRepositoryPattern
- TabSessionOrchestrator
- ConversationLoadingPipeline
- StateReconciliationService
Total Squirrel proposals: 11
Total accepted: 0
The Moral
The Squirrel wanted to build databases. Custom indexes. Temporal queries. The works.
The Lizard asked: what databases already exist?
JetStream exists. It does pub/sub and durability better than anything we could build in a year. SQLite exists. It does queries and full-text search better than anything we could build in a decade. DuckDB exists. It does analytics better than anything we could build ever.
So we composed them. JetStream streams to SQLite. SQLite will stream to DuckDB. Each does what it does best. We write the glue. Twelve lines of factory. A for loop for orchestration.
And solidmem? It served its purpose. It taught us what streams need. Then it died with dignity. Deleted, not deprecated. Because backward compatibility is for products with users. Prototypes have learners.
Boring technology. Beautiful results.
No React. No npm. No Docker.
Just Go, SQLite, and stubbornness.
Day 25 of 2026
In which we built nothing
And shipped everything
By composing what already existed
And deleting what no longer served
🦎📦🗃️
See also:
The Solid Convergence continues:
- The Journals We Keep — Where the stream learned to have two authors
- The Window That Opened Both Ways — Where markdown became the universal interface
The Chain (shipping perspective):
- The Stream That Woke — Where the agent started writing
- The Spec That Wrote Itself — Where widgets learned to query
The Artifacts:
- S-64: Stream Store implementation (Linear)
- S-69: Wire & deprecate solidmem (closed)
infra/streamstore/— The composed stackinfra/solidmem/— RIP, 437 lines, served well
The Philosophy:
- 488 Bytes, or Why I Am As I Am — Where the Lizard Brain was forged
storyline: The Solid Convergence
