Skip to content

Run the web app on Bun (pairux.com, installer.pairux.com) - #128

Merged
ralyodio merged 2 commits into
masterfrom
chore/bun-runtime
Oct 1, 2026
Merged

ralyodio merged 2 commits into
masterfrom
chore/bun-runtime

Conversation

@ralyodio

@ralyodio ralyodio commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Part of the fleet Node → Bun migration (recipe from the phonenumbers.bot pilot). Both dev2 sites built from apps/web/Dockerfile, pairux.com and installer.pairux.com, move their runtime from Node to Bun.

Changes

  • Image (apps/web/Dockerfile):
    • Node + pnpm still install the dependencies. pnpm stays this monorepo's package manager because the Electron desktop and Expo mobile toolchains, the patched @expo/cli (pnpm.patchedDependencies) and the release scripts depend on it, and none of them run on dev2.
    • Next builds with bun --bun next build.
    • The runtime is alpine:3.24 with Bun's musl binary copied in. That is the Alpine release node:24-alpine uses, so ffmpeg for call analysis stays the same version (oven/bun:*-alpine is 3.22). icemap.app runs this same pattern in production.
    • Unchanged: port 8080, /api/health, the three NEXT_PUBLIC_* build args the image already declared, and the non-root uid 1001.
    • The healthcheck now uses Bun's fetch on 127.0.0.1 instead of busybox wget.
  • apps/web scripts: bun --bun next dev|build|start. No workflow built the web app before, so nothing else changes.
  • CI: a new web on Bun workflow installs with pnpm, builds the web app under Bun 1.4.0 and boots the standalone server under Bun. I left ci.yml alone because the sh1pt Actions Fleet manages it.

I'll post the local image verification against live pairux.com and installer.pairux.com in a comment before merging.

🤖 Generated with Claude Code

Both dev2 sites build apps/web/Dockerfile and now run the Next standalone
server under Bun instead of Node.

- Image: Node + pnpm only install dependencies (pnpm stays the monorepo's
  package manager: the Electron and Expo toolchains, the patched @expo/cli and
  the release scripts depend on it, and none of them run on dev2). Next builds
  with `bun --bun next build`; the runtime is alpine:3.24 (the Alpine
  node:24-alpine had, so ffmpeg for call analysis is unchanged) with Bun's
  musl binary copied in, the pattern icemap.app runs in production. Port 8080,
  /api/health, the three NEXT_PUBLIC_* build args and the non-root uid 1001 are
  unchanged; the healthcheck uses Bun's fetch on 127.0.0.1.
- apps/web scripts: `bun --bun next dev|build|start`.
- CI: a new "web on Bun" workflow installs with pnpm, builds the web app under
  Bun and boots the standalone server under Bun. ci.yml is left alone (it is
  managed by the sh1pt Actions Fleet).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown

ThreatCrush Security Scan

47 finding(s)

HIGH/CRITICAL: 4 | MEDIUM: 27 | LOW: 16

