oven/bun:1 doesn't ship with npm or pnpm, so the previous
`RUN npm install -g pnpm@9.15.0` failed with `/bin/sh: 1: npm: not
found` on the first Mac Mini build. Bun's own install command
doesn't honor pnpm-workspace.yaml, so we can't use it as a drop-in.
Switch the builder stage to node:20-alpine which has npm built in,
install pnpm there, resolve the workspace graph, then COPY the
finished tree into the bun runtime stage. The runtime stage stays
on oven/bun:1 — bun handles pnpm's node_modules/.pnpm symlink farm
natively, so the workspace layout works the same as it does on a
developer machine.
Tested locally: `docker compose -f docker-compose.macmini.yml build
mana-api` now succeeds through the install stage. The runtime
stage is unchanged.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds the missing production deployment artifacts for the unified
apps/api Hono/Bun server. Until now apps/api was code-only — built
during the consolidation sweep but never wired into the Mac Mini
compose stack, so all 17 product modules that depend on it
(calendar, todo, picture, planta, nutriphi, news, traces, presi,
music, contacts, storage, context, guides, research, chat, moodlit,
who) effectively had no backend in production. The frontend modules
shipped, but their compute calls fell through to localhost:3060 in
the browser and just failed.
This commit fixes the gap.
apps/api/Dockerfile (NEW)
-------------------------
Multi-stage Bun build that runs from the monorepo root so the four
workspace dependencies (@mana/shared-hono, @mana/shared-logger,
@mana/shared-storage, @mana/media-client) actually resolve. Builder
stage installs via pnpm with the --filter @mana/api... selector to
keep the install graph minimal; runtime stage copies the resulting
workspace tree (including the pnpm symlink farm) and runs the entry
script with bun directly — no compile step, since bun handles
TypeScript natively.
@mana/media-client lives under services/mana-media/packages/client,
not packages/, so the COPY path is the awkward
services/mana-media/packages/client → ./services/mana-media/packages/
client mirror to keep the workspace layout intact.
Healthcheck hits /health every 30s with a 15s start period — same
shape as the other Bun services in this compose file.
docker-compose.macmini.yml — new mana-api service
-------------------------------------------------
Slotted between glitchtip-worker and the games section. Build
context is the monorepo root (`.`) because the Dockerfile needs the
workspace tree. Container name `mana-api`, image `mana-api:local`,
mem_limit 384m (higher than the smaller Bun services because the
unified server holds 17 modules' route definitions + Drizzle schema
caches in memory).
Environment wires up everything apps/api needs:
- MANA_AUTH_URL → mana-auth:3001 for JWT validation
- MANA_LLM_URL → mana-llm:3025 for chat / picture / who LLM calls
- MANA_SEARCH_URL → mana-search:3012 for guides / research
- MANA_CREDITS_URL → mana-credits:3002 for credit validation
- MANA_MEDIA_URL → mana-media:3011 for image uploads
- DATABASE_URL → mana_platform Postgres for the few server-side
state stores (research_results, presi share-links, traces guides)
- MANA_SERVICE_KEY → for the credit/auth service-to-service calls
- LOGGER_FORMAT=json → structured logs for grafana ingestion
- CORS_ORIGINS=https://mana.how → only the unified web origin
needs access, the standalone game frontends don't call this
Port 3060 is exposed on the host so cloudflared can route
api.mana.how → mana-api:3060 (separate Mac Mini side step, not
in this commit).
docker-compose.macmini.yml — mana-web wiring
--------------------------------------------
Two new env vars:
PUBLIC_MANA_API_URL=http://mana-api:3060
PUBLIC_MANA_API_URL_CLIENT=https://api.mana.how
The hooks.server.ts injection plumbing for window.__PUBLIC_MANA_API_URL__
already existed (added in an earlier sweep but never had a value to
inject). The CSP connect-src list and the SSR injection script tag
also already include PUBLIC_MANA_API_URL_CLIENT — so once the env
arrives, the existing client-side getManaApiUrl() helper picks it
up automatically.
mana-web also gets a depends_on entry on mana-api with
condition: service_healthy so the web container doesn't start
serving requests against a dead API.
Verification
------------
docker compose -f docker-compose.macmini.yml config validates
cleanly (no YAML errors). Image build is NOT exercised in this
commit — that happens on the Mac Mini via build-app.sh after the
push lands.
Out of scope for this commit (Mac Mini side, manual steps):
1. ssh mana-server, git pull
2. ./scripts/mac-mini/build-app.sh mana-api (first build, ~3-5 min)
3. ./scripts/mac-mini/build-app.sh mana-web (rebuild with new env)
4. cloudflared route: add api.mana.how → mana-api:3060 to
~/.cloudflared/config.yml and `systemctl restart cloudflared`
5. Test https://api.mana.how/health from anywhere
6. Test https://mana.how/who in a browser
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>