Switch from unix socket to TCP proxy

Proxy to Go server on 127.0.0.1:8000 instead of unix socket.
Go sees localhost connections as trusted for auto-login.
Removes all the unix socket, IP forwarding, and socket path
plumbing complexity.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Corey Johnson 2026-03-10 19:32:42 -07:00
parent 24b9629f0f
commit 383f6a8143
3 changed files with 18 additions and 26 deletions

View File

@ -1,8 +1,9 @@
import { join } from 'path' import { join } from 'path'
import { accessSync, chmodSync, constants, mkdirSync, unlinkSync } from 'fs' import { accessSync, chmodSync, constants, mkdirSync } from 'fs'
import type { Subprocess } from 'bun' import type { Subprocess } from 'bun'
const BIN_DIR = join(import.meta.dir, '..', 'bin') const BIN_DIR = join(import.meta.dir, '..', 'bin')
const GO_PORT = 8000
let healthy = false let healthy = false
let proc: Subprocess | undefined let proc: Subprocess | undefined
@ -16,10 +17,10 @@ function getBinaryName(): string {
return `tronbyt-server-${platform}-${arch}` return `tronbyt-server-${platform}-${arch}`
} }
async function waitForHealthy(socketPath: string, maxAttempts = 60): Promise<boolean> { async function waitForHealthy(maxAttempts = 60): Promise<boolean> {
for (let i = 0; i < maxAttempts; i++) { for (let i = 0; i < maxAttempts; i++) {
try { try {
const resp = await fetch('http://localhost/health', { unix: socketPath }) const resp = await fetch(`http://127.0.0.1:${GO_PORT}/health`)
if (resp.ok) return true if (resp.ok) return true
} catch {} } catch {}
await Bun.sleep(1000) await Bun.sleep(1000)
@ -63,7 +64,7 @@ function validate(dataDir: string): string | undefined {
} }
} }
export async function spawn(dataDir: string, socketPath: string) { export async function spawn(dataDir: string) {
const binPath = join(BIN_DIR, getBinaryName()) const binPath = join(BIN_DIR, getBinaryName())
if (!(await Bun.file(binPath).exists())) { if (!(await Bun.file(binPath).exists())) {
@ -76,14 +77,11 @@ export async function spawn(dataDir: string, socketPath: string) {
return return
} }
try { unlinkSync(socketPath) } catch {}
console.log('Starting tronbyt server...') console.log('Starting tronbyt server...')
proc = Bun.spawn([binPath], { proc = Bun.spawn([binPath], {
env: { env: {
...process.env, ...process.env,
TRONBYT_UNIX_SOCKET: socketPath,
DATA_DIR: dataDir, DATA_DIR: dataDir,
DB_DSN: join(dataDir, 'tronbyt.db'), DB_DSN: join(dataDir, 'tronbyt.db'),
PRODUCTION: process.env.PRODUCTION ?? 'true', PRODUCTION: process.env.PRODUCTION ?? 'true',
@ -100,7 +98,7 @@ export async function spawn(dataDir: string, socketPath: string) {
proc = undefined proc = undefined
}) })
if (await waitForHealthy(socketPath)) { if (await waitForHealthy()) {
healthy = true healthy = true
console.log('Tronbyt server is healthy') console.log('Tronbyt server is healthy')
} else { } else {

View File

@ -5,31 +5,29 @@ export interface WsData {
protocols: string[] protocols: string[]
} }
const GO_PORT = 8000
const GO_BASE = `http://127.0.0.1:${GO_PORT}`
const upstreams = new Map<ServerWebSocket<WsData>, WebSocket>() const upstreams = new Map<ServerWebSocket<WsData>, WebSocket>()
export function createProxy(socketPath: string, isHealthy: () => boolean, isRunning: () => boolean) { export function createProxy(isHealthy: () => boolean, isRunning: () => boolean) {
async function proxyFetch(req: Request, clientIP?: string): Promise<Response> { async function proxyFetch(req: Request): Promise<Response> {
const url = new URL(req.url) const url = new URL(req.url)
if (url.pathname === '/ok') { if (url.pathname === '/ok') {
if (!isHealthy()) return new Response('starting', { status: isRunning() ? 200 : 503 }) if (!isHealthy()) return new Response('starting', { status: isRunning() ? 200 : 503 })
return fetch('http://localhost/health', { unix: socketPath }) return fetch(`${GO_BASE}/health`)
.then((r) => (r.ok ? new Response('ok') : new Response('unhealthy', { status: 503 }))) .then((r) => (r.ok ? new Response('ok') : new Response('unhealthy', { status: 503 })))
.catch(() => new Response('unhealthy', { status: 503 })) .catch(() => new Response('unhealthy', { status: 503 }))
} }
const hasBody = req.method !== 'GET' && req.method !== 'HEAD' const hasBody = req.method !== 'GET' && req.method !== 'HEAD'
const body = hasBody ? await req.arrayBuffer() : undefined const body = hasBody ? await req.arrayBuffer() : undefined
const headers = new Headers(req.headers)
const forwardedFor = req.headers.get('x-forwarded-for')
headers.set('x-forwarded-for', forwardedFor ? `${forwardedFor}, ${clientIP}` : (clientIP ?? ''))
headers.set('x-real-ip', clientIP ?? '')
return fetch(`http://localhost${url.pathname}${url.search}`, { return fetch(`${GO_BASE}${url.pathname}${url.search}`, {
method: req.method, method: req.method,
headers, headers: req.headers,
body, body,
unix: socketPath,
}).then((r) => { }).then((r) => {
// Bun auto-decompresses gzip but leaves content-encoding header. // Bun auto-decompresses gzip but leaves content-encoding header.
// Strip it so the next proxy layer doesn't try to decompress again. // Strip it so the next proxy layer doesn't try to decompress again.
@ -45,7 +43,7 @@ export function createProxy(socketPath: string, isHealthy: () => boolean, isRunn
const websocket = { const websocket = {
open(ws: ServerWebSocket<WsData>) { open(ws: ServerWebSocket<WsData>) {
const upstream = new WebSocket(`ws+unix://${socketPath}:${ws.data.path}`, ws.data.protocols) const upstream = new WebSocket(`ws://127.0.0.1:${GO_PORT}${ws.data.path}`, ws.data.protocols)
upstream.binaryType = 'arraybuffer' upstream.binaryType = 'arraybuffer'
upstreams.set(ws, upstream) upstreams.set(ws, upstream)

View File

@ -1,12 +1,10 @@
import { join } from 'path'
import { createProxy, type WsData } from './proxy' import { createProxy, type WsData } from './proxy'
import { isHealthy, isRunning, shutdown, spawn } from './binary' import { isHealthy, isRunning, shutdown, spawn } from './binary'
const DATA_DIR = process.env.DATA_DIR! const DATA_DIR = process.env.DATA_DIR!
const PORT = Number(process.env.PORT) || 3000 const PORT = Number(process.env.PORT) || 3000
const SOCKET_PATH = join(DATA_DIR, 'tronbyt.sock')
const { proxyFetch, websocket } = createProxy(SOCKET_PATH, isHealthy, isRunning) const { proxyFetch, websocket } = createProxy(isHealthy, isRunning)
const server = Bun.serve({ const server = Bun.serve({
port: PORT, port: PORT,
@ -25,9 +23,7 @@ const server = Bun.serve({
return new Response('WebSocket upgrade failed', { status: 500 }) return new Response('WebSocket upgrade failed', { status: 500 })
} }
const clientIP = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() return proxyFetch(req)
|| server.requestIP(req)?.address
return proxyFetch(req, clientIP)
}, },
websocket, websocket,
@ -38,4 +34,4 @@ console.log(`Listening on port ${server.port}`)
process.on('SIGTERM', shutdown) process.on('SIGTERM', shutdown)
process.on('SIGINT', shutdown) process.on('SIGINT', shutdown)
spawn(DATA_DIR, SOCKET_PATH) spawn(DATA_DIR)