feat(infra): deploy mana-ai + wire Mission Grant keys via docker-compose

Wire the Mission Key-Grant feature into the production Mac Mini
compose stack so mana-ai can boot and mana-auth can mint grants.

- New mana-ai service block (port 3066) — 256m mem limit, depends on
  postgres + mana-llm, tick interval configurable via
  MANA_AI_TICK_INTERVAL_MS / MANA_AI_TICK_ENABLED. Pulls
  MANA_AI_PRIVATE_KEY_PEM from env; absent = grants silently disabled.
- mana-auth environment gains MANA_AI_PUBLIC_KEY_PEM (default empty
  so existing deployments without the keypair degrade to 503
  GRANT_NOT_CONFIGURED rather than failing to boot).
- mana-auth Dockerfile rewritten to the two-stage pnpm+bun pattern
  used by mana-credits/mana-events — required now that mana-auth has
  a @mana/shared-ai workspace dep. The previous single-stage
  Dockerfile with service-scoped build context couldn't resolve any
  @mana/* imports; that only worked historically because it fell
  through at runtime via a pre-built layer.
- mana-ai Dockerfile copies packages/shared-ai into the installer
  stage alongside shared-hono.

The build contexts for mana-auth flip from services/mana-auth to the
repo root. Existing CI/CD paths (scripts/mac-mini/build-app.sh) pass
through to docker compose build and pick up the new context
automatically — no script edits needed.

Flip-on procedure: on the Mac Mini, set MANA_AI_PUBLIC_KEY_PEM +
MANA_AI_PRIVATE_KEY_PEM in .env (already done, see
secrets/mana-ai/README.md on the host), then rebuild mana-auth +
build mana-ai.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-04-15 14:24:50 +02:00
parent 12072c6b6c
commit cb384bc7ef
3 changed files with 79 additions and 6 deletions

View file

@ -249,8 +249,8 @@ services:
mana-auth:
build:
context: services/mana-auth
dockerfile: Dockerfile
context: .
dockerfile: services/mana-auth/Dockerfile
image: mana-auth:local
container_name: mana-auth
restart: always
@ -273,6 +273,11 @@ services:
# KEK for the encryption-vault feature (Phase 9). Required in production
# — generate with `openssl rand -base64 32`. See services/mana-auth/CLAUDE.md.
MANA_AUTH_KEK: ${MANA_AUTH_KEK}
# RSA-OAEP-2048 public key of the mana-ai runner. Used to wrap
# per-mission data keys in POST /me/ai-mission-grant. Paired with
# MANA_AI_PRIVATE_KEY_PEM on the mana-ai service. Absent → endpoint
# returns 503 GRANT_NOT_CONFIGURED (graceful degrade).
MANA_AI_PUBLIC_KEY_PEM: ${MANA_AI_PUBLIC_KEY_PEM:-}
MANA_NOTIFY_URL: http://mana-notify:3013
MAX_DAILY_SIGNUPS: ${MAX_DAILY_SIGNUPS:-0}
# Must be a superset of TRUSTED_ORIGINS in
@ -290,6 +295,49 @@ services:
retries: 3
start_period: 40s
# ============================================
# Tier 1a': AI Mission Runner (Hono + Bun)
# Background ticker that plans due AI Missions and stages proposals
# back to user devices via mana-sync. Opt-in decrypt of encrypted
# inputs via the Mission Key-Grant flow (see services/mana-ai/CLAUDE.md
# and docs/plans/ai-mission-key-grant.md).
# ============================================
mana-ai:
build:
context: .
dockerfile: services/mana-ai/Dockerfile
image: mana-ai:local
container_name: mana-ai
restart: always
mem_limit: 256m
depends_on:
postgres:
condition: service_healthy
mana-llm:
condition: service_started
environment:
TZ: Europe/Berlin
NODE_ENV: production
PORT: 3066
SYNC_DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:-mana123}@postgres:5432/mana_sync
MANA_LLM_URL: http://mana-llm:3020
MANA_SERVICE_KEY: ${MANA_SERVICE_KEY}
TICK_INTERVAL_MS: ${MANA_AI_TICK_INTERVAL_MS:-60000}
TICK_ENABLED: ${MANA_AI_TICK_ENABLED:-true}
# RSA-OAEP-2048 private key paired with MANA_AI_PUBLIC_KEY_PEM on
# mana-auth. Used to unwrap per-mission data keys at tick time.
# Absent → all grants skip silently with reason="not-configured".
MANA_AI_PRIVATE_KEY_PEM: ${MANA_AI_PRIVATE_KEY_PEM:-}
ports:
- "3066:3066"
healthcheck:
test: ["CMD", "bun", "-e", "fetch('http://127.0.0.1:3066/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"]
interval: 120s
timeout: 10s
retries: 3
start_period: 40s
# ============================================
# Tier 1b: Credits Service (Hono + Bun)
# ============================================

View file

@ -9,6 +9,7 @@ WORKDIR /app
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY services/mana-ai/package.json ./services/mana-ai/
COPY packages/shared-hono ./packages/shared-hono
COPY packages/shared-ai ./packages/shared-ai
# Install only mana-ai and its workspace deps
RUN pnpm install --filter @mana/ai-service... --no-frozen-lockfile --ignore-scripts

View file

@ -1,12 +1,36 @@
# Install stage: use node + pnpm to resolve workspace dependencies.
# Build context must be the monorepo root (see docker-compose.macmini.yml).
FROM node:22-alpine AS installer
RUN corepack enable && corepack prepare pnpm@9.15.0 --activate
WORKDIR /app
# Copy workspace structure
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY services/mana-auth/package.json ./services/mana-auth/
COPY packages/shared-hono ./packages/shared-hono
COPY packages/shared-ai ./packages/shared-ai
# Install only mana-auth and its workspace deps
RUN pnpm install --filter @mana/auth... --no-frozen-lockfile --ignore-scripts
# Runtime stage: bun
FROM oven/bun:1 AS production
WORKDIR /app
COPY package.json bun.lock* ./
RUN bun install --frozen-lockfile 2>/dev/null || bun install
# Copy installed deps from installer stage
COPY --from=installer /app/node_modules ./node_modules
COPY --from=installer /app/services/mana-auth/node_modules ./services/mana-auth/node_modules
COPY --from=installer /app/packages ./packages
COPY src ./src
COPY tsconfig.json drizzle.config.ts ./
# Copy source
COPY services/mana-auth/package.json ./services/mana-auth/
COPY services/mana-auth/src ./services/mana-auth/src
COPY services/mana-auth/tsconfig.json services/mana-auth/drizzle.config.ts ./services/mana-auth/
WORKDIR /app/services/mana-auth
EXPOSE 3001