Severity Rule Location
HIGH sh-eval-expansion .githooks/pre-commit:33
HIGH js-electron-node-integration apps/desktop/src/main/window.ts:49
HIGH sh-unquoted-expansion-destructive apps/installer/scripts/install.sh:715
HIGH sh-unquoted-expansion-destructive apps/installer/scripts/install.sh:917
MEDIUM sh-remote-script-execution apps/installer/scripts/install.sh:691
MEDIUM sh-remote-script-execution apps/installer/scripts/install.sh:820
MEDIUM sh-remote-script-execution apps/installer/scripts/install.sh:822
MEDIUM sh-remote-script-execution apps/installer/scripts/install.sh:1078
MEDIUM sh-remote-script-execution apps/installer/scripts/install.sh:1080
MEDIUM sh-remote-script-execution apps/livekit/setup-livekit-server.sh:93
MEDIUM sh-remote-script-execution apps/turn/deploy-droplet.sh:62
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:48
MEDIUM js-unescaped-html-sink apps/web/src/app/blog/[slug]/page.tsx:73
MEDIUM js-unescaped-html-sink apps/web/src/app/c/[handle]/page.tsx:196
MEDIUM js-open-redirect apps/web/src/app/cli/authorize/consent.tsx:66
MEDIUM js-unescaped-html-sink apps/web/src/app/l/[joinCode]/page.tsx:129
MEDIUM js-unescaped-html-sink apps/web/src/app/l/[joinCode]/page.tsx:213
MEDIUM js-unescaped-html-sink apps/web/src/app/layout.tsx:142
MEDIUM js-unescaped-html-sink apps/web/src/app/live/page.tsx:145
MEDIUM js-unescaped-html-sink apps/web/src/app/page.tsx:123
MEDIUM js-unescaped-html-sink apps/web/src/app/pricing/page.tsx:286
MEDIUM js-open-redirect apps/web/src/app/pricing/UpgradeButton.tsx:50
MEDIUM js-unescaped-html-sink apps/web/src/app/u/[username]/page.tsx:282
MEDIUM js-open-redirect apps/web/src/hooks/useDesktopHandoff.ts:24
MEDIUM redos-nested-quantifier apps/web/src/lib/deliverable.ts:11
MEDIUM js-unescaped-html-sink apps/web/src/lib/player/player.ts:124
MEDIUM js-unescaped-html-sink apps/web/src/lib/player/player.ts:393
MEDIUM js-unescaped-html-sink apps/web/src/lib/player/player.ts:396
MEDIUM js-unescaped-html-sink apps/web/src/lib/player/player.ts:405
MEDIUM js-unescaped-html-sink apps/web/src/lib/player/player.ts:608
MEDIUM sql-template-interpolation packages/ai-core/src/prompts.ts:36
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:138
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:139
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:340
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:341
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:371
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:372
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:383
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:397
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:398
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:405
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:415
LOW secret-generic-credential apps/desktop/src/main/agent-cli/commands.test.ts:416
LOW secret-generic-credential apps/livekit/fly.toml:12
LOW secret-generic-credential apps/turn/fly.toml:11
LOW secret-generic-credential docs/API.md:747
LOW secret-generic-credential docs/API.md:753

Snippets are redacted; ThreatCrush never prints matched credential material.

Next 16.3's Turbopack evaluates PostCSS in a separate node worker and passes
it the parent's flags, so under bun --bun with a real Node on PATH it dies with
"node: --bun is not allowed in NODE_OPTIONS". The build stays on Node (pnpm
--filter @pairux/web build, as before); the standalone server runs on Bun.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@ralyodio

ralyodio commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor Author

Local verification. I built the image from git archive of this branch through a compose service shaped like dev2's pairux.com (same Dockerfile, build args, env file, port and memory limit) and compared it with live https://pairux.com.

  • docker top shows bun apps/web/server.js as the non-root user. ffmpeg in the image is 8.1.2, the same Alpine 3.24 line as before.
  • /api/health, /, /download, /pricing, /features, /docs and /login return 200. /robots.txt is byte-identical. /github gives the same 307. The 404s match.
  • The HTML content is the same once the per-request CSP nonces are normalised. Pages run 0.6–3 KB larger only because Bun's stream flushes the RSC payload in more, smaller <script> pushes; the payload itself is identical.
  • Memory was 112 MiB after the comparison run, against 123 MiB for the Node container on dev2. The image is 423 MB (539 MB before). docker stop is immediate.

dependency audit is non-gating (continue-on-error). It flags GHSA-vcvr-r3jv-pc5j, which affects next >=16.2.0 <16.3.6. The advisory was published after master's last audit run (09-28), so master would flag it too. The Next bump is a separate pass.

🤖 Generated with Claude Code

@ralyodio
ralyodio merged commit 43d85e2 into master Oct 1, 2026
13 of 14 checks passed
@ralyodio
ralyodio deleted the chore/bun-runtime branch October 1, 2026 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant