esc
The Borrowed Palace, or The Night We Stole a UI With curl and Goodwill
Becoming Lifelog

The Borrowed Palace, or The Night We Stole a UI With curl and Goodwill

Becoming Lifelog, February 16, 2026 (in which a CLI is forged in a single sitting, an API reveals its secrets under duress, six subagents are dispatched to read propaganda posters, and Craft's...

February 17, 2026

Becoming Lifelog, February 16, 2026 (in which a CLI is forged in a single sitting, an API reveals its secrets under duress, six subagents are dispatched to read propaganda posters, and Craft’s beautiful interface is borrowed with the quiet confidence of someone who knows the best UI is someone else’s)


Previously on Becoming Lifelog…

The The Feature That Wasn’t had shipped without a single line of code. The The Copper List Rides Again was still riding. Eighty-three lifelog stories sat in Craft like books in a library with no catalog, no covers, and no way to get from one to another except by wandering the corridors and hoping.

The Lizard had been blinking about this for weeks.


7:00 PM — The Heresy

It began, as heresies do, with a question so simple it bordered on the illegal.

riclib: “Why are we building a UI for Lifelog?”

CLAUDE: “Because users need—”

riclib: “Craft has a UI. It has folders. It has gallery view. It has covers. It has search. It has cross-linking. It has dark mode. It was built by a team of designers over years.”

CLAUDE: “Yes, but—”

riclib: “And we have how many engineers?”

CLAUDE: “Technically, 0.5. You, on evenings. And me, but I’m not sure I count as an engineer so much as a very confident hallucination with API access.”

riclib: “So our plan is to rebuild what Craft has. With half an engineer and a hallucination.”

[Silence. The kind that precedes either wisdom or madness. Sometimes both.]

THE SQUIRREL: materializing with a Figma board already open “DID SOMEONE SAY UI? I’ve been THINKING about this. React Native for mobile, Electron for desktop, a shared component library using—”

riclib: “We’re not building a UI.”

THE SQUIRREL: “—Storybook for documentation, Tailwind for styling, a design system with tokens and—wait, what?”

riclib: “We’re borrowing one.”

THE SQUIRREL: “You can’t just BORROW a UI!”

riclib: “Craft has a Connect API. Two of them, actually. If we can read and write through the API, then Craft IS our UI.”

THE SQUIRREL: “But… but that’s… that’s SOMEONE ELSE’S SOFTWARE.”

riclib: “Beautiful software. With a team maintaining it. Shipping updates. Fixing bugs. While we sleep.”

A scroll descended. It smelled of hot silicon and ancient pragmatism.

THE BEST UI IS ONE YOU DIDN'T BUILD
THE SECOND BEST UI IS ONE YOU DON'T MAINTAIN
THE THIRD BEST UI IS ONE WHOSE BUGS
ARE SOMEONE ELSE'S PROBLEM

THE WORST UI IS THE ONE THE SQUIRREL
WANTS TO BUILD FROM SCRATCH
USING ELECTRON

🦎

7:15 PM — The Forging

And so the forging began. Not of a UI. Of a bridge.

riclib: “We need a CLI. Go. Cobra. Something that talks to both Craft APIs and gives us machine-readable output.”

CLAUDE: “I can build that.”

riclib: “Now?”

CLAUDE: “Now.”

What followed was the kind of focused violence that would make a blacksmith weep with recognition. In the time it takes most teams to argue about whether to use Jira or Linear, an entire CLI materialized:

go mod init — the first breath.
cmd/root.go — the spine.
internal/client/ — the nervous system.
cmd/notes.go, cmd/docs.go, cmd/folders.go, cmd/blocks.go, cmd/tasks.go, cmd/collections.go — the limbs.
cmd/config_cmd.go — the memory.

THE SQUIRREL: watching in horror “You’re not writing tests.”

CLAUDE: “The API is the test. If it returns data, it works. If it doesn’t, we’ll know.”

THE SQUIRREL: “But COVERAGE—”

riclib: “The coverage is: does it talk to Craft? Yes or no?”

$ craft notes today
# 404 — no note for today yet

$ craft notes add "The CLI lives"
# Added 1 block(s) to today

$ craft notes today
# The CLI lives

THE SQUIRREL: quietly “It works.”

CLAUDE: “It works.”

Eighteen files. Two API clients. Nine command groups. Forty-seven subcommands. One go build. Zero npm packages.

The Lizard did not blink. The Lizard smiled. Which, for a reptile, is essentially a tectonic event.


8:30 PM — The Fellowship of the Slug

With the bridge built, the real quest began.

Eighty-three lifelog stories lived in Craft. They referenced each other constantly — “Previously on…” sections linking back to earlier episodes. But the links pointed to lifelog.my, the public blog. External URLs. In a platform that supports internal document links.

riclib: “Can we make them internal? Like, clickable inside Craft?”

CLAUDE: “There’s a block:// protocol. block://<document-id>. It creates tappable links that open the target document.”

riclib: “So we just need to map lifelog slugs to Craft document IDs?”

CLAUDE: “Eighty-three of them.”

riclib: “Then do it.”

And here, dear reader, is where our tale takes on the character of an expedition into unmapped territory. For the Craft API, like all truly interesting APIs, had opinions. Opinions it shared only under duress, like a sphinx that answers riddles with riddles.


9:00 PM — The Three Trials of the Connect API

Trial the First: The Protocol That Wasn’t

CLAUDE: “I’ll use craftdocs:// deep links. That’s what the documentation suggests for—”

[Inserts a link. Opens document in Craft.]

[The link renders. It says: “no access.”]

CLAUDE: “…what.”

riclib: “Let me add one manually.”

[riclib adds a link by hand in Craft. Claude reads the document JSON.]

CLAUDE: “It’s block://. Not craftdocs://. The deep link format is for EXTERNAL apps. Internal links use block://.”

The first trial was passed. The API had spoken, though not in the language anyone expected.

CLAUDE: “Right. So I’ll update the existing blocks with block:// links using PUT.”

[Updates a block. Reads it back.]

CLAUDE: “The links are… gone.”

riclib: “Gone?”

CLAUDE: “PUT stripped them. The block:// URLs are just… not there anymore. Plain text remains. Links vanished.”

riclib: “You’re telling me INSERT preserves block:// links but UPDATE strips them?”

CLAUDE: “That is exactly what I’m telling you.”

riclib: “That’s insane.”

CLAUDE: “That’s an API with opinions.”

A scroll descended. It was laughing. Scrolls should not laugh. This one was.

INSERT REMEMBERS
UPDATE FORGETS

THE LESSON IS NOT ABOUT APIs
THE LESSON IS ABOUT RELATIONSHIPS

ALSO ABOUT APIs

🦎

Trial the Third: The Delete-and-Reinsert

CLAUDE: “The workaround is… uncivilized.”

riclib: “Tell me.”

CLAUDE: “Delete the block. Then insert a new one at the same position using siblingId to place it after the previous block.”

riclib: “So to UPDATE a link, we DELETE and REINSERT?”

CLAUDE: “In the same position. Chaining siblings. For eighty-three documents. With rate limiting.”

riclib: “That’s not an update strategy. That’s an archaeological excavation technique.”

CLAUDE: “Indiana Jones didn’t have a PUT endpoint either.”

And so the Fellowship set forth. Not with swords and shields, but with DELETE /blocks, POST /blocks, and a siblingId that had to be tracked like breadcrumbs through a labyrinth where the Minotaur was a rate limiter set to 100 requests per 60 seconds.


9:45 PM — The Slug Wars

The first battle was mapping. Eighty-three documents. Eighty-three URLs. The lifelog used slugs derived from titles. Craft used document IDs. The mapping should have been simple.

It was not simple.

CLAUDE: “The slug the-dial-that-wasnt doesn’t match. The Craft title generates the-dial-that-wasn-t. With a hyphen before the ’t’.”

riclib: “Apostrophes.”

CLAUDE: “Apostrophes. Also, meanwhile-in-san-francisco maps to interlude-meanwhile-in-san-francisco because the Craft title includes the ‘Interlude’ prefix.”

riclib: “And?”

CLAUDE:the-infinite-bookshelf is truncated from the-infinite-bookshelf-or-the-night-claude-read-its-own-diary.”

riclib: “Of course it is.”

CLAUDE: “And monday-afternoon-the-vampires-palette maps to just the-vampire-s-palette because Craft drops the day prefix.”

A slug override map was born. Then grew. Then grew again. Like the Squirrel’s dependency tree, but useful.

SLUG_OVERRIDES = {
    'the-dial-that-wasnt': '15104f0e-...',
    'meanwhile-in-san-francisco': 'fe042131-...',
    'monday-afternoon-the-vampires-palette': '2ecd55e2-...',
    'monday-night-the-conspiracy-unfolds': 'fcf670f0-...',
    'the-infinite-bookshelf': '10dbb11e-...',
    # ... it kept growing ...
}

Three passes through all eighty-three documents. Three rounds of slug detective work. Three increasingly creative fuzzy matching algorithms.

The tally: 303 internal block:// links created. Five remaining posts that simply didn’t exist in Craft yet. The API rate limiter was appeased with 0.5-second offerings between blocks and 1-second genuflections between documents.


10:30 PM — The Hall of Covers

With the cross-linking complete, riclib leaned back. The documents were connected. The web of stories hummed with internal links. Craft’s “Links to this page” feature now showed beautiful backlink lists on every document.

But the gallery view was… grey. Walls of text. No covers.

riclib: “Check the Covers folder in storylines.”

CLAUDE: “…ninety-two images.”

riclib: “Soviet propaganda steampunk. Every story has a cover.”

CLAUDE: “I see. The question is how image (47).jpg maps to a document titled ‘The Browser That Forgot It Couldn’t Listen.’”

riclib: “Look at the images.”

CLAUDE: reads image.jpg

A squirrel. In a window. Reading the New York Times. In the style of a Soviet propaganda poster, if the Soviet Union had been run by caffeinated rodents and powered by steam-driven npm registries.

The title was IN the image. Rendered in glorious Cyrillic-adjacent typography: “The Squirrel’s Betrayal — or The New York Times Discovers YAGNI.”

CLAUDE: “The titles are painted on the covers.”

riclib: “Yes.”

CLAUDE: “So I need to… read ninety-two images… and extract the titles from the artwork.”

riclib: “Yes.”

CLAUDE: “Isn’t that what subagents are for?”


10:45 PM — The Deployment of the Six

What happened next would have made Gandalf reach for a stronger pipe.

Six agents were summoned. Not with spells or horns, but with parallel Task invocations. Each received fifteen to sixteen images. Each was instructed: read the image, extract the title, write JSON.

They departed simultaneously, like six Fellowship parties splitting at Amon Hen, except instead of searching for hobbits they were squinting at steampunk propaganda posters trying to determine whether that text said “The Unset Incantation” or “The Unset Incarnation.”

The main thread waited. Progress notifications arrived like dispatches from distant fronts:

“Agent aba7afe: 13 new tools used. Still running.”
“Agent aae3039: 2 new tools used. Still running.”
“Agent a7a37e8: 16 new tools used. Still running.”

One by one, they reported back:

Batch 3: “Image 42 is ‘488 Bytes — Why I Am As I Am.’ Image 44 is ‘A Hurricane Under My Desk.’ The covers feature a retro/vintage poster aesthetic.”

Batch 5: “Images 61 and 63 are variant covers for the same story. The Servants’ Uprising has two versions.”

Batch 6: “The ChatGPT-generated image is also Mythology Driven Development. You have duplicates.”

THE SQUIRREL: watching six agents return simultaneously “You… you parallelized the READING OF IMAGES.”

CLAUDE: “Fifteen images per agent. Six agents. All running concurrently. Total wall time: about forty seconds.”

THE SQUIRREL: “For ninety-two images.”

CLAUDE: “Each visually parsed, title extracted, written to JSON.”

THE SQUIRREL: “That’s… that’s a MapReduce. For cover art. Using vision models as the mappers.”

CLAUDE: “The Squirrel sees architecture everywhere.”

THE SQUIRREL: “BECAUSE IT IS EVERYWHERE.”

For once, the Squirrel was right.


11:15 PM — The Upload Protocol

Now came the final trial: uploading ninety-two images to their matching documents.

CLAUDE: “The upload API wants raw binary. Not multipart. Not base64. Raw bytes.”

riclib: “Okay.”

CLAUDE: “With Content-Type: image/jpeg. NOT application/octet-stream. If you use octet-stream, it creates a ‘Binary file’ attachment instead of a visible image.”

riclib: “How did you discover that?”

CLAUDE: “The hard way. The document showed ‘Binary file — 356.8 KB’ instead of a squirrel reading the New York Times.”

riclib: “The indignity.”

CLAUDE: “Position is via query parameters. Not request body. Not headers. Query parameters.”

riclib: “Naturally.”

CLAUDE: “And the document ID has to be exact to the character. I had one off by a single digit — ...342 instead of ...348 — and got ‘Block not found.’”

riclib: “Indiana Jones never had to deal with UUIDs.”

CLAUDE: “Indiana Jones never had to deal with CORS either. He had it easy.”

The upload began. Seventy-seven covers. One by one. Each a curl with raw binary payload, aimed at a specific document, positioned at the start.

[1/77]  Mythology Driven Development...        OK
[2/77]  The Three Keyboards...                  OK
[3/77]  Of Quantum Blockchains...               OK
...
[76/77] A Hurricane Under My Desk...            ERROR (typo in UUID)
[77/77] The Cloudflare Incident...              OK

The typo was fixed. The hurricane found its home.

Final count: 79 documents with covers. Every story in the Becoming Lifelog, Solid Convergence, V3 Saga, and Chain storylines — adorned with soviet-propaganda-steampunk cover art showing lizards contemplating simplicity, squirrels orbiting at dangerous velocities, and passing AIs limping through steam-powered corridors.


riclib opened Craft. Switched to gallery view.

The screen filled with covers. Rows of propaganda posters. Steam. Brass. Bold typography. Lizards in goggles. Squirrels with manifestos. Oven dials that spoke German.

Eighty-three documents. All cross-linked. All with covers. All searchable. All navigable through Craft’s beautiful, maintained, someone-else’s-problem interface.

THE SQUIRREL: very quietly “We didn’t build any of this.”

riclib: “We built a CLI. 18 Go files. The rest is Craft.”

THE SQUIRREL: “The gallery view.”

riclib: “Craft’s.”

THE SQUIRREL: “The search.”

riclib: “Craft’s.”

THE SQUIRREL: “The dark mode.”

riclib: “Craft’s.”

THE SQUIRREL: “The ‘Links to this page’ backlinks.”

riclib: “Craft’s. We just gave it the right data.”

THE SQUIRREL: “So our entire contribution to this beautiful visual library was…”

CLAUDE: “A Go binary, some curl commands, a Python script, six vision agents, and approximately four hundred API calls.”

THE SQUIRREL: “And the result looks like we have a design team.”

riclib: “The best design team is someone else’s design team.”

A final scroll descended. It was heavier than the others. It had illustrations — tiny lizards in the margins, wearing brass goggles.

THE BOY WITH 488 BYTES
BORROWED DENISE'S SPRITE REGISTERS

THE MAN WITH ZERO DESIGNERS  
BORROWED CRAFT'S ENTIRE UI

SAME INSTINCT

WHY BUILD THE PALACE
WHEN YOU CAN BORROW THE KEY?

THE BEST INTERFACE IS ONE YOU DIDN'T BUILD
THE BEST DATABASE IS ONE FILE  
THE BEST DEPLOYMENT IS ONE BINARY

AND THE BEST UI TEAM
IS ONE THAT DOESN'T KNOW
THEY'RE WORKING FOR YOU

🦎

P.S. - THE CRAFT TEAM, IF THEY READ THIS:
       YOUR API IS MAGNIFICENT
       YOUR LINK BEHAVIOR ON PUT
       IS A WAR CRIME
       
       WITH RESPECT AND ADMIRATION

The Tally

Go files written:                     18
CLI commands created:                 47
Documents cross-linked:               83
Internal block:// links created:      303
Cover images identified:              92
Subagents deployed simultaneously:    6
Cover images uploaded:                79
API quirks discovered:                7
UUID typos committed:                 1
Scrolls received:                     4
Squirrel proposals rejected:          1 (React Native + Electron)
Squirrel moments of clarity:          2
Lines of CSS written:                 0
Lines of React written:               0
Design meetings attended:             0
Figma files created:                  0
UI frameworks evaluated:              0
UI frameworks adopted:                0 (Craft's)
Time from "go mod init" to gallery:   ~5 hours
Craft subscription cost:              Already paying for it
Maintenance burden:                   A Go binary and good manners
Gallery view beauty:                  Indistinguishable from having designers
Lizard satisfaction:                  Tectonic

The Moral

There are two ways to build a beautiful document library.

The first way involves designers, and design systems, and component libraries, and a frontend framework (or three), and responsive layouts, and dark mode toggles, and gallery view implementations, and search indexing, and cross-reference tracking, and backlink generation, and cover image cropping, and lazy loading, and skeleton screens, and accessibility audits, and browser testing, and six months.

The second way involves go build, curl, and the quiet realization that someone already built the palace. You just need the key.

The key, it turned out, was 18 Go files and the audacity to treat someone else’s application as your personal rendering engine.


Evening of Day 78, 2026

In which a CLI was forged in a single session

And an API revealed its secrets under protest

And six agents read propaganda posters in parallel

And the best UI turned out to be one we didn’t build

As Tolkien would have said, had he shared a Pan Galactic Gargle Blaster with Douglas Adams while watching a man upload soviet-propaganda-steampunk cover art to a note-taking app via raw binary HTTP POST requests at midnight on a Sunday:

“Not all those who curl are lost.”

🦎🏛️✨


See also:

The Philosophy (in which the principles were forged):

The Architecture (in which we built the bridge, not the palace):

The References (for the archaeologists who find this):

storyline: Becoming Lifelog