惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园_首页
H
Help Net Security
腾讯CDC
宝玉的分享
宝玉的分享
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LangChain Blog
爱范儿
爱范儿
T
The Blog of Author Tim Ferriss
J
Java Code Geeks
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
MyScale Blog
MyScale Blog
Engineering at Meta
Engineering at Meta
N
Netflix TechBlog - Medium
D
Docker
V
V2EX
Last Week in AI
Last Week in AI
G
Google Developers Blog
IT之家
IT之家
C
Check Point Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
人人都是产品经理
人人都是产品经理
博客园 - 叶小钗
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
博客园 - 聂微东

Hacker News - Newest: "AI"

AI can't read an investor deck AI as an attorney? Student uses ChatGPT, Gemini to sue UW over alleged racial discrimination Hacking MCP Servers in AI Systems – The Rug Pull: Tool Changes After Approval GitHub - MeepCastana/KubeezCut: Free Web based video editor Can AI judge journalism? A Thiel-backed startup says yes, even if it risks chilling whistleblowers Coming soon: 10 Things That Matter in AI Right Now DARPA built an AI to fact-check enemy weapons claims What explains heterogeneity in AI adoption? When AI Meets Muscle: Context-Aware Electrical Stimulation Promises a New Way to Guide Human Movements - Department of Computer Science AI Changed How We Build. It Did Not Change What Matters. Linux rules on using AI-generated code - Copilot is OK, but humans must take 'full responsibility for the… Meta spins up AI version of Mark Zuckerberg to engage with employees Code Mode: Let Your AI Write Programs, Not Just Call Tools | TanStack Blog GitHub - Delavalom/graft: Go framework for building AI agents. Type-safe tools, multi-provider (OpenAI, Anthropic, Gemini, Bedrock), zero vendor SDKs. India's TCS tops estimates, says new AI models did not dent services demand Gen Z's fading AI hype Strong feeling: we are in a folded AI reality GitHub - machinarii/total-recall-catalog: A reference catalog of latest knowledge retrieval, memory & RAG systems GitHub - mensfeld/code-on-incus: Give each AI agent its own isolated machine with root, Docker, and systemd. Active defense detects and stops threats automatically.. Quantization, LoRA, and the 8% Problem: Benchmarking Local LLMs for Production AI Iran war: We spoke to the man making Lego-style AI videos that experts say are powerful propaganda Powell, Bessent discussed Anthropic's Mythos AI cyber threat with major U.S. banks GitHub - immartian/bellamem: Persistent belief-graph memory for AI agents. Retrieves decisive context by importance — not recency, not RAG, not /compact. recursive-mode: The Repo-Native Operating System for AI Engineering After the attack on Sam Altman's home, will AI CEO's go on the offensive? The biggest advance in AI since the LLM Opus 4.6 vs GPT 5.4 One Prompt Unity World Generation Test “AI polls” are fake polls Client Challenge Can AI be a 'child of God'? Inside Anthropic's meeting with Christian leaders
Publishing AI Write-ups to a URL - Without Leaking the Cl...
Brady Stroud · 2026-06-23 · via Hacker News - Newest: "AI"

A lot of my work throws off short-lived write-ups - an investigation into a bug, a plan for a migration, a status report, a quick dashboard. Not documentation exactly; more the paper trail of figuring something out.

These days I have an AI agent turn each one into a single self-contained HTML file when the work's done, then publish it to a URL I can drop in a PR or send to someone. The "make it shareable" step is one command.

That worked great - right up until I noticed some of those write-ups were client work, sitting on a public URL.

This is the whole story: how I publish them, and how I rebuilt the publishing side to be private by default.

One command to publish

The starting point was a tiny CLI, publish-plan:

publish-plan ./q3-report.html
# -> https://docs.stroud.dev/q3-report

It uploads the HTML to a Cloudflare R2 bucket and prints a clean URL. That's it.

The reason it matters is that an agent can call it itself. I can end a task with "...and publish the report," and the agent hands me back a link. No copy-pasting markdown into a doc tool, no screenshots - a real rendered page at a real URL.

HTML plus a URL beats a wall of markdown in a chat window every time. It renders properly, it sticks around, and I can send it to someone who never opens a terminal.

The magic: it's a skill, not just a CLI

Here's the part that makes it feel automatic. publish-plan isn't only a command I run by hand - it's an agent skill. A short SKILL.md teaches the agent how to publish: the command, how the namespaces work, and the privacy rules.

