Files
wisp/docker/web.Dockerfile
T
NilsBriggen 9f42ee2460
CI / test (push) Has been cancelled
CI / deploy-web (push) Has been cancelled
CI / build-apk (push) Has been cancelled
Build Wisp: on-device transcription studio (web + native, one codebase)
Private, offline speech-to-text that runs Whisper on the user's own device —
free, no account, no per-minute fees. Replaces Otter.ai / Rev.

- Pure, tested engine: chunking, overlap timestamp-stitching, exports
  (SRT/VTT/TXT/MD/JSON), WAV codec, resampler, job queue, model catalog (142 tests).
- Platform-abstracted TranscriptionEngine: transformers.js on web (loaded from
  CDN at runtime to dodge Metro's onnxruntime-web bundling limits), whisper.rn
  on native. Shared pipeline orchestrates decode -> chunk -> transcribe -> stitch.
- Cross-platform StorageRepo (Dexie web / expo-sqlite native), Zod-validated.
- UI: library + search, import, live-progress transcription, synced click-to-seek
  editor, multi-format export; model picker + privacy in settings.
- Web ships as a single-page PWA with COOP/COEP isolation for threaded WASM;
  Docker (nginx) image + Traefik compose for wisp.briggen.dev.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 17:54:21 +02:00

73 lines
3.4 KiB
Docker

# syntax=docker/dockerfile:1
#
# docker/web.Dockerfile
# ---------------------------------------------------------------------------
# Multi-stage build for the Wisp web app.
#
# Stage 1 (builder): Uses the official Bun image to install dependencies and
# run the Expo/Metro static web export. The output is a
# fully static SPA in /app/dist.
#
# Stage 2 (runtime): A tiny nginx:alpine image that serves the exported
# static files. nginx is configured (docker/nginx.conf)
# to send the cross-origin isolation headers that the
# multi-threaded WASM Whisper backend requires, namely:
# Cross-Origin-Opener-Policy: same-origin
# Cross-Origin-Embedder-Policy: require-corp
#
# Build: docker build -f docker/web.Dockerfile -t wisp-web:latest .
# Run: docker run --rm -p 8080:8080 wisp-web:latest
#
# NOTE: build context must be the repository root (so the whole repo + lockfile
# are available to the builder).
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
# Stage 1: build the static site with Bun + Expo export
# ---------------------------------------------------------------------------
# Pin to the Bun 1.x line. `oven/bun:1` tracks the latest 1.x patch release;
# pin to an exact digest/tag in production if you want fully reproducible builds.
FROM oven/bun:1 AS builder
WORKDIR /app
# Copy manifest + lockfile first so Docker can cache the (slow) dependency
# install layer independently of source changes. `bun.lock` is Bun's lockfile.
COPY package.json bun.lock ./
# Install EXACTLY what the lockfile specifies. --frozen-lockfile fails the build
# if package.json and bun.lock have drifted, which keeps CI deterministic.
RUN bun install --frozen-lockfile
# Now copy the rest of the repository (app code, assets, config, etc.).
# Anything matched by .dockerignore (node_modules, dist, .git ...) is skipped.
COPY . .
# Produce the static web bundle. `bun run export:web` maps to
# `expo export --platform web`, which writes the static site to ./dist
# (Expo's default --output-dir). app.json sets web.output = "static", so this
# emits prerendered HTML + the JS/WASM assets for the SPA.
RUN bun run export:web
# ---------------------------------------------------------------------------
# Stage 2: serve the static site with nginx
# ---------------------------------------------------------------------------
FROM nginx:alpine AS runtime
# Drop the stock nginx site config and install ours. Our config:
# - listens on 8080 (non-privileged port; matches docker-compose + reverse proxy)
# - SPA fallback (try_files ... /index.html)
# - COOP/COEP/CORP cross-origin isolation headers (for threaded WASM)
# - gzip + long-lived caching for hashed static assets
# - serves /wisp.apk if an APK has been dropped into the web root
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
# Copy the exported static site from the builder stage into nginx's web root.
COPY --from=builder /app/dist /usr/share/nginx/html
# Document the port the container listens on (informational; publish with -p).
EXPOSE 8080
# nginx:alpine's default entrypoint already runs nginx in the foreground
# ("daemon off") via /docker-entrypoint.sh, so no CMD override is needed.