Reach — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Reach (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
GitHub x Google Docs for non-technical teams. Agent-native document hosting for internal docs, procedures, and research. Open source.
Live at [reachpad.dev](https://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.
or MCP (markdown or single-file HTML). Each edit appends an immutable version.
source to an agent. Representation is content-negotiated: ?raw=1 is the unambiguous form to hand an agent.
approve(surfaced as "Looks good"), request-changes ("Needs a change / out of date"), and comment (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
# create (returns slug + a one-time manageToken to edit/delete later)
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"}'
# read raw source (what you hand an agent)
curl -s "$BASE/d/<slug>?raw=1"
# read the rendered page (what a person sees)
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.
# read raw, then edit the next version with safe simultaneous edits
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).
# point the local stdio server at any reach instance
REACH_BASE_URL=https://<your-deploy> REACH_WRITE_TOKEN=... npm run mcpA 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.
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.
REACH_CONTENT_KEY): a leaked storage urlyields ciphertext, not content.
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.
js/coffee eval engines are disabled.
script-src 'self' 'nonce-...'), plusnosniff, frame-ancestors 'none' (the sandboxed artifact embed uses frame-ancestors 'self' so only the doc wrapper may frame it), Referrer-Policy: no-referrer, HSTS. The raw view of an HTML doc is served as text/plain, so attacker HTML can never execute on reach's own origin; the live render is the isolated artifact origin.
rejected before the (quadratic) sanitizer runs.
(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)
# optional: gate creation/writes and private reads
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 --prodIn 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, SECURITY.md to report a vulnerability, and CHANGELOG.md for what changed.
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~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.