Text-mode web fetch proxy for the VectoJS WebOS
Browser app. A Cloudflare Worker that fetches a URL server-side, strips HTML
to plain text, and returns { title, text } with permissive CORS.
GET https://proxy.vectojs.org/?url=<encoded-url>
Response:
{
"url": "https://example.com/",
"title": "Example Domain",
"text": "Example Domain\n\nThis domain is for use…",
"truncated": false
}- Only
http:/https:targets are allowed. - A hostname blocklist rejects private/loopback/link-local/CLI-metadata hosts (SSRF guard). It does not resolve DNS, so a public hostname resolving to a private IP is not caught — demo-scoped, not a general open proxy.
- The upstream body is streamed and capped at 2 MiB (the reader is cancelled at
the ceiling, so memory never exceeds it); text output is capped at 8000 chars
and
truncated: trueflags a page that hit the ceiling. Content is constrained to text/* and html types, and nav/header/footer/aside/form blocks are dropped before extraction so article text is not crowded out.
bun install
bun run dev # local: http://localhost:8787
bun run check # tsc --noEmit
bun run lint # oxlint
bun run deploy # wrangler deploy (route: proxy.vectojs.org)Push to main → CI runs verify then deploy (wrangler) to the
webos-proxy worker. Requires the CLOUDFLARE_API_TOKEN +
CLOUDFLARE_ACCOUNT_ID repo secrets (org-level).
The proxy.vectojs.org hostname is a Workers custom domain attached
one-time to the webos-proxy service (the token lacks Zone Workers Routes
scope, so a zone route in wrangler.toml is not used). The attachment
auto-created the proxied DNS record on the vectojs.org zone. To re-attach
(or move) it:
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/workers/domains" \
-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"hostname":"proxy.vectojs.org","service":"webos-proxy","environment":"production","zone_id":"e221cc18a57003bb6bef3d34605ffb1e"}'