share.benroberts.ai

A private publishing platform where a reader's comment becomes an agent's next task.

● LIVE STARTED JUL 2026 VERCEL SERVERLESS (TYPESCRIPT) · SUPABASE · GITHUB API · VITEST
~20s
Publish time

What this is

A hidden subdomain where I publish self-contained HTML and hand someone the link. Artifacts, mini-apps, one-off tools, a P&L teaching guide, a workout plan for a friend. Each is a folder with an unguessable slug and an index.html. Commit, push, live in about twenty seconds.

It replaced a habit I'd rather not admit to: emailing PDFs and screenshots of things that should have been web pages.

Three pieces make it a platform rather than a folder. A PIN-gated hub lists every drop with its dates, version count, and unresolved comments, and is never linked from any drop. Versions come free, because git history is the version store; any past commit of a drop serves at a versioned URL, retroactively, with zero publish discipline. And every drop carries a comment widget where a reader types a name and a comment, no account.

That last one is where it stops being a static host. Those comments land in Supabase, get pulled by a job key at the start of my next Claude Code session, and become the thing I fix. Reader says the chart is wrong on their phone. Next session opens with that comment in front of me, I fix the drop, push, resolve.

Why it's built this way

The obvious alternative was a CMS, or Notion, or letting Claude host the artifact. I rejected all three for the same reason: I wanted a URL I own, at a hostname that's mine, with no login for the person I'm sharing with and no signup for them to comment.

Privacy by unguessable slug instead of authentication was a deliberate trade. A login on every drop means every friend and family member gets an account, a password, a reset flow, and a reason to give up. Unguessable slug plus robots.txt Disallow plus a noindex, nofollow header plus a per-page robots meta tag makes the page unfindable but frictionless. When something genuinely needs a lock, that drop gets the encrypted gate: content AES-256-GCM encrypted, key derived from a PIN, payload unreadable in page source. Nobody gets a "real" login. One drop gets real cryptography.

Versions were the choice I'm happiest with. I could have built a version table, a publish flow, a "save as new version" button... and then failed to use it. Deriving history from commits meant that the day the feature shipped, a drop already had four browsable versions going back weeks. Retroactive on arrival.

The build log, failures left in

2026-07-03

— Stood up. First drop was a workout plan for a friend. That's the whole origin story.

2026-07-11

— First PIN-locked drop, a P&L field guide with live accounting data in it. Encrypted payload, PBKDF2 key from a 4-digit PIN.

2026-07-11

— v1.0.0 of the platform shell: hub, git-derived versions, comments on every drop, agent list/resolve loop wired into session start.

2026-07-21 · THE RIP-OUT

A drop's embedded video wouldn't play. Vercel serves every drop at both /slug and /slug/ with no redirect between them, so a relative asset path resolves on one form and 404s on the other... and the slash-less form is the one that circulates in links. Worse was how it passed verification: I'd been curling asset URLs directly, which proves the file is hosted and proves nothing about the page. Two rules came out of it, both standing. Absolute asset paths always. And verify through the page: fetch the live slash-less URL, extract the src and href attributes it actually emitted, check those.

⚠ What would break

DNS is manual. benroberts.ai sits at a registrar with no wildcard record and no API credentials on hand, so every new subdomain is a hand-entered A record. Fine for one a year. Bad the day I want ten.

The unguessable slug is the whole security model. Anyone with the link is in. Forwarded, pasted into a group chat, synced across someone's browsers... the drop is as public as they make it. Accepted risk for a workout plan, wrong for anything real, which is why the encrypted gate exists. Choosing right for a given drop is a judgment call I make every time, not a setting that protects me.

What I learned

The interesting part of a publishing platform turned out not to be publishing. It was the return path. A comment box only humans read is a comment box. A comment box whose contents are waiting in my agent's session-start context is a work queue, and the difference cost maybe two hundred lines.

Where AI was confidently wrong: the verification of that video drop. Every check reported green. The checks were real, they ran, they passed, and they were checking the wrong object, the asset instead of the page referencing it. Nothing lied. The scope was wrong, and confident green on a wrong-scoped check is worse than a red, because it stops the investigation. A human opened the link and saw a dead player.

What this demonstrates

Surfaces where the feedback loop closes back into the agent instead of dead-ending in a human's inbox. Derived state (git history) over maintained state (a version table), so a feature can't rot from disuse. A privacy model chosen to fit the reader.

The shift in how I work: publishing stopped being an event. Anything worth handing someone is a push, and anything they say about it comes back as an item.