docs/plans/portable-binary.md
Create a portable, self-contained distribution of happy-server as a single Bun-compiled binary. It runs without Redis (already has in-memory event bus), uses PGlite for embedded PostgreSQL, and local filesystem for file storage. CLI provides happy-server migrate and happy-server serve commands.
eventRouter.ts). Redis is only used for redis.ping() health check — zero actual pub/sub usage. @socket.io/redis-streams-adapter is a dependency but never imported in source code.pglite-prisma-adapter provides a Prisma driver adapter for PGlite. Requires driverAdapters preview feature in schema.uploadImage.ts, files.ts, referenced in eventRouter.ts, accountRoutes.ts, type.ts.prisma/migrations/. PGlite adapter doesn't support prisma migrate CLI, so we apply SQL files directly via PGlite.--compile output. Workaround: copy postgres.data/postgres.wasm files next to binary.@electric-sql/pglite, pglite-prisma-adapter to happy-server dependenciesdriverAdapters to previewFeatures in prisma/schema.prisma generator blockprisma generate to regenerate client with adapter supportsources/storage/db.ts to conditionally use PGlite when PGLITE_DIR env var is set
PGLITE_DIR is set: create PGlite instance with that dir, wrap in PrismaPGlite adapter, pass to new PrismaClient({ adapter })new PrismaClient() (connects via DATABASE_URL as before)getPGlite() function for direct SQL access (needed by migration command)main.ts, make redis.ping() conditional — only if REDIS_URL env var is setsources/storage/files.ts:
DATA_DIR/files/ directorysources/storage/uploadImage.ts:
s3client.putObject with conditional: S3 or fs.writeFile to local pathresolveImageUrl to return local file-serving URL when in local mode/files/*)sources/standalone.ts as the portable entry point:
process.argv for subcommands: migrate, servemigrate: initialize PGlite directly, read all prisma/migrations/*/migration.sql files in order, execute them via PGlite SQL, track applied migrationsserve: call existing main() logic--help: print usagebuild:standalone script to package.json: bun build ./sources/standalone.ts --compile --outfile happy-serverbun run build:standalone./happy-server migrate creates and migrates a PGlite database./happy-server serve starts the server with PGlite./happy-server migrate — verify database is created in ./data/./happy-server serve — verify server starts, health endpoint respondsPGlite initialization:
import { PGlite } from '@electric-sql/pglite';
import { PrismaPGlite } from 'pglite-prisma-adapter';
const pglite = new PGlite(process.env.PGLITE_DIR || './data/pglite');
const adapter = new PrismaPGlite(pglite);
const db = new PrismaClient({ adapter });
Migration approach:
prisma/migrations/ sorted by directory name (timestamp order)_prisma_migrations table to track applied migrationsprisma migrate deploy doesData directory structure:
./data/ (or $DATA_DIR)
pglite/ # PGlite database files
files/ # Uploaded files (replaces S3)
public/users/... # Same path structure as S3
CLI usage:
happy-server migrate # Apply database migrations
happy-server serve # Start the server
PGLITE_DIR, DATA_DIR, HANDY_MASTER_SECRET)happy-server init command for first-time setup