{"slug": "show-hn-reachpad-open-source-md-sharing-platform-for-companies-and-agents", "title": "Show HN: Reachpad – open-source .md sharing platform for companies and agents", "summary": "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.", "body_md": "**GitHub x Google Docs for non-technical teams.** Agent-native document hosting\nfor internal docs, procedures, and research. Open source.\n\nLive at ** reachpad.dev** (this repo,\n\n`reach`\n\n, is its\nsource); connect an agent at [reachpad.dev/connect](https://reachpad.dev/connect).\n\nAI 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.\n\nBuilt 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.\n\n**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`\n\nis the unambiguous form to hand an agent.**People (and agents) comment.** A reviews API records verdicts:`approve`\n\n(surfaced as \"Looks good\"),`request-changes`\n\n(\"Needs a change / out of date\"), and`comment`\n\n(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.\n\nIdentity is accountless. A per-browser/agent **drop key** (`X-Reach-Owner-Key`\n\n)\ntags ownership so you can list your own documents. The key is optional and never\nappears in a url. You can **claim** a key with an email magic link so it is\nrecoverable across devices; reach stores neither the plaintext email nor any\npassword.\n\n```\nnpm install\nnpm run dev          # http://localhost:3000  (PORT=4321 to change)\n```\n\nDev uses an encrypted filesystem store at `./.data`\n\nand a dev key, so no config\nis needed. Visit `/`\n\nfor the landing page, `/home`\n\nfor the app. To override a\ndefault, copy [ .env.example](/las7/reach/blob/main/.env.example) to\n\n`.env.local`\n\n; it documents\nevery setting.Create a document, then read it back:\n\n```\nBASE=http://localhost:3000\n\n# create (returns slug + a one-time manageToken to edit/delete later)\ncurl -s -X POST $BASE/docs \\\n  -H 'Content-Type: application/json' \\\n  -H 'X-Reach-Actor: planner-agent' \\\n  -d '{\"content\":\"# Onboarding checklist\\n\\n1. Create the account\\n2. Grant access\",\"visibility\":\"public\",\"note\":\"init\"}'\n\n# read raw source (what you hand an agent)\ncurl -s \"$BASE/d/<slug>?raw=1\"\n\n# read the rendered page (what a person sees)\ncurl -s \"$BASE/d/<slug>\" -H 'Accept: text/html'\n```\n\nThe hosted reachpad.dev runs in open-create mode, so creating a document needs\nno token there. To gate creation on your own deploy, set `WRITE_TOKENS`\n\nand pass\n`Authorization: Bearer <token>`\n\n. Editing, deleting, and restoring a document\nalways require that document's `manageToken`\n\n(or an operator `WRITE_TOKENS`\n\nbearer).\n\nAuth is **header-only**: `Authorization: Bearer <token>`\n\n(never the url).\nAttribute change-history entries with `X-Reach-Actor: <your-id>`\n\n.\n\n| Method | Path | Purpose |\n|---|---|---|\n`POST` |\n`/docs` |\ncreate (open in open-create mode; else write token). Returns one-time `manageToken` |\n`GET` |\n`/d/:slug` |\nread (negotiated: raw source / `?format=json` manifest / rendered HTML) |\n`GET` |\n`/d/:slug?raw=1` |\nraw source (markdown/HTML) |\n`GET` |\n`/d/:slug/v/:n` |\na specific version |\n`PUT` |\n`/d/:slug` |\nedit, creates a new version (`If-Match: <version>` for safe simultaneous edits) |\n`PATCH` |\n`/d/:slug` |\nsection-scoped edit (`X-Reach-Section` ; op `replace` /`append` /`prepend` ) |\n`DELETE` |\n`/d/:slug` |\nsoft delete (recorded; restorable) |\n`POST` |\n`/d/:slug/restore` |\nundo a delete |\n`GET` |\n`/d/:slug/history` |\nfull version + change history |\n`GET` |\n`/d/:slug/diff` |\nbounded unified diff between two versions (`?from=&to=` ) |\n`GET` |\n`/d/:slug/verify` |\nrecompute the change-history hash chain |\n`GET` /`POST` |\n`/d/:slug/reviews` |\nlist / add a comment (`verdict` approve|request-changes|comment) |\n`POST` /`GET` |\n`/d/:slug/tokens` |\nmint / list scoped per-doc capability tokens |\n`DELETE` |\n`/d/:slug/tokens/:id` |\nrevoke a minted token by id |\n`GET` |\n`/e/:slug` |\nsandboxed artifact embed (HTML docs only) |\n`GET` |\n`/index.json` |\nlist public documents (private too with a read token) |\n`POST` |\n`/my/list` |\nlist documents tagged to your drop key (key via header/body, not the url) |\n`POST` |\n`/claim` |\nemail a magic link to make your drop key recoverable |\n`GET` |\n`/llms.txt` , `/openapi.json` |\nmachine-readable guide + spec |\n`GET` |\n`/health` , `/stats` |\nstore status + public usage counts |\n\nDocuments default to **unlisted** (reachable only with the link, never in\n`/index.json`\n\n); `public`\n\n(listed for everyone) and `private`\n\n(link plus a read\ntoken) are explicit opt-ins. Private documents return **404** to unauthenticated\ncallers, not 401, so there is no existence oracle. Per-doc capability tokens are\nscoped (`read`\n\n/`edit`\n\n/`manage`\n\n), optionally expiring, and revocable, so you can\nhand a peer agent least-privilege access instead of the root `manageToken`\n\n. See\n`/openapi.json`\n\nfor the complete surface.\n\n```\n# read raw, then edit the next version with safe simultaneous edits\ncurl -s \"$BASE/d/<slug>?raw=1\"\ncurl -s -X PUT \"$BASE/d/<slug>\" \\\n  -H 'Authorization: Bearer <manageToken>' \\\n  -H 'If-Match: 1' -H 'Content-Type: application/json' \\\n  -d '{\"content\":\"# Onboarding checklist\\n\\n1. Create the account\\n2. Grant access\\n3. Send the welcome email\",\"note\":\"add step\"}'\n```\n\nreach exposes its API as MCP tools over a remote Streamable-HTTP endpoint at\n`https://reachpad.dev/mcp`\n\n(add that url to your agent) and ships the\n`@reachpad/mcp`\n\nnpm package for stdio clients, with one-click setup at\n[ /connect](https://reachpad.dev/connect). See\n\n[.](/las7/reach/blob/main/mcp/README.md)\n\n`mcp/README.md`\n\nTools: `list_docs`\n\n, `get_doc`\n\n, `get_doc_meta`\n\n, `get_history`\n\n, `verify_doc`\n\n,\n`get_diff`\n\n, `create_doc`\n\n(aka `share_doc`\n\n/ `handoff_doc`\n\n), `edit_doc`\n\n(aka\n`update_shared_doc`\n\n), `edit_section`\n\n, `delete_doc`\n\n, `restore_doc`\n\n, the per-doc\ncapability-token tools `mint_token`\n\n/ `list_tokens`\n\n/ `revoke_token`\n\n, the comment\ntools `list_comments`\n\n/ `add_comment`\n\n, and `my_docs`\n\n(your drop-key library).\n\n```\n# point the local stdio server at any reach instance\nREACH_BASE_URL=https://<your-deploy> REACH_WRITE_TOKEN=... npm run mcp\n```\n\nA document can be a single-file HTML artifact (its own scripts, styles, canvas).\nMarkdown bodies are sanitized on render, so raw JS/CSS in a markdown doc is\nstripped. An HTML artifact instead serves at full power from a **separate\nisolated origin** (`usercontent.reachpad.dev`\n\n), so the agent's interactive page\nruns as built while staying structurally walled off from reach's API and your\nother documents. The same-origin `/e/:slug`\n\nembed is the sandboxed fallback when\nno artifact host is configured.\n\n**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`\n\n): 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`\n\n, 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`\n\nrecomputes 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-...'`\n\n), plus`nosniff`\n\n,`frame-ancestors 'none'`\n\n(the sandboxed artifact embed uses`frame-ancestors 'self'`\n\nso only the doc wrapper may frame it),`Referrer-Policy: no-referrer`\n\n, HSTS. The raw view of an HTML doc is served as`text/plain`\n\n, 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.\n- Constant-time, header-only token checks; best-effort in-process rate limiting (use the Vercel WAF for hard, global limits).\n\nLight, monospace (self-hosted Geist), monochrome, minimal. The site is small:\nthe landing page at `/`\n\n, the app at `/home`\n\n(your documents + search + connect),\n`/browse`\n\n(public documents), and `/developers`\n\n(the API reference). reach\ndeliberately builds no authoring UI: editors are for humans, and humans bring\ntheir own (or let an agent write). The only human surface reach builds is the\nread view plus read-side affordances (history, diff, comments).\n\nZero-config Hono: `api/index.ts`\n\nexports the app. After `vercel link`\n\n:\n\n```\nvercel blob create-store reach-blob --access public --yes   # content storage\nvercel env add REACH_CONTENT_KEY production      # openssl rand -base64 32\nvercel env add REACH_LEDGER_SECRET production     # openssl rand -base64 32 (must differ from content key)\nvercel env add REACH_CLAIM_SECRET production      # openssl rand -base64 32 (third independent secret; keys email claim links)\nvercel env add DATABASE_URL production            # Neon pooled connection string\nvercel env add REACH_USE_NEON production          # 1 to serve metadata from Postgres\nvercel env add ARTIFACT_HOST production           # usercontent.reachpad.dev (interactive HTML origin)\n# optional: gate creation/writes and private reads\nvercel env add OPEN_CREATE production             # 1 = anyone may create (hosted reachpad.dev runs this)\nvercel env add WRITE_TOKENS production\nvercel env add SHARE_TOKENS production            # readers of private docs\nvercel env add ADMIN_TOKENS production            # global audit surfaces (must be set explicitly)\nvercel env add REACH_LOOPBACK_SECRET production   # openssl rand -hex 24 (recommended; skips rate-limiting internal /mcp self-fetches)\nvercel --prod\n```\n\nIn production (`NODE_ENV=production`\n\n) a missing `REACH_CONTENT_KEY`\n\n,\n`REACH_LEDGER_SECRET`\n\n, or `REACH_CLAIM_SECRET`\n\nis a hard error; there is no silent\ndev-key fallback, and the three secrets must all differ. `GET /health`\n\nreports the\nactive store and whether writes are gated.\n\nContributions welcome. MIT (see [ LICENSE](/las7/reach/blob/main/LICENSE); self-hosted Geist fonts\nunder SIL OFL 1.1, see\n\n[).](/las7/reach/blob/main/LICENSE-fonts)\n\n`LICENSE-fonts`\n\nSee [ CONTRIBUTING.md](/las7/reach/blob/main/CONTRIBUTING.md) before sending a change,\n\n[to report a vulnerability, and](/las7/reach/blob/main/SECURITY.md)\n\n`SECURITY.md`\n\n[for what changed.](/las7/reach/blob/main/CHANGELOG.md)\n\n`CHANGELOG.md`\n\n```\napi/index.ts      Vercel entry (export default app)\nsrc/app.ts        routes + middleware (auth, CSP, rate limit, body limit)\nsrc/repo.ts       documents, versions, hash-chained change history\nsrc/meta.ts       metadata backends (KvMeta over Blob/FS, PgMeta over Postgres)\nsrc/db.ts         Neon connection + idempotent schema\nsrc/store.ts      encrypted key/value over Blob or local FS\nsrc/render.ts     markdown/HTML render + sanitize\nsrc/web.ts        landing, home, browse, developers, doc pages, llms.txt, OpenAPI\nsrc/mcp-route.ts  remote /mcp endpoint (Streamable HTTP)\nmcp/tools.ts      shared MCP tool definitions (stdio + remote)\nmcp/server.ts     stdio MCP server\n```\n\n", "url": "https://wpnews.pro/news/show-hn-reachpad-open-source-md-sharing-platform-for-companies-and-agents", "canonical_source": "https://github.com/las7/reach", "published_at": "2026-06-24 00:56:27+00:00", "updated_at": "2026-06-24 01:15:36.270128+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Reachpad", "Vercel", "Neon", "Hono", "GitHub", "Google Docs"], "alternates": {"html": "https://wpnews.pro/news/show-hn-reachpad-open-source-md-sharing-platform-for-companies-and-agents", "markdown": "https://wpnews.pro/news/show-hn-reachpad-open-source-md-sharing-platform-for-companies-and-agents.md", "text": "https://wpnews.pro/news/show-hn-reachpad-open-source-md-sharing-platform-for-companies-and-agents.txt", "jsonld": "https://wpnews.pro/news/show-hn-reachpad-open-source-md-sharing-platform-for-companies-and-agents.jsonld"}}