So I never have to explain any of it. The agent finishes a job, decides the result is worth keeping, reads the skill, and runs the right command - dropping the write-up into the correct namespace with the correct visibility. "Publish this, and keep client work private" is knowledge the agent already has.

Which is exactly why the next part matters. If the agent is the one pulling the trigger, privacy can't be a polite suggestion in a prompt - it has to be enforced by the system itself.

The problem: everything was public

Under the hood it was the simplest thing that could work: an R2 bucket served on a custom domain. Upload an object, it's live at docs.stroud.dev/<key>.

Two things bugged me:

  1. I wanted the root to list everything I'd published. But R2 can't list objects from the CLI, and a bucket on a custom domain won't serve an index page - so the root just 404'd.

  2. More importantly: anything I published was reachable by anyone with the URL. Not indexed by Google (I'd set noindex), but reachable. And some of those were client work.

"Reachable by URL" is fine for a plan I want to share. It is not fine for a client's work. I needed real access control, not security-by-obscurity.

A Worker as the gatekeeper

The fix was to stop serving the bucket directly and put a small Cloudflare Worker in front of it. Now every request runs through code that asks one question: is this person allowed to see this?

That one move turns dumb object storage into a real app:

  • the root can list the bucket and render a dashboard (the Worker can list, even though the CLI can't),

  • and nothing is served until it passes a visibility check.

Default answer: no. Private unless something says otherwise.

Privacy as config, sitting next to the work

I didn't want to think about privacy per file. I want to publish and have the right thing happen.

So privacy is declared once, in the repo. Each project - or a whole client folder - carries a docs.json:

// docs.json
{ "path": "client-work", "visibility": "private" }

When I publish, the CLI walks up from the current directory, finds the nearest docs.json, and uses it. A client folder is marked private; my personal stuff is public. The doc lands under that namespace with that visibility - automatically.

cd ~/dev/some-client-project
publish-plan ./findings.html audits/2026/q3
# -> https://docs.stroud.dev/client-work/audits/2026/q3   (private)

The first path segment is the namespace, and the namespace is the privacy boundary. Client work can't end up public by accident, because the folder it's published from already says private.

This is the other half of the skill: the skill knows to publish, and docs.json decides where and how private. Between them, the agent does the right thing without me in the loop.

Three levels

Every doc resolves to one of three:

  • private - only me

  • public - anyone

  • link - anyone holding a share link

The Worker resolves it in order: a valid share link → a per-doc override → the namespace policy → otherwise deny. "Otherwise deny" is the important bit. A stray file with no policy is private, never public.

Signing in with a passkey

I'm the only owner, and I really didn't want to type a password every time. So owner access is a passkey - Touch ID, basically.

The fun part: there's no auth library in here. WebAuthn inside a Cloudflare Worker turns out to be very doable by hand. On registration the browser hands you the public key in a standard format; on login you verify the signature with the built-in WebCrypto API:

const key = await crypto.subtle.importKey(
  "spki", publicKey, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]
);
const ok = await crypto.subtle.verify(
  { name: "ECDSA", hash: "SHA-256" }, key, signature, signedData
);

There's a little dance to convert the signature format and to keep a challenge around between the two steps, but that's the whole shape of it. No dependencies.

The result: I open docs.stroud.dev, Touch ID, and I see a dashboard of everything - grouped by namespace, each tagged private / public / link.

Sharing one private doc

Private-by-default is great until you actually want to send someone a private doc. For that I mint a share link: a signed, expiring token in the URL.

publish-plan share client-work/audits/2026/q3 7d
# -> https://docs.stroud.dev/client-work/audits/2026/q3?k=<token>

It's stateless - there's no database of links. The token is the grant: it says "this doc, until this time," signed with a secret only the Worker and my CLI know. The Worker checks the signature and the expiry, drops a scoped cookie, and serves the doc. When it expires, it stops working.

A nice little detail: if the doc is already public, sharing just gives you the plain URL - a token would be pointless.

Where it landed

Nothing changed about the day-to-day: an agent writes a doc, I run one command, I get a link.

What changed is everything underneath:

  • client docs are private by default, gated by a passkey,

  • public is opt-in, per namespace, declared in the repo,

  • and sharing is a deliberate act that mints an expiring link - not the default state of the world.

The lesson I keep relearning: privacy works best when it lives next to the work - a file in the repo, not a setting I have to remember - and a tiny Worker in front of a bucket is enough to turn "a pile of files on a URL" into something I'd actually trust with client work.