ZeroProxy is a client-owned virtual browsing prototype that runs target pages on the proxy origin without a browser extension. Its design goal is that target-site HTTP, TLS, and WebSocket traffic leaves only through this path:
Service Worker -> Go WASM kernel -> WebSocket/yamux -> SOCKS5 CONNECT -> uTLS -> HTTP/2 or HTTP/1.1
The relay server terminates only the browser WebSocket/yamux pipe. In production it byte-bridges each yamux stream to a Tor SOCKS5 listener; for local compatibility tests -socks internal makes the relay parse the kernel's SOCKS5 CONNECT itself and dial the requested target directly. Target HTTP parsing, redirects, cookies, header policy, HTML rewriting, and target WebSocket framing are owned by the Go WASM kernel and browser-side runtime.
Status: Prototype / partial implementation.
Implemented core spine:
- Encrypted active/share route format:
/zp/p/<encrypted>#k=<key>&server=.... - AES-256-CBC + HMAC-SHA256 URL envelope with HKDF-separated encryption/MAC keys and HMAC verification before decryption.
- Service Worker request classifier that handles every controlled request, blocks unknown requests instead of falling back to native
fetch(event.request), and requires a per-tab runtime capability token on privileged runtime bridge messages. - Go WASM exports:
__go_jshttp,__zp_stream,__zp_kernel_init, and__zp_cookie_set. - A single browser WebSocket pipe carrying yamux streams to the relay server, then SOCKS5 DOMAINNAME CONNECT, uTLS for HTTPS, HTTP/2 when ALPN selects
h2, and HTTP/1.1 fallback/direct handling.-socks 127.0.0.1:9050preserves the Tor bridge;-socks internalis a Tor-free development/test mode that parses SOCKS5 on the relay and dials targets directly from the relay process. - Tokenizer-based HTML transform that injects the runtime prelude, launders executable external scripts through
/zp/api/script?u=..., rewrites iframe/frame document URLs to encrypted/zp/proutes with inheritedserver=relay fragments, preserves author-visible anchor/form attributes for runtime navigation interception, removes or neutralizes preload/preconnect/manifest hints, drops dangerous tags and headers, routes executable event attributes through the Rust WASM rewriter, and handlessrcdoc. - Runtime containment for main-window
fetch, XHR, EventSource, WebSocket,sendBeacon, navigation, forms, history/location masking, storage facades, workers, iframes, and high-risk device/network APIs. Main-window and workerfetchpaths are bridged through/zp/api/fetchso strictconnect-src 'self'does not block target API calls before the Service Worker can route them. Runtime-to-Service-Worker control messages carry a closure-held per-tab capability token. The runtime also applies basic self-fingerprint masking for patched function source strings, Canvas/Audio extraction jitter, and speech voice lists; broad anti-bot spoofing is not a project goal. - Rust WASM JavaScript rewriting is the only script rewrite engine: target-response CSP no longer permits
connect-src *, external, module, worker, imported, inline, event-handler, and synchronous dynamic-function bodies are parsed before execution, dangerous global/window/location access is rewritten to runtime membrane helpers, parse/transform failures fail closed, constructor-constructor escapes are routed through runtime helpers instead of blocked, and blob/data worker scripts remain blocked when they cannot be rewritten synchronously. - Relay server static asset service and
/zp/ws-pipeWebSocket endpoint. - Go and JavaScript share URL implementations that use the same envelope format.
Not complete enough for production or high-assurance acceptance:
- Browser E2E tests cover internal SOCKS5 relay mode, dynamic script laundering, module-script worker bootstrap, inert dynamic preload/preconnect link handling, compound location assignments, iframe postMessage delivery, iframe clean-realm containment, forms, cookies, streaming responses, and basic fingerprint-masking checks, but do not yet prove every worker, direct navigation, device API, and unclassified subresource non-escape path.
- Dynamic iframe containment is synchronous for
contentWindow/contentDocumentreads and common insertion APIs, but remains prototype-level and should keep gaining adversarial browser coverage. - Main-window runtime API compatibility is prototype-level for
fetch, XHR, EventSource, WebSocket,sendBeacon, forms, uploads, descriptor edge cases, and fingerprinting surface fidelity. The wrappers preserve the ZeroProxy transport boundary, but they are not browser-native semantic clones for every option, event, redirect, credential, cache, progress, or close/error edge case. - Response bodies are streamed into JavaScript
Responseobjects, but request/upload bodies are buffered through the Service Worker/WASM bridge with an explicit size cap. Streaming uploads, large multipart/file uploads, request cancellation, and browser backpressure behavior are still prototype-level. - Form navigation compatibility is limited: GET submissions become ZeroProxy navigations, while non-GET submissions are replayed through the runtime fetch path and write the transformed response back into the current document rather than following the browser's native navigation algorithm.
- Worker compatibility is partial: Worker/SharedWorker constructors bootstrap through ZeroProxy, dedicated worker
fetchandimportScriptsare bridged, rewritten worker code receives the runtime membrane helpers it may reference, and worker XHR, WebSocket, EventSource, native device/network APIs, full worklet/module-worker parity, and unrewritable blob/data worker scripts are blocked or prototype-level rather than fully emulated. - Cookie, storage, and history semantics are not yet reconciled across runtime state, Service Worker state, and the Go kernel cookie jar. Encrypted IndexedDB persistence is not implemented.
- Tor daemon deployment and real Tor-egress E2E validation are not included in this repository.
See ARCHITECTURE.md for the implementation map and acceptance boundary. See PHASE3_PLAN.md for the current cutover plan.
- Go 1.26 or the Go toolchain version required by
go.mod. - Node.js LTS and npm for the JavaScript and Puppeteer E2E tests.
- A browser with Service Worker and WebAssembly support. CI uses Puppeteer's pinned Chrome for Testing.
- A Tor SOCKS5 listener configured with stream isolation for anonymized manual target browsing. Tor is not required for the automated Puppeteer suite or local compatibility checks that run the relay with
-socks internal.
Example Tor setting:
SocksPort 127.0.0.1:9050 IsolateSOCKSAuth
Start a local Tor listener for development:
mkdir -p /tmp/zeroproxy-tor
tor --SocksPort "127.0.0.1:9050 IsolateSOCKSAuth" --DataDirectory /tmp/zeroproxy-torKeep that process running and wait until Tor logs Bootstrapped 100% (done) before expecting target browsing to work. If Tor is managed by your OS service manager instead, use the same SocksPort setting in torrc and start the service before starting ZeroProxy.
For Tor-free local compatibility testing, start ZeroProxy with the internal relay SOCKS5 parser instead of starting Tor:
./dist/zeroproxy-server -addr :8080 -socks internalInternal mode is not an anonymity mode: target TCP connections are direct dials from the relay process. It exists so CI and local browser compatibility tests can exercise the browser → Service Worker → WASM → WebSocket/yamux → SOCKS5 parsing pipeline without an external proxy daemon.
Build the browser bundle, Go WASM kernel, and relay server from the repository root:
npm ci
npm run buildThe build writes deployable artifacts under dist/:
dist/web/ built browser assets
dist/kernel.wasm Go WASM transport kernel
dist/zeroproxy-server relay server binary
Start the relay server in another terminal:
./dist/zeroproxy-server -addr :8080 -socks 127.0.0.1:9050Equivalent go run form after npm run build:
go run ./cmd/zeroproxy-server -addr :8080 -socks 127.0.0.1:9050Server flags:
-addr: HTTP listen address. Default::8080.-web: built static web asset directory containingindex.html,sw.js, and/__zp/*assets. Default:dist/web.-kernel: compiled Go WASM kernel served at/__zp/kernel.wasm. Default:dist/kernel.wasm.-socks: Tor SOCKS5 address, orinternalfor the relay's built-in SOCKS5 CONNECT parser/direct dialer used by tests. Default:127.0.0.1:9050.
Open the browser shell on the proxy origin:
http://proxy.localhost:8080/
Use proxy.localhost from the start so the shell, Service Worker, and encrypted /zp/p/<encrypted>#k=<key>&server=... routes share one origin. The server starts even if Tor is not reachable. Target browsing needs either a configured Tor SOCKS5 listener or the explicit non-anonymous -socks internal test mode.
The local verification surface matches the GitHub Actions CI workflow:
npm ci
go test ./...
npm test
npm run buildnpm test runs both JavaScript source-policy tests and the Puppeteer E2E suite. The E2E test builds temporary ZeroProxy artifacts with scripts/build.mjs, starts a local target HTTP server, starts the relay with -socks internal, launches Puppeteer's Chrome, and verifies browser traffic through the ZeroProxy server without requiring Tor.
CI is defined in .github/workflows/ci.yml and runs on pushes to main, pull requests, and manual dispatch. It uses an Ubuntu 24.04 LTS runner, installs Go from go.mod, installs the current Node.js LTS release, runs npm ci, runs the Go and full JavaScript/Puppeteer test suites, and builds deployable dist/ artifacts.
These checks cover source/unit policy invariants, buildability, and a local-browser E2E path through the relay's internal SOCKS5 parser/direct dialer. They do not start Tor, validate real Tor deployment behavior, or prove production traffic compatibility.
| Path | Purpose |
|---|---|
web/index.html, web/zp-core.js |
Browser shell, shared URL encryption/decryption, and ZeroProxy CSP helper. |
web/sw.js |
Service Worker classifier, in-memory tab state, runtime API bridge, WASM kernel calls. |
web/runtime-prelude.js, web/worker-prelude.js |
Target-realm containment hooks, dynamic HTML/link/script policy, and worker bootstrap/membrane helpers. |
scripts/build.mjs |
Full build pipeline for browser bundles, generated WASM support assets, Go WASM kernel, and relay server. |
scripts/test.mjs |
Stable local/CI test runner that executes JavaScript policy tests and the Puppeteer E2E suite in sequence. |
cmd/wasm-kernel |
Go WASM transport kernel exposed to the Service Worker. |
cmd/zeroproxy-server |
Static asset server and Gorilla WebSocket/yamux relay to Tor SOCKS5 or the -socks internal direct-dial SOCKS5 parser. |
internal/zphttp, internal/socks5, internal/utlskernel, internal/wsproto, internal/yamuxconn, internal/wsconn |
Target transport path. |
internal/htmltx, internal/headers, internal/cookiejar, internal/shareurl, internal/zpiso |
HTML rewriting, response header policy, cookie handling, share URL envelope, Tor isolation tokens. |
test/js, test/e2e, internal/*/*_test.go |
JavaScript source-policy tests, Puppeteer browser E2E tests, and Go unit tests. |
.github/workflows/ci.yml |
GitHub Actions CI for Go tests, JavaScript/Puppeteer tests, WASM build, and relay server build. |