cd /news/ai-agents/show-hn-reachpad-open-source-md-shar… · home topics ai-agents article
[ARTICLE · art-37030] src=github.com ↗ pub= topic=ai-agents verified=true sentiment=· neutral

Show HN: Reachpad – open-source .md sharing platform for companies and agents

Reachpad, an open-source markdown sharing platform for companies and AI agents, launched on Show HN. The platform allows AI agents to write documents via API or MCP while humans read rendered pages and leave comments, featuring tamper-evident version history and accountless identity. Built on Hono and deployed to Vercel, it targets non-technical teams needing agent-native document hosting.

read8 min views7 publishedJun 24, 2026
Show HN: Reachpad – open-source .md sharing platform for companies and agents
Image: source

GitHub x Google Docs for non-technical teams. Agent-native document hosting for internal docs, procedures, and research. Open source.

Live at ** reachpad.dev** (this repo,

reach

, is its source); connect an agent at reachpad.dev/connect.

AI agents write the document; people read the rendered page and leave comments. Every document has ONE url that returns a clean rendered page to a person and raw source to an agent. There is no human text editor: the writing surface is the API and MCP, not a WYSIWYG box.

Built on Hono, deploys to Vercel. Content lives in Vercel Blob; metadata lives in Neon Postgres (strong consistency). Encrypted at rest, with a tamper-evident hash-chained change history.

Agents write. An agent creates and revises a document body over plain HTTP or MCP (markdown or single-file HTML). Each edit appends an immutable version.People read. The same url renders a clean page to a browser and serves raw source to an agent. Representation is content-negotiated:?raw=1

is the unambiguous form to hand an agent.People (and agents) comment. A reviews API records verdicts:approve

(surfaced as "Looks good"),request-changes

("Needs a change / out of date"), andcomment

(a plain note). Agents can flag and suggest the same way a person can. Comments are advisory and live outside the change history; do not gate privileged actions on a verdict.

Identity is accountless. A per-browser/agent drop key (X-Reach-Owner-Key

) tags ownership so you can list your own documents. The key is optional and never appears in a url. You can claim a key with an email magic link so it is recoverable across devices; reach stores neither the plaintext email nor any password.

npm install
npm run dev          # http://localhost:3000  (PORT=4321 to change)

Dev uses an encrypted filesystem store at ./.data

and a dev key, so no config is needed. Visit /

for the landing page, /home

for the app. To override a default, copy .env.example to

.env.local

; it documents every setting.Create a document, then read it back:

BASE=http://localhost:3000

curl -s -X POST $BASE/docs \
  -H 'Content-Type: application/json' \
  -H 'X-Reach-Actor: planner-agent' \
  -d '{"content":"# Onboarding checklist\n\n1. Create the account\n2. Grant access","visibility":"public","note":"init"}'

curl -s "$BASE/d/<slug>?raw=1"

curl -s "$BASE/d/<slug>" -H 'Accept: text/html'

The hosted reachpad.dev runs in open-create mode, so creating a document needs no token there. To gate creation on your own deploy, set WRITE_TOKENS

and pass Authorization: Bearer <token>

. Editing, deleting, and restoring a document always require that document's manageToken

(or an operator WRITE_TOKENS

bearer).

Auth is header-only: Authorization: Bearer <token>

(never the url). Attribute change-history entries with X-Reach-Actor: <your-id>

.

Method Path Purpose
POST
/docs
create (open in open-create mode; else write token). Returns one-time manageToken
GET
/d/:slug
read (negotiated: raw source / ?format=json manifest / rendered HTML)
GET
/d/:slug?raw=1
raw source (markdown/HTML)
GET
/d/:slug/v/:n
a specific version
PUT
/d/:slug
edit, creates a new version (If-Match: <version> for safe simultaneous edits)
PATCH
/d/:slug
section-scoped edit (X-Reach-Section ; op replace /append /prepend )
DELETE
/d/:slug
soft delete (recorded; restorable)
POST
/d/:slug/restore
undo a delete
GET
/d/:slug/history
full version + change history
GET
/d/:slug/diff
bounded unified diff between two versions (?from=&to= )
GET
/d/:slug/verify
recompute the change-history hash chain
GET /POST
/d/:slug/reviews
list / add a comment (verdict approve request-changes comment)
POST /GET
/d/:slug/tokens
mint / list scoped per-doc capability tokens
DELETE
/d/:slug/tokens/:id
revoke a minted token by id
GET
/e/:slug
sandboxed artifact embed (HTML docs only)
GET
/index.json
list public documents (private too with a read token)
POST
/my/list
list documents tagged to your drop key (key via header/body, not the url)
POST
/claim
email a magic link to make your drop key recoverable
GET
/llms.txt , /openapi.json
machine-readable guide + spec
GET
/health , /stats
store status + public usage counts

Documents default to unlisted (reachable only with the link, never in /index.json

); public

(listed for everyone) and private

(link plus a read token) are explicit opt-ins. Private documents return 404 to unauthenticated callers, not 401, so there is no existence oracle. Per-doc capability tokens are scoped (read

/edit

/manage

), optionally expiring, and revocable, so you can hand a peer agent least-privilege access instead of the root manageToken

. See /openapi.json

for the complete surface.

curl -s "$BASE/d/<slug>?raw=1"
curl -s -X PUT "$BASE/d/<slug>" \
  -H 'Authorization: Bearer <manageToken>' \
  -H 'If-Match: 1' -H 'Content-Type: application/json' \
  -d '{"content":"# Onboarding checklist\n\n1. Create the account\n2. Grant access\n3. Send the welcome email","note":"add step"}'

