- Add IPv6SourcePool for round-robin IP rotation - fetchViaCurl: Bun.spawn + curl -6 --interface for source binding - Auto-fallback to regular fetch when IPv6 connection fails - Response decompression via --compressed flag - Docker + GHCR build/deploy workflows - 8 routable IPv6 addresses on VPS
6.3 KiB
Edge Proxy Relay
A pure Bun HTTP and WebSocket relay proxy using Bun.serve(). No Next.js, no Express, no React, no Vercel Edge Runtime. Single entry point at src/index.ts -- deploys as a standalone Bun process.
Key architecture facts:
- Entry point:
src/index.ts(wassrc/app/route.tsin the previous Next.js version) - Middleware stack: rate limiter, body limiter, structured logger, SSRF protection
- WebSocket relay: bidirectional relay via
x-relay-targetheader withws://orwss:// - Error classification: DNS errors -> 502, timeouts -> 504, SSRF blocks -> 403, rate limits -> 429
- IPv6 support: dual-stack listen + outbound source rotation via
Bun.spawn+curl --interface - The old Next.js
src/app/route.tsstill exists as a legacy file but is no longer the active entry point
IPv6 Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
HOST |
:: |
Bind address. Use :: for dual-stack (IPv4+IPv6) |
IPV6_SOURCES |
(empty) | Comma-separated IPv6 source addresses for outbound rotation |
Setup
- Add IPv6 addresses to your interface:
ip -6 addr add 2001:df4:c140:1f::d6/128 dev eth0
ip -6 addr add 2001:df4:c140:1f:ffff:ffff:ffff:ffff/128 dev eth0
- Configure the proxy with IPv6 source rotation:
IPV6_SOURCES=2001:df4:c140:1f::d6,2001:df4:c140:1f:ffff:ffff:ffff:ffff bun run src/index.ts
How It Works
- Listen: Server binds to
::(all IPv6 interfaces) withipv6Only: false(dual-stack) - Outbound: When
IPV6_SOURCESis configured, each outbound request rotates through the source addresses usingcurl --interface <ipv6> - Failover: Failed source addresses are automatically disabled after 3 consecutive failures
Source: src/lib/ipv6-pool.ts
import { IPv6SourcePool } from "./lib/ipv6-pool";
const pool = new IPv6SourcePool();
pool.loadFromEnv(); // reads IPV6_SOURCES
const source = pool.getNext(); // round-robin
pool.markSuccess(source); // reset failure count
pool.markFailed(source); // increment failure count
Default to using Bun instead of Node.js.
- Use
bun <file>instead ofnode <file>orts-node <file> - Use
bun testinstead ofjestorvitest - Use
bun build <file.html|file.ts|file.css>instead ofwebpackoresbuild - Use
bun installinstead ofnpm installoryarn installorpnpm install - Use
bun run <script>instead ofnpm run <script>oryarn run <script>orpnpm run <script> - Use
bunx <package> <command>instead ofnpx <package> <command> - Bun automatically loads .env, so don't use dotenv.
APIs
Bun.serve()supports WebSockets, HTTPS, and routes. Don't useexpress.bun:sqlitefor SQLite. Don't usebetter-sqlite3.Bun.redisfor Redis. Don't useioredis.Bun.sqlfor Postgres. Don't usepgorpostgres.js.WebSocketis built-in. Don't usews.- Prefer
Bun.fileovernode:fs's readFile/writeFile - Bun.$
lsinstead of execa.
Testing
Use bun test to run tests.
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});
Frontend
Use HTML imports with Bun.serve(). Don't use vite. HTML imports fully support React, CSS, Tailwind.
Server:
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. <link> tags can point to stylesheets and Bun's CSS bundler will bundle.
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
With the following frontend.tsx:
import React from "react";
import { createRoot } from "react-dom/client";
// import .css files directly and it works
import './index.css';
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);
Then, run index.ts
bun --hot ./index.ts
For more information, read the Bun API docs in node_modules/bun-types/docs/**.mdx.
MCP Tools: code-review-graph
IMPORTANT: This project has a knowledge graph. ALWAYS use the code-review-graph MCP tools BEFORE using Grep/Glob/Read to explore the codebase. The graph is faster, cheaper (fewer tokens), and gives you structural context (callers, dependents, test coverage) that file scanning cannot.
When to use graph tools FIRST
- Exploring code:
semantic_search_nodesorquery_graphinstead of Grep - Understanding impact:
get_impact_radiusinstead of manually tracing imports - Code review:
detect_changes+get_review_contextinstead of reading entire files - Finding relationships:
query_graphwith callers_of/callees_of/imports_of/tests_for - Architecture questions:
get_architecture_overview+list_communities
Fall back to Grep/Glob/Read only when the graph doesn't cover what you need.
Key Tools
| Tool | Use when |
|---|---|
detect_changes |
Reviewing code changes — gives risk-scored analysis |
get_review_context |
Need source snippets for review — token-efficient |
get_impact_radius |
Understanding blast radius of a change |
get_affected_flows |
Finding which execution paths are impacted |
query_graph |
Tracing callers, callees, imports, tests, dependencies |
semantic_search_nodes |
Finding functions/classes by name or keyword |
get_architecture_overview |
Understanding high-level codebase structure |
refactor_tool |
Planning renames, finding dead code |
Workflow
- The graph auto-updates on file changes (via hooks).
- Use
detect_changesfor code review. - Use
get_affected_flowsto understand impact. - Use
query_graphpattern="tests_for" to check coverage.