Runtime · Contributing
Aphrody self-hosted server
One executable that serves the bun.aphrody.app site and its API, with the same request handler as the Cloudflare Worker.
packages/web/server compiles the bun.aphrody.app site (static pages, /install*, /llms.txt) and its routes into a single executable. The Cloudflare Worker (packages/web/cloudflare/src/worker.ts) and the binary share one handler, createAphrodyHandler in packages/web/cloudflare/src/core.ts; each only supplies an AphrodyPlatform (storage, cache, limiters, AI).
Build
bun packages/web/server/build.ts --out build/aphrody-server [--assets <built site>] [--target bun-linux-x64] [--origin https://example.org]
The script builds the site with packages/web/site/build.ts, embeds every file with bun build --compile, and writes <out>/aphrody.
Run
APP_SECRET=change-me APHRODY_PORT=8080 ./aphrody
Configuration is read from environment variables, over an optional JSON file (APHRODY_CONFIG, default ./aphrody.json) with the same keys.
| Key | Default | Role |
|---|---|---|
APHRODY_HOST, APHRODY_PORT | 0.0.0.0, 8080 | Listen address |
APHRODY_TLS_CERT, APHRODY_TLS_KEY | none | PEM paths; both enable HTTPS |
APHRODY_DATA_DIR | ./aphrody-data | SQLite database (aphrody.db) and uploaded files |
APHRODY_ASSETS_DIR | embedded | Serve the site from a directory instead |
APP_SECRET | none | Bearer token for writes (PUT/DELETE files, room posts, AI) |
NEON_DATABASE_URL | none | Public site database (/api/site/*); Neon HTTP only |
TYPESAFE_API_KEY | none | Grounded answers on /api/site/rag |
AI_BASE_URL, AI_API_KEY, AI_MODEL | none | OpenAI-compatible backend for /api/ai and grounded answers |
BUN_SEARCH_UPSTREAM | none | Host proxied for /api/bun/search |
YOLO_* | disabled | Same keys as the Worker's vars and secrets |
Routes
Identical to the Worker: /api/health, /api/files/*, /api/index, /api/rooms/*, /api/ai, /api/bun/search, /api/site/*, /mcp, /a2a, /message:send, /v1/*. Everything else is the static site.
Limits
/api/site/*needsNEON_DATABASE_URL: the queries are PostgreSQL and do not run on SQLite (503 otherwise)./api/bun/searchneeds Vectorize on the Worker; the binary only proxiesBUN_SEARCH_UPSTREAM(501 otherwise).- Rate limits and caches are per process. Room WebSockets are Worker-only (501); history and posts work.
- Cloudflare Workers AI (
YOLOprovider path) is not available in the binary. - Services under
~/apps(Gitea, Komodo, control plane) and thebun-edgenginx origin remain separate.