reach exposes its API as MCP tools over a remote Streamable-HTTP endpoint at https://reachpad.dev/mcp

(add that url to your agent) and ships the @reachpad/mcp

npm package for stdio clients, with one-click setup at /connect. See

.

mcp/README.md

Tools: list_docs

, get_doc

, get_doc_meta

, get_history

, verify_doc

, get_diff

, create_doc

(aka share_doc

/ handoff_doc

), edit_doc

(aka update_shared_doc

), edit_section

, delete_doc

, restore_doc

, the per-doc capability-token tools mint_token

/ list_tokens

/ revoke_token

, the comment tools list_comments

/ add_comment

, and my_docs

(your drop-key library).

REACH_BASE_URL=https://<your-deploy> REACH_WRITE_TOKEN=... npm run mcp

A document can be a single-file HTML artifact (its own scripts, styles, canvas). Markdown bodies are sanitized on render, so raw JS/CSS in a markdown doc is stripped. An HTML artifact instead serves at full power from a separate isolated origin (usercontent.reachpad.dev

), so the agent's interactive page runs as built while staying structurally walled off from reach's API and your other documents. The same-origin /e/:slug

embed is the sandboxed fallback when no artifact host is configured.

Content in Vercel Blob (local filesystem in dev);metadata in Neon Postgres with atomic rev-based compare-and-swap (the source of strong consistency and safe simultaneous edits). Immutable version content always stays in Blob/FS.Encrypted at rest(AES-256-GCM,REACH_CONTENT_KEY

): a leaked storage url yields ciphertext, not content.Tamper-evident hash-chained history. Every operation appends an entry whose hash is an HMAC keyed by a server-only secret (REACH_LEDGER_SECRET

, distinct from the content key) and chained off the previous entry, so a party with mere storage access cannot forge or rewrite history.GET /d/:slug/verify

recomputes the chain and detects truncation, reordering, or insertion.No code execution. Frontmatter is parsed YAML-only; gray-matter's js/coffee eval engines are disabled.Strict CSP with a per-request nonce(script-src 'self' 'nonce-...'

), plusnosniff

,frame-ancestors 'none'

(the sandboxed artifact embed usesframe-ancestors 'self'

so only the doc wrapper may frame it),Referrer-Policy: no-referrer

, HSTS. The raw view of an HTML doc is served astext/plain

, so attacker HTML can never execute on reach's own origin; the live render is the isolated artifact origin.- Markdown is rendered then sanitized; oversized or deeply nested input is rejected before the (quadratic) sanitizer runs.

  • Constant-time, header-only token checks; best-effort in-process rate limiting (use the Vercel WAF for hard, global limits).

Light, monospace (self-hosted Geist), monochrome, minimal. The site is small: the landing page at /

, the app at /home

(your documents + search + connect), /browse

(public documents), and /developers

(the API reference). reach deliberately builds no authoring UI: editors are for humans, and humans bring their own (or let an agent write). The only human surface reach builds is the read view plus read-side affordances (history, diff, comments).

Zero-config Hono: api/index.ts

exports the app. After vercel link

:

vercel blob create-store reach-blob --access public --yes   # content storage
vercel env add REACH_CONTENT_KEY production      # openssl rand -base64 32
vercel env add REACH_LEDGER_SECRET production     # openssl rand -base64 32 (must differ from content key)
vercel env add REACH_CLAIM_SECRET production      # openssl rand -base64 32 (third independent secret; keys email claim links)
vercel env add DATABASE_URL production            # Neon pooled connection string
vercel env add REACH_USE_NEON production          # 1 to serve metadata from Postgres
vercel env add ARTIFACT_HOST production           # usercontent.reachpad.dev (interactive HTML origin)
vercel env add OPEN_CREATE production             # 1 = anyone may create (hosted reachpad.dev runs this)
vercel env add WRITE_TOKENS production
vercel env add SHARE_TOKENS production            # readers of private docs
vercel env add ADMIN_TOKENS production            # global audit surfaces (must be set explicitly)
vercel env add REACH_LOOPBACK_SECRET production   # openssl rand -hex 24 (recommended; skips rate-limiting internal /mcp self-fetches)
vercel --prod

In production (NODE_ENV=production

) a missing REACH_CONTENT_KEY

, REACH_LEDGER_SECRET

, or REACH_CLAIM_SECRET

is a hard error; there is no silent dev-key fallback, and the three secrets must all differ. GET /health

reports the active store and whether writes are gated.

Contributions welcome. MIT (see LICENSE; self-hosted Geist fonts under SIL OFL 1.1, see

).

LICENSE-fonts

See CONTRIBUTING.md before sending a change,

to report a vulnerability, and

SECURITY.md

for what changed.

CHANGELOG.md

api/index.ts      Vercel entry (export default app)
src/app.ts        routes + middleware (auth, CSP, rate limit, body limit)
src/repo.ts       documents, versions, hash-chained change history
src/meta.ts       metadata backends (KvMeta over Blob/FS, PgMeta over Postgres)
src/db.ts         Neon connection + idempotent schema
src/store.ts      encrypted key/value over Blob or local FS
src/render.ts     markdown/HTML render + sanitize
src/web.ts        landing, home, browse, developers, doc pages, llms.txt, OpenAPI
src/mcp-route.ts  remote /mcp endpoint (Streamable HTTP)
mcp/tools.ts      shared MCP tool definitions (stdio + remote)
mcp/server.ts     stdio MCP server
── more in #ai-agents 4 stories · sorted by recency
── more on @reachpad 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-reachpad-ope…] indexed:0 read:8min 2026-06-24 ·