tronbyt/docs/PLAN.md
2026-03-10 16:53:05 -07:00

4.5 KiB

Tronbyt Toes App — Implementation Plan

What This Is

A toes app that wraps the Tronbyt Go server (self-hosted Tidbyt replacement) as a managed subprocess. Bun proxies all HTTP and WebSocket traffic to the Go binary over a unix socket. The Tidbyt device talks to tronbyt.toes.local and everything chains through.

Tidbyt device
  → tronbyt.toes.local (toes proxy)
    → Bun app on PORT (this app)
      → Go binary on unix socket

File Structure

tronbyt/
  package.json
  index.tsx              # re-exports src/server
  src/
    server/
      index.tsx          # Hype app: spawns Go binary, proxies all traffic
    pages/
      index.tsx          # Hype page (not needed, Go serves its own UI)
  bin/
    .gitkeep
    tronbyt-server       # pre-built binary (gitignored, per-platform)
  docs/
    PLAN.md              # this file

Implementation Steps

1. Binary setup

  • Download the right binary from https://github.com/tronbyt/server/releases
    • Mac dev: tronbyt-server-darwin-arm64
    • Pi production: tronbyt-server-linux-arm64
  • Place in bin/tronbyt-server, make executable (chmod +x)
  • Add bin/tronbyt-server to .gitignore
  • On macOS: System Settings → Privacy & Security → Allow Anyway

2. Spawn the Go binary

On server startup, spawn bin/tronbyt-server with these env vars:

Env Var Value Why
TRONBYT_UNIX_SOCKET {DATA_DIR}/tronbyt.sock Avoids port conflicts, clean proxying
DATA_DIR {DATA_DIR} Persists DB, apps, firmware across deploys
DB_DSN {DATA_DIR}/tronbyt.db Explicit DB path
PRODUCTION false Skip firmware downloads (set true on Pi later)
SINGLE_USER_AUTO_LOGIN true Home network, no login needed
SYSTEM_APPS_AUTO_REFRESH true Keep community apps updated

Wait for health by polling the unix socket at /health until it returns 200.

3. HTTP proxy

Proxy all requests from Bun → Go over the unix socket:

app.all('*', async (c) => {
  const resp = await fetch(socketUrl + c.req.path + search, {
    method: c.req.method,
    headers: c.req.raw.headers,
    body: c.req.raw.body,
    unix: socketPath,
  })
  return new Response(resp.body, resp)
})

Bun's fetch supports unix option natively — no extra libraries needed.

4. WebSocket proxy

Two WS endpoints to proxy:

  • GET /ws — dashboard real-time updates
  • GET /{deviceId}/ws — device push connection

Use Bun.serve's websocket support to accept the upgrade on the Bun side, then open a WebSocket to the Go server over the unix socket and relay messages bidirectionally.

5. Health check

app.get('/ok', async (c) => {
  const resp = await fetch('http://localhost/health', { unix: socketPath })
  return resp.ok ? c.text('ok') : c.text('unhealthy', 503)
})

6. Graceful shutdown

process.on('SIGTERM', () => {
  goProcess.kill('SIGTERM')
})

The Go server handles SIGTERM cleanly. Toes gives 10s before SIGKILL.

7. Remove Hype pages

The scaffolded src/pages/index.tsx isn't needed — the Go server serves its own web UI. The Bun app is purely a proxy, no HTML rendering. Can simplify to just src/server/index.tsx or even a single index.ts.

Toes Environment Variables

These are set automatically by toes on every app:

Var Description
PORT Port to listen on
DATA_DIR Per-app persistent data directory
APPS_DIR Shared apps directory
TOES_URL Base URL of toes server
APP_URL This app's subdomain URL

Device Configuration

Flash the Tidbyt with Tronbyt firmware, then set:

Image URL: http://tronbyt.toes.local/<device-id>/next

The device ID is assigned by the Tronbyt server when you add the device in its web UI.

Gotchas

  1. IPv6 binding — Not an issue with unix socket approach (no TCP binding on Go side)
  2. First boot is slow — Community apps repo clones on first start (~15s). With PRODUCTION=false, firmware download is skipped. Set PRODUCTION=true on Pi if you want OTA firmware updates.
  3. macOS unsigned binary — System Settings → Privacy & Security → Allow Anyway
  4. Device WiFi — Tidbyt ESP32 is 2.4GHz only
  5. Image URL must be full pathhttp://tronbyt.toes.local/<device-id>/next, not just the base URL

Dependencies

No new dependencies needed beyond what's already in package.json. Bun's native fetch handles unix sockets. The Go binary is self-contained (Pixlet embedded).

Can remove @because/forge and @because/howl from dependencies since this app has no UI of its own.