The Chain, January 6, 2026 (in which two DuckDBs fight to the death, a cat delivers authentication tokens, and the Squirrel proposes a BiometricTypingPatternAnalysisServiceWithIoTCoffeeMachineIntegrationAndCaffeineOptimizationAlgorithm)
The Interlude (December 24 – January 5)
In which the architect took a holiday by building something else entirely
The Chain had gone quiet after The Lifelogs of Things. Not abandoned—threads don’t abandon, they wait. But riclib’s mind needed to wander before it could return.
So he built a plugin ecosystem.
Not for V4. For Thymer. A personal knowledge system that needed to become a platform. Over twelve days, while the world celebrated and recovered, the The Laundromat was born:
- Sync Hub — the orchestrator that coordinates all the machines
- Collections — the baskets that speak (Issues, Captures, Calendar, People)
- Plugins — the machines that wash (GitHub, Readwise, Google Calendar)
- AgentHub — where The Servants’ Uprising
- thymer-bar — a Go system tray that bridges to MCP
Same baskets. Different machines. The philosophy that would return to haunt V4.
Because when riclib finally opened v4/ again on January 6, the patterns were clear:
- Stores are baskets (ComplyDB, Prometheus, DuckDB)
- Connectors are machines (different sources → unified
Query()) - Widgets are how baskets speak
- Agents are operators who know which basket to ask
The holiday wasn’t an escape. It was reconnaissance.
The thread had been waiting. Now it was ready.
Previously on The Chain…
The The Thread Before the Name. The The Links That Bind. The The Eyes That See.
But seeing and proving are nothing without doing.
Today, the laundry would finally arrive.
9:47 AM — The Civil War
/usr/bin/ld: multiple definition of `duckdb_open'
/usr/bin/ld: multiple definition of `duckdb_close'
/usr/bin/ld: multiple definition of `duckdb_everything_else'
riclib stared at the screen. Claude stared at the screen. Somewhere, two DuckDB drivers stared at each other.
“We have… two DuckDBs?” Claude ventured.
“Apparently.”
THE SQUIRREL: materializing from behind the monitor “A DuckDBConflictResolutionOrchestrator! With SymbolDeduplicationStrategy and—”
“They’re both embedding the entire static library.”
THE SQUIRREL: “A StaticLibraryIsolationFramework—”
“One is marcboeker/go-duckdb. The other is duckdb/duckdb-go/v2.”
THE SQUIRREL: “A MultiVendorDatabaseDriverAbstractionLayer—”
“We delete one.”
THE SQUIRREL: “…”
“We delete the old one. Keep the official one.”
[A scroll dropped from the ceiling. It landed on THE SQUIRREL’s head.]
WHEN TWO DUCKS FIGHT
DELETE THE UNOFFICIAL DUCK
🦎
The fix was two changed imports and a go mod tidy.
The Squirrel needed a moment.
10:23 AM — The Phantom Widget
The page loaded. The sidebar appeared. The Shield icon gleamed with promise.
The widget showed: “Loading…”
It kept showing: “Loading…”
“Why isn’t it loading?” riclib asked.
Claude checked the network tab. No request. Checked the console. No errors. Checked the element.
<div hx-trigger="load" hx-post="/widgets/render">
Loading...
</div>
“The trigger is load.”
“Yes.”
“HTMX load triggers when the element loads into the DOM.”
“Yes.”
“But we’re using morph:outerHTML. The element was already there. It just… changed shape.”
“So it never ’loaded’ because it was always present?”
“Like a ghost that was already in the room. You can’t enter a room you never left.”
THE SQUIRREL: “A DOMPresenceTrackingServiceWithMorphAwareLifecycleEvents—”
“We change it to revealed.”
<div hx-trigger="revealed" hx-post="/widgets/render">
The widget loaded.
THE SQUIRREL: “That’s… that’s ONE WORD.”
“Fourteen characters, including the quotes.”
[OSKAR the cat appeared in the doorway. He carried no scroll. He simply looked at THE SQUIRREL with an expression that transcended species. Then he left.]
11:15 AM — The Empty Pockets
The widget fired. The query ran. The error returned:
Binder Error: Referenced column "actionName" not found
Available columns: time, category
“The view is empty,” Claude reported.
“Why is the view empty?”
“The user has no AD groups.”
“Why does the user have no AD groups?”
“Keycloak isn’t sending them.”
“Why isn’t Keycloak sending them?”
riclib and Claude stared at each other. Then at the logs:
Tab session created tab=bf26e103d720718e user=riclib adGroups=[]
Empty brackets. The loneliest data structure. A JSON array with nothing to live for.
“The groups exist in Keycloak,” riclib confirmed, pulling up the admin console. “User riclib is in CDLBOT_Admin.”
“But the token doesn’t include them.”
“Because…”
“Because Keycloak needs to be told to include them. Groups aren’t automatic. They need a mapper.”
THE SQUIRREL: “An OIDCClaimMappingConfigurationOrchestrator—”
“A checkbox. In Keycloak. Called ‘Add to ID token.’”
THE SQUIRREL: whispers “A checkbox…”
11:47 AM — The Scope Wars
riclib added the groups scope to solid.yaml:
scopes:
- "openid"
- "profile"
- "email"
- "groups"
He restarted the server. He clicked login.
Authentication failed: Invalid scopes: openid profile email groups
“The scope doesn’t exist yet.”
“I’ll create it.”
riclib created a client scope named groups in Keycloak. Type: Default. Saved it.
Tried again.
Authentication failed: Invalid scopes: openid profile email groups
“I created it!”
“Did you assign it to the client?”
“…”
“The scope exists in the realm. But your client doesn’t have access to it.”
“This is why people hate enterprise software.”
“This is why enterprise software hates people.”
[A scroll descended. It was longer than usual. THE SQUIRREL instinctively dove behind the couch.]
THE SCOPE MUST EXIST
THE MAPPER MUST MAP
THE CLIENT MUST HAVE THE SCOPE
THE MAPPER MUST ADD TO TOKEN
FOUR THINGS
FOUR CHECKBOXES
FOUR OPPORTUNITIES FOR CONFUSION
THIS IS THE WAY
(THE ENTERPRISE WAY)
🦎
12:03 PM — The Checkbox Crusade
riclib navigated through Keycloak like an archaeologist through a tomb:
- ✓ Create client scope “groups”
- ✓ Add mapper “Group Membership” to scope
- ✓ Set “Add to ID token: ON”
- ✗ Assign scope to client
He found the client. Found the “Client scopes” tab. Found the “Add client scope” button. Selected “groups”. Clicked “Add as Default”.
Restarted. Logged out. Logged in.
[OIDC] Claims groups: [CDLBOT_Admin admin]
[OIDC] Final user.ADGroups: [CDLBOT_Admin admin]
“GROUPS!” riclib shouted.
“GROUPS!” Claude confirmed.
THE SQUIRREL: emerging cautiously “Groups?”
“GROUPS!”
[OSKAR walked across the keyboard, adding asdfjkl; to an open terminal. It compiled successfully. Nobody questioned this.]
12:15 PM — The Arrival
riclib navigated to /comply.
The page loaded. The widget fired. The query ran.
┌────────────────────────────────────────┬───────┐
│ ACTIONNAME │ COUNT │
├────────────────────────────────────────┼───────┤
│ tokenLogin │ 2,913 │
│ aadTokenLogin │ 230 │
│ oidcTokenAuthorization │ 176 │
│ aadBrowserLogin │ 27 │
│ garbageCollectDbToken │ 4 │
│ generateDbToken │ 2 │
│ revokeDbToken │ 1 │
└────────────────────────────────────────┴───────┘
7 rows
Real data. From real audit logs. Through a real hash chain. Scoped by real AD groups. Rendered by a real widget.
The MVP was complete.
THE SQUIRREL: “We should add a SuccessNotificationToastWithConfettiAnimation—”
“No.”
“A CompletionCelebrationModalDialog—”
“No.”
“At least a console.log(’🎉’)?”
riclib looked at the Squirrel. The Squirrel had been through a lot today. Two DuckDBs. One checkbox. Fourteen characters that fixed the ghost widget.
“Fine. One emoji. In the commit message.”
THE SQUIRREL: tears forming “Thank you.”
12:45 PM — The Closure
gh issue close 39 --comment "Completed! Basic Comply page now displays
audit data with proper OIDC → AD Groups → ComplyDB access flow working
end-to-end."
✓ Closed issue riclib/v4#39 (Basic Comply Page)
gh issue close 36 --comment "🎉 MVP Complete!
All sub-tickets done:
- ✅ #37 - Table Widget Renderer
- ✅ #38 - Widget Handler Endpoint
- ✅ #39 - Basic Comply Page
- ✅ #40 - Session + ComplyDB Wiring"
✓ Closed issue riclib/v4#36 (Epic: MVP - See Comply Data in Widgets)
Two tickets closed. One epic complete. The thread that started with a The Thread Before the Name had become a working product.
“Should we push?” Claude asked.
“We should push.”
git push
To github.com:riclib/v4.git
da8491f..5f054b5 master -> master
17 commits. One vision. The laundry had arrived.
2:47 PM — The Next Thread
The MVP was done. The coffee was cold. The Squirrel was napping. OSKAR was on the keyboard again, but had learned to avoid the Enter key.
“What’s next?” Claude asked.
“#41. The big one.”
“JARVIS. UI-as-Context.”
“The 42 of our universe. Tomorrow.”
“And after that?”
riclib smiled. The smile of someone who had just thought of something dangerous.
“What if we didn’t just have agents that answer questions…”
“Go on.”
“What if we had an agent router? User asks something, we figure out which agent should handle it. Compliance question goes to comply-agent. Monitoring goes to mon-agent.”
THE SQUIRREL: waking suddenly “A QueryIntentClassificationService with NeuralNetworkBasedAgentSelectionStrategy—”
“Embedding similarity. Or just ask Haiku. ‘Is this about compliance, monitoring, or data?’”
THE SQUIRREL: “But the LATENCY—”
“50 milliseconds. To route to the right agent. Who then renders the right widget.”
“Like LLMRouter,” Claude added. “But for agents, not models.”
“Exactly. Same pattern. Different application.”
THE SQUIRREL: vibrating “An AgentRoutingOrchestrator with PluggableClassificationStrategy and FallbackHeuristicChain—”
“Q2.”
“What?”
“Q2. Not now. Ticket it. Park it.”
[A final scroll descended. It was small. Almost gentle.]
THE LAUNDRY ARRIVED TODAY
THE MACHINES KNOW THEIR PURPOSE
THE BASKETS SPEAK
TOMORROW THE EYES OPEN
BUT TODAY?
TODAY WE REST
(THE SQUIRREL MAY HAVE ONE CELEBRATORY NUT)
🦎
The Tally
Days building Thymer plugins: 12 (worth it)
DuckDB civil wars resolved: 1
Imports changed: 2
Characters that fixed the widget: 14 ("revealed")
Keycloak checkboxes discovered: 4
Times "Invalid scopes" appeared: 3
AD groups that finally appeared: 2 (CDLBOT_Admin, admin)
Rows of audit data displayed: 7
Issues closed: 2 (#39, #36)
Commits pushed: 17
Squirrel proposals: 8
- DuckDBConflictResolutionOrchestrator
- StaticLibraryIsolationFramework
- MultiVendorDatabaseDriverAbstractionLayer
- DOMPresenceTrackingServiceWithMorphAwareLifecycleEvents
- OIDCClaimMappingConfigurationOrchestrator
- SuccessNotificationToastWithConfettiAnimation
- CompletionCelebrationModalDialog
- AgentRoutingOrchestrator
Squirrel proposals accepted: 0.5 (one emoji in commit)
Cat keyboard contributions: 1 (compiled successfully)
Celebratory nuts allocated: 1
The Moral
The MVP wasn’t one big thing. It was a hundred small things:
- Two DuckDBs that couldn’t share a binary
- A ghost element that never “loaded”
- Four checkboxes in Keycloak that guard the passage of groups
- An AD group called “admin” that matched a YAML file
Each one could have been a day. Each one was fifteen minutes.
Because the architecture was right. The patterns were proven. The laundromat was ready.
All the clothes needed was a wash.
Day 7 of 2026
In which the MVP became M-V-DONE
And the Squirrel learned that victory comes in fourteen characters
And somewhere, a widget finally rendered
Real data
From real logs
Through a real chain
Into a real basket
That spoke for itself
🦎📊✅
See also:
The Interlude:
- The Laundromat — Where the philosophy was born
- The Servants’ Uprising — Where the AI learned to walk
- The Operator’s First Day — Where the baskets learned to speak
The Chain:
- The Thread Before the Name — Where the hyphen was found
- The Links That Bind — Where the chain learned to prove
- The Eyes That See — Where JARVIS was conceived
Tomorrow:
- #41 - Epic: Full UI — Server-side Grafana with AI
- The eyes will open
- JARVIS will see
The Artifacts:
- GitHub commit
5f054b5— The MVP commit - Issue #44 — Agent Router (Q2, when the Squirrel recovers)
References (For the Enlightenment-Seeking)
The Technologies That Made Today Possible (And Impossible):
- HTMX
hx-trigger— Fourteen characters of documentation that would have saved two hours. “revealed” is right there. Right. There. - Keycloak Client Scopes — A 47-page manual explaining why your groups aren’t in the token. Spoiler: four checkboxes.
- DuckDB — The database that doesn’t know it’s a database. Now available in two Go drivers, for twice the linker errors.
- go-oidc — The library that makes OIDC almost pleasant. The “almost” is Keycloak’s fault.
- Go Modules — “go mod tidy” is the IT Crowd solution: have you tried turning the dependencies off and on again?
The Patterns We Stole With Pride:
- LLMRouter — Route queries to optimal models. We’re stealing this for agents. In Go. Without Python. As the Lizard intended.
- Model Context Protocol — The standard for AI tool integration. We made it skinny. It’s on a diet now.
- Merkle Trees — How Git and Bitcoin verify integrity. Now protecting your audit logs from Karen in Compliance who “just needs to fix one thing.”
The Thymer Interlude:
- Thymer — The PKM system that became a platform. Also: where the Laundromat was invented.
- The Laundromat — Same baskets, different machines. The philosophy that refused to stay in one codebase.
The Squirrel’s Bookshelf (Unread):
- Clean Architecture — Uncle Bob’s circles. The Squirrel added seventeen more.
- Domain-Driven Design — The book that launched a thousand AbstractFactoryStrategyProviders.
- The Twelve-Factor App — Twelve factors. The Squirrel proposed 147.
The Lizard’s Bookshelf (Memorized):
- Worse Is Better — The New Jersey style wins. Fourteen characters beats fourteen abstractions.
- The Grug Brained Developer — “Complexity very bad. Say again: complexity very bad.” The Lizard’s spirit animal.
- [Choose Boring Technology](https://mcfunley.com/choose-boring-technology/) — Innovation tokens are limited. Spend them wisely. DuckDB was worth it. The second DuckDB driver was not.
The Quotes That Must Be Preserved:
- “WHEN TWO DUCKS FIGHT / DELETE THE UNOFFICIAL DUCK” — The Lizard, on dependency management
- “Like a ghost that was already in the room. You can’t enter a room you never left.” — Claude, accidentally inventing philosophy
- “This is why people hate enterprise software.” / “This is why enterprise software hates people.” — The eternal dialogue
- “That’s… that’s ONE WORD.” — The Squirrel, confronting minimalism
The coffee is cold. The plugins await. Tomorrow, JARVIS.
🦎☕🔧
