Find the pages your site forgot — and the exact paragraph that should link to them.
Orphan-page rescue and internal-link insertion for serious WordPress and Webflow sites. Built entirely on the Cloudflare developer platform.
A self-hosted, open-source alternative to Link Whisper and InLinks — your data, your AI key, no per-seat SaaS bill.
Landing → workbench → ranked orphans → audit log. Real UI, seeded data.
A growing site quietly accumulates orphan pages — published URLs that nothing else links to. Search engines struggle to find them, and the traffic they could earn leaks away.
recto reads every page you have published, finds the orphans, ranks them by lost Google Search Console impressions, and shows you the exact paragraph on another page where an internal link belongs. You approve each suggestion; recto pushes the link through your CMS's REST API so it lives in the published HTML and survives even if you stop using the tool.
- You stay in the seat. recto suggests, you approve. Every change is yours.
- Ranked by real traffic. Orphans sorted by GSC impressions, not page depth.
- Links that persist. Pushed via the CMS API into published HTML — no shadow DB, no lock-in.
- Bring your own key. Paste an OpenAI/Anthropic key and route anchor generation through it.
- Connect a site — WordPress Application Password or Webflow API key.
- Connect Google Search Console (optional) — ranks orphans by traffic.
- Read the site — one crawl pass, up to 10,000 pages.
- Surface the orphans — ranked list, most valuable first.
- Insert the link — pick a candidate paragraph; recto pushes it through the CMS API.
- Re-read on a schedule — weekly by default; new orphans surface as you publish.
This is a monorepo with two top-level packages:
.
├── recto/ # The backend — pnpm/Cloudflare monorepo
│ ├── apps/
│ │ ├── workers/api/ # Hono API on Cloudflare Workers
│ │ └── web/ # static Pages bundle (mirror of recto-ui)
│ ├── packages/
│ │ ├── db/ # Drizzle (D1 dialect) schema + migrations
│ │ ├── shared/ # zod schemas, constants, shared types
│ │ └── eslint-recto/ # brand-voice + hook-pattern lint rules
│ ├── load/ # k6 load scripts
│ ├── scripts/ # smoke / integration / fuzz / deploy
│ └── ops/runbooks/ # operational runbooks (e.g. GSC OAuth setup)
└── recto-ui/ # The frontend — audited static UI (landing + app shell)
├── app/ # signed-in app screens (vanilla HTML/CSS/JS)
├── styles/ # design tokens + components
└── tests/ # Playwright persona, a11y, and visual-baseline gates
Cloudflare-only. No servers, no containers.
| Concern | Cloudflare primitive |
|---|---|
| API | Workers (Hono) |
| Relational data (OLTP) | D1 (SQLite) |
| Embeddings / similarity | Vectorize |
| Sessions, rate limits, ETag cache | KV |
| Cold blobs (audit archive) | R2 |
| Async jobs (crawl / embed / push / verify / email) | Queues |
| Scheduled work (GSC sync, weekly digest, link reverify) | Cron Triggers |
| Embedding + anchor-generation floor | Workers AI (BGE, Llama 3.1 8B) |
| JS-rendered pages | Browser Rendering |
| Crawl progress (SSE) | Durable Objects |
| Transactional email | Emailit → Resend → MailChannels fallback |
The landing page:
The signed-in app — every link is suggested, you approve each one. The audit log shows the verify-state legend for every CMS push:
The app screens (docs/screenshots/app-*.png) are captured from the real UI
against a seeded local backend — exactly what you get after pnpm setup. The
example site, orphan rankings, and audit history are demo seed data.
Prerequisites: Node ≥ 20, pnpm 9, and a free Cloudflare account.
wrangleris already a dev dependency —pnpm installbrings it in, no global install needed.
git clone https://github.com/eikiyo/recto.git && cd recto/recto && pnpm setupThat one line does everything: it auto-generates local dev secrets (so the
app boots with zero key-pasting), opens .dev.vars in case you want to add
real GSC/Emailit keys, installs the whole workspace, creates + migrates the local
D1 database, and launches the API on http://localhost:8787 and the UI on
http://localhost:8765 together (Ctrl-C stops both).
First run,
pnpm installdownloads the toolchain (wrangler, drizzle, playwright) — that's a one-time few minutes. After it, boot is seconds. The only thing you ever have to type is that one command.
Prefer to run the steps yourself?
pnpm install
cp apps/workers/api/.dev.vars.example apps/workers/api/.dev.vars # then fill in keys
pnpm --filter @recto/api db:migrate:local # applies the committed migrations
pnpm dev # both servers; or pnpm dev:api / pnpm dev:web separatelyNote:
db:generateis a maintainer-only step — run it only when you changeschema.ts. The repo ships curated migrations, so a fresh clone just applies them withdb:migrate:local.
Health check:
curl http://localhost:8787/api/health
# → {"status":"ok","checks":{"env":"dev","db":"bound","kv":"bound",...,"dbPing":"ok"},"ts":...}To deploy your own instance, create the Cloudflare resources and paste their IDs into recto/apps/workers/api/wrangler.toml (search for REPLACE_WITH_):
cd recto
pnpm exec wrangler d1 create recto
pnpm exec wrangler kv namespace create KV
# then set production secrets:
pnpm exec wrangler secret put RECTO_KEK
pnpm exec wrangler secret put MAGIC_LINK_SECRET
# …etc (see apps/workers/api/.dev.vars.example for the full list)All runtime configuration is environment-driven. The single source of truth is
recto/apps/workers/api/.dev.vars.example,
which documents every variable the Worker reads and where to obtain it. Non-secret
bindings (D1, KV, Queues, R2, Vectorize, AI) are declared in
recto/apps/workers/api/wrangler.toml.
The Google Search Console OAuth setup is the one manual step — a full walkthrough lives in
recto/ops/runbooks/gsc-setup.md.
cd recto
pnpm --filter @recto/api typecheck # strict TypeScript
pnpm --filter @recto/api lint # ESLint + brand-voice rules
pnpm --filter @recto/api test # vitest: unit, property, chaos
bash scripts/smoke.sh # API smoke test (needs wrangler dev running)The frontend ships its own audited Playwright suite (persona simulations,
accessibility, and visual-baseline snapshots) under recto-ui/tests/.
Issues and pull requests are welcome. Please read CONTRIBUTING.md and the Code of Conduct first. Security reports go through the process in SECURITY.md.
MIT © 2026 Eikiyo

