Add hosted captun.sh safety controls#22
Merged
Merged
Conversation
commit: |
…05/26/hosted-safety-on-gateway
…05/26/hosted-safety-on-gateway
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d41f629. Configure here.
mmkal
added a commit
that referenced
this pull request
May 26, 2026
## Summary Adds the hosted `captun.sh` path so a first-time user can get a public tunnel without deploying anything first. ```bash # with something listening on localhost:3000 npx captun 3000 ``` The CLI now defaults to the hosted `https://captun.sh` gateway when there is no local config, generates a random tunnel name, and prints a public URL like: ```text https://abc123.captun.sh -> localhost:3000 ``` No Cloudflare account, config file, token, or deploy wizard is needed for that first run. Users can still run `npx captun deploy` later if they want their own self-hosted gateway. ## What Changed - defaults missing local config to the hosted `https://captun.sh` gateway - changes `createCaptunTunnel` to connect with `{ gateway, name, token, fetch }` and wait for the gateway to return `{ url, token }` - renames the low-level Cap'n Web accept APIs to `acceptFetcherCapability` and `acceptFetcherCapabilityFromSocket` - updates the CLI, deploy wizard, config file, smoke scripts, benchmarks, docs, and hosted browser demo to use `gateway`/`token` - keeps the self-hosted Cloudflare Tunnel Gateway readable in `src/worker.ts`, with the hosted `captun.sh` product surface isolated under `src/hosted/` - reserves product/control-plane tunnel names, serves `www.captun.sh`, and redirects the apex host to `www` - adds `CONTEXT.md` and ADR-0001 to keep the Fetcher Capability / Tunnel / Gateway language clear #20 is superseded by this shape; hosted rate limiting and ownership controls are rebuilt in #22 on top of the gateway-owned protocol. ## Library Example ```ts import { createCaptunTunnel } from "captun"; const tunnel = await createCaptunTunnel({ fetch: (request) => Response.json({ path: new URL(request.url).pathname }), }); console.log(tunnel.url); // https://abc123.captun.sh ``` Self-hosted use now passes the gateway URL, not a tunnel URL template: ```bash npx captun 3000 --gateway 'https://captun.youraccount.workers.dev' --token abc123 ``` ## Structure - `src/index.ts` contains the client API and low-level Fetcher Capability helpers. - `src/worker.ts` is the deployable self-hosted Cloudflare Tunnel Gateway. - `src/hosted/site.ts` and `src/hosted/worker.ts` are the Iterate-operated hosted surface for `captun.sh`. - `wrangler.jsonc` is for self-hosted deployment; `wrangler.hosted.jsonc` is for the hosted service. ## Verification - `pnpm run check` - `pnpm test` - `pnpm run build` - `pnpm exec vitest run test/worker.test.ts test/e2e.test.ts examples/weather-reporter/e2e.test.ts` - `CAPTUN_PUBLIC_E2E=1 pnpm exec vitest run test/public-hosted.test.ts` - deployed `captun-public` to Iterate prd with `captun.sh/*` and `*.captun.sh/*`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Adds the first public-hosted safety layer for the now-merged hosted
captun.shgateway.Hosted
captun.shtunnels now get a generatedcaptun-token; the active tunnel keeps that token, same-token reconnects can replace it, and different-token reconnects get409instead of evicting someone else. Forwarded responses also dropSet-Cookieheaders whoseDomainescapes the active tunnel hostname whilecaptun.shwaits for enough usage to justify Public Suffix List submission.Structure
src/worker.tsowns the shared Cloudflare Tunnel Gateway andCaptunServerShardtunnel lifecycle; its defaultdecideTunnelAdmissionbehavior keeps self-hosted deployments trusted and token-gated only whenCAPTUN_TOKENis set.src/hosted/worker.tsis the Iterate-operated hosted gateway entrypoint forcaptun.sh; it subclasses the shared shard to override public hosted admission, wraps requests with hosted rate limits, and strips broad response cookie domains.src/hosted/rate-limit.tsowns hosted-only Durable Object rate-limit buckets.src/hosted/site.tsowns thewww.captun.shdocs/demo surface and browser module.Notes for reviewers
CAPTUN_TOKENkeep trusted replacement behavior; deployments withCAPTUN_TOKENstill require the configured token.HostedRateLimiterbinding.409/429bodies instead of generic WebSocket failures.Domain=captun.sh, sibling tunnel domains, and other domains outside that tunnel namespace.Verification
pnpm exec vitest run test/hosted-worker.test.tspnpm run checkpnpm testNote
High Risk
Changes public tunnel admission, rate limiting, and cookie handling on the untrusted hosted gateway—security-sensitive paths with broad test coverage but production impact on all captun.sh users.
Overview
This PR adds the first public hosted safety layer for
captun.sh, keeping public gateway policy insrc/hosted/whilesrc/worker.tsstays the shared self-hosted gateway core.Tunnel ownership: Hosted clients (CLI, library, browser demo) now get a generated
captun-tokenwhen targetingcaptun.sh. The hosted shard overrides admission so anonymous connects require that token, same-token reconnects can replace the session, and a different token gets 409 instead of evicting the active tunnel. Self-hosted behavior is unchanged unlessCAPTUN_TOKENis set.Rate limiting: New
HostedRateLimiterDurable Object buckets throttle connect attempts and forwarded traffic percf-connecting-ipand per tunnel name, with a diagnostic path that does not open WebSockets or replace active tunnels. Missing the limiter binding returns 503 unless explicitly disabled.Client UX:
createCaptunTunnelprobes failed WebSocket upgrades viax-captun-connect-diagnosticand surfacesCaptunTunnelConnectErrorwith HTTP status/body; the CLI skips DNS hints for the known name-conflict case and reuses the same hosted token across retries.Response hardening: Hosted forwards strip
Set-Cookieheaders whoseDomainescapes the active tunnel hostname (e.g. apex or sibling subdomains).Reviewed by Cursor Bugbot for commit 133ffbf. Bugbot is set up for automated code reviews on this repo. Configure here.