Foundation for autonomous Claude-driven testing. Plan:
docs/plans/mana-mcp-and-personas.md.
New packages
- @mana/tool-registry — schema-first ToolSpec<InputSchema, OutputSchema>
with zod generics, scope ('user-space' | 'admin') and policyHint
('read' | 'write' | 'destructive'). sync-client helpers speak the
mana-sync push/pull protocol directly so RLS and field-level LWW are
preserved. MasterKeyClient fetches per-user MKs via the existing
mana-auth GET /api/v1/me/encryption-vault/key endpoint (JWT-gated,
ZK-aware, already audited) — no new service-key endpoint built.
ZeroKnowledgeUserError surfaced as a typed throw.
- @mana/shared-crypto — AES-GCM-256 primitives extracted from the web
app's $lib/data/crypto/aes.ts so the server-side tool handlers and the
browser produce byte-for-byte identical wire format
(enc:1:{b64(iv)}.{b64(ct)}). Web app aes.ts now re-exports from
shared-crypto — 5 existing importers unchanged, svelte-check stays
green.
New service
- services/mana-mcp (:3069, Bun/Hono) — MCP Streamable HTTP gateway.
JWKS auth against mana-auth, per-user session isolation (session-id
belongs to the user who opened it — cross-user access returns 403),
admin-scoped tools filtered out before registration. MasterKeyClient
cached per process with a 5-minute TTL.
11 tools registered
- habits.{create,list,update,archive}, spaces.list (plaintext, M1)
- todo.{create,list,complete}, notes.{create,search}, journal.add
(encrypted — field lists match
apps/mana/apps/web/src/lib/data/crypto/registry.ts verbatim)
Infra
- Port 3069 added to docs/PORT_SCHEMA.md
- services/mana-mcp/CLAUDE.md with architecture, auth model,
tool-authoring recipe, local smoke-test steps
- Root CLAUDE.md services list updated
Type-check green across shared-crypto, mana-tool-registry, mana-mcp.
svelte-check on apps/mana/apps/web stays at 0 errors / 0 warnings.
Boot smoke verified: /health returns registry.loaded=true, unauthed
/mcp → 401, invalid-JWT /mcp → 401 with descriptive message.
Decisions locked in for later milestones (per plan D1–D10):
- Personas will be real mana-auth users (users.kind='persona'), no
service-key bypass (D1, D2)
- Tool-registry is the SSOT; mana-ai and the legacy
apps/api/src/mcp/server.ts get merged into it in M4 (three current
parallel tool catalogs collapse to one)
- Persona-runner (:3070) will be a separate service using the Claude
Agent SDK + MCP client (D5)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
CLAUDE.md
Guidance for Claude Code when working in this repo.
Monorepo Overview
pnpm workspace monorepo with two consolidated tops:
apps/mana/apps/web— unified SvelteKit frontend serving 27+ product modules undermana.how. One build, one IndexedDB, one auth session, one deployment.apps/api(@mana/api) — unified Hono/Bun backend API server. Consolidates per-module compute servers; routes registered under/api/v1/{module}/*.
Per-product directories under apps/{product}/ still exist for landing pages and product-specific packages, but the active web frontend and API both live in the two consolidated apps above. The only remaining mobile app is apps/memoro/apps/mobile (Expo SDK 55) — all other per-product mobile apps were removed on 2026-04-20.
- Package Manager: pnpm 9.15.0
- Build System: Turborepo
- Node: 20+
- Primary doc:
apps/mana/CLAUDE.md— module structure, data layer, encryption, routing.
Repo layout
apps/
├── mana/ # Unified frontend (SvelteKit web + Astro landing)
├── api/ # Unified backend API (Hono/Bun) — @mana/api
├── memoro/apps/ # Only remaining mobile app (Expo SDK 55)
├── {product}/ # Per-product landing pages, packages
│ # Standalone (own container, not unified): manavoxel
games/ # arcade, voxelava, whopixels, worldream
services/ # Backend services (Hono/Bun, Go, Python) — see list below
packages/ # Shared workspace packages (@mana/*)
docs/ # Long-form docs (deployment, hardware, postmortems, etc.)
.claude/guidelines/ # Coding conventions — read before changing code
Active services (services/)
mana-auth (3001), mana-sync (3050), mana-credits, mana-user, mana-subscriptions, mana-analytics, mana-search (3021), mana-crawler, mana-api-gateway, mana-notify, mana-media, mana-llm, mana-image-gen, mana-video-gen, mana-stt, mana-tts, mana-voice-bot, mana-events, mana-geocoding (3018), mana-landing-builder, mana-ai (3067, background AI Mission Runner — see services/mana-ai/CLAUDE.md), mana-research (3068, web research provider orchestration across 16+ providers — see services/mana-research/CLAUDE.md and docs/plans/mana-research-service.md), mana-mcp (3069, MCP gateway exposing the shared tool-registry to Claude Desktop / Claude Code / persona-runner — see services/mana-mcp/CLAUDE.md and docs/plans/mana-mcp-and-personas.md). Each non-trivial service has its own CLAUDE.md.
Coding Guidelines
Always consult before changing code:
| Document | Purpose |
|---|---|
.claude/GUIDELINES.md |
Overview |
.claude/guidelines/code-style.md |
Formatting, naming, linting |
.claude/guidelines/sveltekit-web.md |
Svelte 5 runes, stores |
.claude/guidelines/expo-mobile.md |
React Native, NativeWind |
.claude/guidelines/hono-server.md |
Hono/Bun servers |
.claude/guidelines/database.md |
Drizzle ORM, pgSchema |
.claude/guidelines/authentication.md |
Mana Auth integration |
.claude/guidelines/error-handling.md |
Result types, error codes |
.claude/guidelines/testing.md |
Vitest, mock factories |
.claude/guidelines/design-ux.md |
UI patterns, a11y |
.claude/guidelines/ai-tools.md |
Adding AI tools to a module |
Development Quick Start
See docs/LOCAL_DEVELOPMENT.md for the full setup.
pnpm docker:up # PostgreSQL, Redis, MinIO
pnpm setup:env # Generate per-app .env files from .env.development
pnpm setup:db # Create databases + push schemas
# Start the unified Mana app (most common)
pnpm run mana:dev
# Project-specific full stack (auth + backend + web with auto DB setup)
pnpm dev:chat:full
pnpm dev:todo:full
pnpm dev:picture:full
# … one per project
# Service-only
pnpm dev:auth # mana-auth (3001)
pnpm dev:sync # mana-sync Go server (3050)
Quality:
pnpm run build
pnpm run type-check
pnpm run format
pnpm run validate:all # turbo recursion + pgSchema + crypto registry — run before push
pnpm run test:coverage # emit v8 coverage under per-package coverage/
validate:all is the local mirror of the CI validate job — it runs in
seconds and fails fast on any of the three invariant checks. Use it as a
pre-push gate.
Key Architecture Notes
These are the patterns that span the repo. Service-/app-specific details live in their own CLAUDE.md.
Local-first data layer
The unified Mana app uses one IndexedDB (mana) with all 120+ collections. Module stores write directly to Dexie tables; hooks in database.ts track changes into _pendingChanges tagged by appId. The unified sync engine (sync.ts) groups by appId and pushes to mana-sync (Go, port 3050), which persists field-level LWW into PostgreSQL with RLS.
Full architecture, sprint history, threat model:
At-rest encryption
Sensitive user content in 27 tables is AES-GCM-256 encrypted before hitting IndexedDB. Master key lives in mana-auth, KEK-wrapped (MANA_AUTH_KEK env, must be set in prod). Optional zero-knowledge mode via Settings → Sicherheit.
When touching sensitive fields:
- Add the table to
apps/mana/apps/web/src/lib/data/crypto/registry.tswith the field allowlist await encryptRecord(tableName, record)before writesawait decryptRecords(tableName, visible)after Dexie reads, before the type converter
Default new user-typed fields to encrypt; default new IDs/timestamps/sort-keys to plaintext.
Authentication
All servers use @mana/shared-hono with authMiddleware(). Tokens are EdDSA JWTs issued by mana-auth with claims {sub, email, role, sid, tier, exp, iss, aud}. Cross-app SSO works across *.mana.how. See .claude/guidelines/authentication.md and services/mana-auth/.
Adding an app to SSO requires updating all three:
PRODUCTION_TRUSTED_ORIGINSinservices/mana-auth/src/auth/sso-origins.ts(the SSOT — better-auth.config.ts re-exports from here)CORS_ORIGINSfor mana-auth indocker-compose.macmini.yml- Run
bun test src/auth/sso-config.spec.tsfromservices/mana-auth/— now hard-fails on drift in either direction
Access tiers
guest < public < beta < alpha < founder. Apps gate themselves via requiredTier in packages/shared-branding/src/mana-apps.ts; the JWT carries a tier claim; AuthGate enforces it client-side. Admin API at PUT /api/v1/admin/users/:id/tier.
Database (PostgreSQL)
Two databases: mana_platform (all services + app server-side data, schema-isolated via pgSchema) and mana_sync (sync engine, write-heavy). Always use pgSchema('name').table(...), never plain pgTable(). Adding a new schema: see .claude/guidelines/database.md.
Object storage
MinIO (Docker, S3-compatible) in both local and prod. Console: http://localhost:9001 (minioadmin/minioadmin). Use @mana/shared-storage helpers. Pre-configured per-project buckets (picture-storage, chat-storage, cards-storage, …).
Turborepo: avoid recursive turbo calls
CRITICAL: Parent workspace packages (e.g. apps/chat/package.json) must NEVER define type-check, build, lint, test, test:coverage, or check scripts that call turbo run <task>. Root turbo already orchestrates those — defining them in children causes infinite recursion (10+ minute hangs, thousands of duplicate tasks). Only dev is OK to delegate to turbo from a parent package, since it's persistent and typically scoped.
Enforced by pnpm run validate:turbo (scripts/validate-no-recursive-turbo.mjs), wired into the CI validate job — a new turbo run build inside a non-root package.json now fails the PR.
Shared Packages (packages/)
| Package | Purpose |
|---|---|
@mana/shared-auth |
Client-side auth for web/mobile |
@mana/shared-hono |
Hono middleware (auth, health, errors) |
@mana/shared-storage |
S3/MinIO helpers |
@mana/shared-branding |
App registry, tiers, branding |
@mana/shared-types |
Common TS types |
@mana/shared-utils |
Utility functions |
@mana/shared-ui |
React Native UI components |
@mana/shared-theme |
Theme config |
@mana/shared-i18n |
i18n |
@mana/local-store |
Local-first store primitives — used by unified Mana, manavoxel, arcade, and shared-uload/-stores/-links |
@mana/local-llm |
Browser-local LLM inference (transformers.js + Gemma 4 E2B, WebGPU). Powers /llm-test and the playground module. See packages/local-llm/CLAUDE.md for the CSP requirements and the transformers.js v4 gotchas. |
@mana/local-stt |
Browser-local speech-to-text (transformers.js + Whisper, WebGPU). Powers the QuickInputBar mic button. Same architecture as local-llm. See packages/local-stt/CLAUDE.md. |
Adding Dependencies
pnpm add -D <pkg> -w # Workspace root (dev tools)
pnpm add <pkg> --filter @mana/web # A specific app
pnpm add <pkg> --filter @mana/shared-utils # A shared package
Environment Variables
Single source of truth: .env.development (committed). After editing, run pnpm setup:env to regenerate per-app .env files with the right prefixes (EXPO_PUBLIC_* for mobile, PUBLIC_* for SvelteKit, no prefix for Hono/Bun servers). Mapping logic in scripts/generate-env.mjs. Full guide: docs/ENVIRONMENT_VARIABLES.md.
Server Access
- Production (Mac Mini):
ssh mana-server(Cloudflare Tunnel). Seedocs/MAC_MINI_SERVER.md. Useful:./scripts/mac-mini/status.sh,./scripts/mac-mini/deploy.sh,./scripts/mac-mini/build-app.sh <app>. - GPU server (Windows, RTX 3090):
ssh mana-gpu(192.168.178.11, LAN only). Hosts STT/TTS/image-gen/video-gen/Ollama. Seedocs/WINDOWS_GPU_SERVER_SETUP.md.
Reference Docs
| Path | When you need it |
|---|---|
apps/mana/CLAUDE.md |
Default — module pattern, routing, encryption usage |
apps/mana/apps/web/src/lib/data/DATA_LAYER_AUDIT.md |
Sync engine deep-dive, encryption rollout, threat model |
docs/LOCAL_DEVELOPMENT.md |
First-time setup, dev:*:full commands |
docs/ENVIRONMENT_VARIABLES.md |
All env vars |
docs/DATABASE_MIGRATIONS.md |
Migration workflow + rollback |
docs/DEPLOYMENT.md |
Production deployment |
docs/PORT_SCHEMA.md |
Which service runs on which port |
Service-specific CLAUDE.md files |
Service internals |