Replace @arcade/backend (NestJS) with @arcade/server (Hono/Bun). Same two endpoints, no auth required (public game generator): - POST /api/games/generate — AI game generation (Gemini, Claude, GPT) - POST /api/games/submit — Community game submission via GitHub PR - GET /health — Health check This removes the last remaining NestJS backend from the monorepo. NestJS is now completely gone — all servers use Hono + Bun. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
36 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Monorepo Overview
This is a pnpm workspace monorepo containing multiple product applications with shared packages. All projects use a local-first architecture (Dexie.js + mana-sync) with Hono/Bun compute servers and follow consistent patterns.
Package Manager: pnpm 9.15.0 (use pnpm for all commands)
Build System: Turborepo
Node Version: 20+
Detailed Guidelines
For comprehensive guidelines on code patterns and conventions, see the .claude/ directory:
| Document | Purpose |
|---|---|
.claude/GUIDELINES.md |
Main reference overview |
.claude/guidelines/code-style.md |
Formatting, naming, linting |
.claude/guidelines/database.md |
Drizzle ORM, schema patterns |
.claude/guidelines/testing.md |
Jest/Vitest, mock factories |
.claude/guidelines/hono-server.md |
Hono/Bun compute servers |
.claude/guidelines/error-handling.md |
Go-style Result types, error codes |
.claude/guidelines/sveltekit-web.md |
Svelte 5 runes, stores |
.claude/guidelines/expo-mobile.md |
React Native, NativeWind |
.claude/guidelines/authentication.md |
Mana Core Auth integration |
.claude/guidelines/design-ux.md |
UI patterns, animations, a11y |
Always consult these guidelines before making changes.
Projects
| Project | Description | Apps |
|---|---|---|
| manacore | Multi-app ecosystem platform | Mobile, Web |
| chat | AI chat application | Backend, Mobile, Web, Landing |
| picture | AI image generation | Backend, Mobile, Web, Landing |
| memoro | AI voice recording & memo management | Backend, Audio-Backend, Mobile, Web, Landing |
| manadeck | Card/deck management | Backend, Mobile, Web |
| todo | Task management | Backend, Web, Landing |
| calendar | Calendar & scheduling | Backend, Web, Landing |
| contacts | Contact management | Backend, Web |
| storage | Cloud file storage | Backend, Web |
| mukke | Music production | Backend, Web, Landing |
| zitare | Daily inspiration quotes | Web, Landing |
| clock | World clock, alarms, timers | Web, Landing |
| presi | Presentations | Mobile, Web, Landing |
| questions | Research assistant | Backend, Web |
| context | Document workspace | Backend, Mobile, Web |
| photos | Photo management | Web |
| nutriphi | Nutrition tracking | Backend, Web, Landing |
| planta | Plant care | Backend, Web |
| skilltree | Skill tracking | Web |
| citycorners | City guide for Konstanz | Web, Landing |
| inventar | Inventory management | Web |
| traces | City exploration | Backend, Mobile |
| times | Time tracking | Web |
| uload | URL shortener & link management | Server, Web, Landing |
| news | AI news reader & personal library | Server, Web, Landing |
| wisekeep | AI transcription & wisdom library | Server, Web, Landing |
| reader | Text-to-Speech with offline audio | Mobile |
| bauntown | Developer community website | Landing |
| moodlit | Ambient lighting & mood app | Server, Web, Landing |
| calc | Calculator & converter | Web |
| playground | LLM playground | Web |
Games (games/)
| Game | Description | Tech |
|---|---|---|
| arcade | AI browser games platform (22+ games) | SvelteKit, Hono+Bun, Gemini/Claude/GPT |
| voxelava | Voxel game | SvelteKit |
| whopixels | Phaser.js pixel game | Phaser, JavaScript |
| worldream | World exploration game | SvelteKit |
Archived Projects (apps-archived/)
Currently empty. To archive a project, move it from apps/ to apps-archived/ (excluded from workspace).
Development Commands
For detailed local development setup, see docs/LOCAL_DEVELOPMENT.md.
Quick Start (Recommended)
Use dev:*:full commands to start any app with automatic database setup:
pnpm docker:up # Start PostgreSQL, Redis, MinIO
pnpm dev:chat:full # Start chat with auth + auto DB setup
pnpm dev:zitare:full # Start zitare with auth + auto DB setup
pnpm dev:contacts:full # Start contacts with auth + auto DB setup
pnpm dev:calendar:full # Start calendar with auth + auto DB setup
pnpm dev:clock:full # Start clock with auth + auto DB setup
pnpm dev:todo:full # Start todo with auth + auto DB setup
pnpm dev:picture:full # Start picture with auth + auto DB setup
pnpm dev:uload:full # Start uload with auth + auto DB setup
These commands automatically:
- Create the database if missing
- Push the latest schema
- Start auth, backend, and web with colored output
Database Setup
pnpm setup:db # Setup ALL databases and schemas
pnpm setup:db:chat # Setup just chat
pnpm setup:db:auth # Setup just auth
Individual App Commands
# Start specific project (runs all apps in project)
pnpm run manacore:dev
pnpm run memoro:dev
pnpm run manadeck:dev
pnpm run picture:dev
pnpm run chat:dev
pnpm run zitare:dev
pnpm run contacts:dev
# Start specific app within project
pnpm run dev:chat:mobile # Just mobile app
pnpm run dev:chat:server # Just Hono/Bun server
pnpm run dev:chat:local # sync + server + web (no auth needed)
pnpm run dev:chat:app # Server + web together
# Build & quality
pnpm run build
pnpm run type-check
pnpm run format
Each project has its own CLAUDE.md with detailed project-specific commands.
Architecture Patterns
Monorepo Structure
manacore-monorepo/
├── apps/ # Active SaaS product applications
│ ├── chat/
│ │ ├── apps/
│ │ │ ├── server/ # Hono/Bun compute server
│ │ │ ├── mobile/ # Expo React Native app
│ │ │ ├── web/ # SvelteKit web app
│ │ │ └── landing/ # Astro marketing page
│ │ └── packages/ # Project-specific shared code
│ ├── manadeck/
│ ├── picture/
│ └── ...
├── apps-archived/ # Archived apps (excluded from workspace)
│ ├── bauntown/
│ ├── memoro/
│ ├── news/
│ ├── nutriphi/
│ ├── reader/
│ ├── uload/
│ └── wisekeep/
├── games/ # Game projects
│ ├── arcade/ # AI browser games platform (SvelteKit + Hono/Bun)
│ ├── voxelava/ # Voxel game
│ ├── whopixels/ # Phaser.js pixel game
│ └── worldream/ # World exploration game
├── services/ # Standalone microservices
│ ├── mana-auth/ # Central auth (Hono + Bun + Better Auth)
│ ├── mana-auth/ # Central auth rewrite (Hono + Bun + Better Auth)
│ ├── mana-credits/ # Credit system (Hono + Bun)
│ ├── mana-user/ # User settings & tags (Hono + Bun)
│ ├── mana-subscriptions/ # Subscription billing (Hono + Bun)
│ ├── mana-analytics/ # Feedback & analytics (Hono + Bun)
│ ├── mana-sync/ # Local-first data sync (Go, WebSocket)
│ ├── mana-search/ # Search & content extraction (Go)
│ ├── mana-crawler/ # Web crawler (Go)
│ ├── mana-api-gateway/ # API gateway + rate limiting (Go)
│ ├── mana-notify/ # Notifications: email, push, Matrix, webhook (Go)
│ ├── mana-matrix-bot/ # 21 Matrix bot plugins (Go)
│ ├── mana-media/ # Media platform: CAS, thumbnails (Hono + Bun)
│ ├── mana-llm/ # LLM abstraction (Python/FastAPI)
│ ├── mana-image-gen/ # AI image generation with FLUX (Python/FastAPI)
│ ├── mana-stt/ # Speech-to-text (Python/FastAPI)
│ ├── mana-tts/ # Text-to-speech (Python/FastAPI)
│ ├── mana-voice-bot/ # Voice assistant (Python/FastAPI)
│ └── mana-landing-builder/# Org landing pages (Astro → Cloudflare Pages)
├── packages/ # Monorepo-wide shared packages
└── docker/ # Docker configuration files
Standard Project Structure (inside apps/)
apps/{project}/
├── apps/
│ ├── server/ # Hono/Bun compute server (when present)
│ ├── mobile/ # Expo React Native app
│ ├── web/ # SvelteKit web app
│ └── landing/ # Astro marketing page
├── packages/ # Project-specific shared code
└── package.json
Turborepo Configuration
CRITICAL: Avoid Recursive Turbo Calls
Parent workspace packages (e.g., apps/chat/package.json, apps/zitare/package.json) must NEVER have scripts that call turbo run <task> for tasks that turbo orchestrates from the root.
// WRONG - Creates infinite recursion!
// apps/chat/package.json
{
"scripts": {
"type-check": "turbo run type-check", // DON'T DO THIS
"build": "turbo run build", // DON'T DO THIS
"lint": "turbo run lint" // DON'T DO THIS
}
}
// CORRECT - Let root turbo handle orchestration
// apps/chat/package.json
{
"scripts": {
"dev": "turbo run dev" // OK for dev (persistent task, scoped)
// No type-check, build, lint scripts - handled by root turbo
}
}
Why this matters: When root turbo runs type-check, it finds packages with type-check scripts and runs them. If that script is turbo run type-check, it spawns another turbo process that does the same thing → infinite loop. This causes tasks to run for 10+ minutes with thousands of duplicate task entries.
The dev script exception: Using turbo run dev in parent packages is acceptable because:
- It's typically run directly on that package (scoped)
- Dev tasks are persistent and turbo handles them differently
Current turbo.json settings:
concurrency: "5"- Parallel task limit (adjust based on machine)type-checkhasdependsOn: ["^type-check"]- Dependencies are checked first
Technology Stack by App Type
Mobile Apps (Expo):
- React Native 0.76-0.81 + Expo SDK 52-54
- Expo Router (file-based routing)
- NativeWind (Tailwind for React Native)
- Zustand (state management)
Web Apps (SvelteKit):
- SvelteKit 2.x + Svelte 5
- Tailwind CSS
- Supabase SSR auth
Landing Pages (Astro):
- Astro 5.x
- Tailwind CSS
- Static site generation
Compute Servers (Hono + Bun):
- Hono 4.x + Bun runtime
- TypeScript, Drizzle ORM (where needed)
@manacore/shared-honofor auth middleware
Authentication Architecture
All projects use mana-core-auth as the central authentication service:
┌─────────────┐ ┌─────────────┐ ┌────────────────┐
│ Client │────>│ Server │────>│ mana-auth │
│ (Web/Mobile)│ │ (Hono/Bun) │ │ (port 3001) │
└─────────────┘ └─────────────┘ └────────────────┘
│ │ │
│ Bearer token │ POST /validate │
│ │ {token} │
│ │<────────────────────│
│ │ {valid, payload} │
│<──────────────────│ │
│ Response │ │
Key Components
| Component | Purpose |
|---|---|
services/mana-auth |
Central auth service (Better Auth + EdDSA JWT) |
@manacore/shared-hono |
Shared Hono middleware for JWT validation |
@manacore/shared-auth |
Client-side auth for web/mobile apps |
Hono Server Auth Integration
All compute servers use @manacore/shared-hono for auth:
import { authMiddleware, healthRoute, errorHandler, notFoundHandler } from '@manacore/shared-hono';
const app = new Hono();
app.onError(errorHandler);
app.notFound(notFoundHandler);
app.route('/health', healthRoute('my-server'));
app.use('/api/*', authMiddleware());
// In route handlers, get user from context:
app.get('/api/v1/data', (c) => {
const userId = c.get('userId');
// ...
});
Required Environment Variables
# All servers need this
MANA_CORE_AUTH_URL=http://localhost:3001
CORS_ORIGINS=http://localhost:5173
JWT Token Structure (EdDSA)
{
"sub": "user-id",
"email": "user@example.com",
"role": "user",
"sid": "session-id",
"exp": 1764606251,
"iss": "manacore",
"aud": "manacore"
}
Testing Auth Integration
# 1. Start mana-auth
pnpm dev:auth
# 2. Start an app locally
pnpm dev:contacts:local
# 3. Get a token
TOKEN=$(curl -s -X POST http://localhost:3001/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com", "password": "password"}' | jq -r '.accessToken')
# 4. Call protected endpoint
curl http://localhost:3033/api/v1/import/vcard \
-H "Authorization: Bearer $TOKEN"
Adding a New App to SSO
When adding a new app that should participate in cross-app SSO, update all three locations:
trustedOriginsinservices/mana-core-auth/src/auth/better-auth.config.tsCORS_ORIGINSfor mana-auth indocker-compose.macmini.yml- Run
pnpm test -- src/auth/sso-config.spec.ts(fromservices/mana-core-auth/) to verify
Missing any of these will silently break SSO for that app.
Access Tier System (Phased Release)
Apps can be gated behind access tiers for phased rollouts (e.g., founder-only alpha, then beta, then public).
Tier Hierarchy
| Tier | Level | Who |
|---|---|---|
guest |
0 | Unauthenticated visitors (local-only) |
public |
1 | Any registered user (default for new signups) |
beta |
2 | Beta testers |
alpha |
3 | Alpha testers / internal |
founder |
4 | Founding members |
A user can access an app if their tier level >= the app's requiredTier level.
How It Works
mana-apps.tsdefinesrequiredTierper app (e.g.,requiredTier: 'founder')- Users table stores
accessTiercolumn (default:'public') - JWT includes a
tierclaim, set during token creation in better-auth config - AuthGate checks the tier client-side and shows an "access restricted" state if insufficient
Key Files
| File | Purpose |
|---|---|
packages/shared-branding/src/mana-apps.ts |
App registry with requiredTier |
services/mana-auth/src/db/schema/auth.ts |
accessTier column on users |
services/mana-auth/src/auth/better-auth.config.ts |
Adds tier to JWT claims |
packages/shared-auth-ui/src/components/AuthGate.svelte |
Client-side tier gating |
services/mana-auth/src/routes/admin.ts |
Admin API for tier management |
Gating an App
Pass requiredTier to AuthGate in the app's layout:
<AuthGate requiredTier="beta">
<slot />
</AuthGate>
The tier value comes from the app's entry in mana-apps.ts. Apps without requiredTier default to 'public' (accessible to all registered users).
Admin API
# Set a user's tier
PUT /api/v1/admin/users/:id/tier
{ "tier": "beta" }
# Get a user's tier
GET /api/v1/admin/users/:id/tier
# List users (includes tier)
GET /api/v1/admin/users
Releasing an App
To widen access, change requiredTier in mana-apps.ts:
// Founder-only alpha
{ id: 'myapp', requiredTier: 'founder' }
// Open to beta testers
{ id: 'myapp', requiredTier: 'beta' }
// Public release
{ id: 'myapp', requiredTier: 'public' }
No database migration needed -- just update the config and redeploy the app.
Search Architecture
Projects requiring web search and content extraction use mana-search as the central search service:
┌─────────────────────────────────────────────────────────────┐
│ Consumer Apps │
│ Questions │ Chat │ Project Doc Bot │ Future Apps │
└─────────────────────────┬───────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ mana-search (Port 3021) │
│ Search API │ Extract API │ Redis Cache │
└─────────────────────────┬───────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────┐
│ SearXNG (Port 8080, internal) │
│ Google │ Bing │ DuckDuckGo │ Wikipedia │ arXiv │ ... │
└─────────────────────────────────────────────────────────────┘
Key Components
| Component | Purpose |
|---|---|
services/mana-search |
Go search service with SearXNG + Redis |
| SearXNG | Meta-search engine aggregating multiple sources |
| Redis | Caching layer (search: 1h TTL, extract: 24h TTL) |
API Endpoints
# Web search
POST /api/v1/search
{
"query": "quantum computing",
"options": {
"categories": ["general", "science"],
"engines": ["google", "wikipedia"],
"limit": 10
}
}
# Extract content from URL
POST /api/v1/extract
{
"url": "https://example.com/article",
"options": { "includeMarkdown": true }
}
# Bulk extract (max 20 URLs)
POST /api/v1/extract/bulk
# Health & metrics
GET /health
GET /metrics
Search Categories
| Category | Engines |
|---|---|
general |
Google, Bing, DuckDuckGo, Brave, Wikipedia |
news |
Google News, Bing News |
science |
arXiv, Google Scholar, PubMed, Semantic Scholar |
it |
GitHub, StackOverflow, NPM, MDN |
Starting the Service
# Start SearXNG + Redis for local development
cd services/mana-search && docker-compose -f docker-compose.dev.yml up -d
# Start Go search service
cd services/mana-search && go run ./cmd/server
Environment Variables
# Consumer apps need this
MANA_SEARCH_URL=http://localhost:3021
# mana-search service config
SEARXNG_URL=http://localhost:8080
REDIS_HOST=localhost
REDIS_PORT=6379
CACHE_SEARCH_TTL=3600
CACHE_EXTRACT_TTL=86400
Usage in Server
// Direct fetch
const response = await fetch('http://mana-search:3021/api/v1/search', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: 'machine learning basics',
options: { categories: ['general', 'science'], limit: 5 }
})
});
const { results, meta } = await response.json();
Svelte 5 Runes Mode (Web Apps)
All SvelteKit apps use Svelte 5 runes:
// CORRECT - Svelte 5
let count = $state(0);
let doubled = $derived(count * 2);
$effect(() => {
console.log(count);
});
// WRONG - Old Svelte syntax
let count = 0;
$: doubled = count * 2;
Local-First Architecture
All web apps use a local-first data layer: reads/writes go to IndexedDB (Dexie.js) first, sync to server in the background. This enables guest mode, offline CRUD, and instant UI.
Key Components
| Component | Location | Purpose |
|---|---|---|
@manacore/local-store |
packages/local-store/ |
Dexie.js collections, sync engine, Svelte 5 reactive queries |
mana-sync |
services/mana-sync/ |
Go sync server (WebSocket push, field-level LWW conflict resolution) |
| Todo Hono Server | apps/todo/apps/server/ |
Lightweight compute server (RRULE, reminders, admin) on Bun |
Data Flow
Guest: App → IndexedDB (Dexie.js) → UI (no sync)
Logged in: App → IndexedDB → UI → SyncEngine → mana-sync (Go) → PostgreSQL
← WebSocket push ←
Migrated Apps (21/23)
| App | Collections | Status |
|---|---|---|
| Todo | tasks, projects, labels, taskLabels, reminders | Done |
| Zitare | favorites, lists | Done |
| Calendar | calendars, events | Done |
| Clock | alarms, timers, worldClocks | Done |
| Contacts | contacts | Done |
| ManaDeck | decks, cards | Done |
| Picture | images, boards, boardItems, tags, imageTags | Done |
| Presi | decks, slides | Done |
| Inventar | collections, items, locations, categories | Done |
| NutriPhi | meals, goals, favorites | Done |
| Planta | plants, plantPhotos, wateringSchedules, wateringLogs | Done |
| Storage | files, folders, tags, fileTags | Done |
| Chat | conversations, messages, templates | Done |
| Questions | collections, questions, answers | Done |
| Mukke | songs, playlists, playlistSongs, projects, markers | Done |
| Context | spaces, documents | Done |
| Photos | albums, albumItems, favorites, tags, photoTags | Done |
| SkilltTree | skills, activities, achievements | Done |
| CityCorners | locations, favorites | Done |
| Times | clients, projects, timeEntries, tags, templates, settings | Done |
| uLoad | links, tags, folders, linkTags | Done |
| Calc | calculations, savedFormulas | Done |
| ManaCore | userSettings, dashboardConfigs | Done |
Not migrated (no CRUD data model): Matrix (protocol client), Playground (stateless)
Dev Commands (Local-First Stack)
pnpm dev:sync # Go sync server (port 3050)
pnpm dev:sync:build # Compile Go binary
pnpm dev:todo:server # Hono/Bun compute server (port 3019)
pnpm dev:todo:local # Web + sync + server (no auth needed)
pnpm dev:todo:full # Everything incl. auth + DB setup
Adding Local-First to a New App
- Create
apps/{app}/apps/web/src/lib/data/local-store.ts— define collections withcreateLocalStore() - Create
apps/{app}/apps/web/src/lib/data/guest-seed.ts— onboarding data - Rewrite stores to use
collection.getAll()/collection.insert()instead of API calls - In layout:
await store.initialize(),store.startSync()on login,allowGuest={true}on AuthGate - Set
userEmail = ''for guests so PillNav shows login button - Add
GuestWelcomeModalfor first-visit experience
Architecture Plan
Full migration plan: .claude/plans/local-first-architecture-migration.md
Shared Packages (packages/)
| Package | Purpose |
|---|---|
@manacore/local-store |
Local-first data layer (Dexie.js + sync engine) |
@manacore/shared-hono |
Shared Hono middleware (auth, health, errors) |
@manacore/shared-auth |
Client-side auth service for web/mobile apps |
@manacore/shared-storage |
S3-compatible storage (MinIO) |
@manacore/shared-types |
Common TypeScript types |
@manacore/shared-utils |
Utility functions |
@manacore/shared-ui |
React Native UI components |
@manacore/shared-theme |
Theme configuration |
@manacore/shared-i18n |
Internationalization |
Import shared packages:
import { createAuthService } from '@manacore/shared-auth';
import { formatDate, truncate } from '@manacore/shared-utils';
Database (Supabase)
- All projects use Supabase for PostgreSQL database, auth, and storage
- Row Level Security (RLS) policies enforce access control via JWT claims
- Each project has its own Supabase project/schema
- Types typically generated via
supabase gen types
Object Storage (MinIO)
S3-compatible object storage for file uploads, generated images, etc.
Architecture
| Environment | Service | Purpose |
|---|---|---|
| Local + Production | MinIO (Docker) | S3-compatible storage |
Local Development
# Start infrastructure (includes MinIO)
pnpm docker:up
# MinIO Web Console: http://localhost:9001
# Username: minioadmin
# Password: minioadmin
# S3 API endpoint: http://localhost:9000
Pre-configured Buckets
| Bucket | Project | Purpose |
|---|---|---|
picture-storage |
Picture | AI-generated images |
chat-storage |
Chat | User file uploads |
manadeck-storage |
ManaDeck | Card/deck assets |
nutriphi-storage |
NutriPhi | Meal photos |
presi-storage |
Presi | Presentation slides |
calendar-storage |
Calendar | Calendar attachments |
contacts-storage |
Contacts | Contact avatars/files |
storage-storage |
Storage | Cloud drive files |
Usage in Server
import { createPictureStorage, generateUserFileKey, getContentType } from '@manacore/shared-storage';
const storage = createPictureStorage();
// Upload
const key = generateUserFileKey(userId, 'image.png');
const result = await storage.upload(key, buffer, {
contentType: getContentType('image.png'),
public: true,
});
// Download
const data = await storage.download(key);
// Presigned URLs
const uploadUrl = await storage.getUploadUrl(key, { expiresIn: 3600 });
Environment Variables
# MinIO (local + production via Docker)
S3_ENDPOINT=http://localhost:9000
S3_REGION=us-east-1
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin
Landing Pages (Cloudflare Pages)
All landing pages are deployed to Cloudflare Pages using Direct Upload via Wrangler CLI.
Landing Pages
| Project | Package | Cloudflare Project | URL |
|---|---|---|---|
| Chat | @chat/landing |
chat-landing |
https://chat-landing.pages.dev |
| Picture | @picture/landing |
picture-landing |
https://picture-landing.pages.dev |
| ManaCore | @manacore/landing |
manacore-landing |
https://manacore-landing.pages.dev |
| ManaDeck | @manadeck/landing |
manadeck-landing |
https://manadeck-landing.pages.dev |
| Zitare | @zitare/landing |
zitare-landing |
https://zitare-landing.pages.dev |
Local Deployment
# First time: Login to Cloudflare
pnpm cf:login
# Create projects (one-time setup)
pnpm cf:projects:create
# Deploy individual landing page
pnpm deploy:landing:chat
pnpm deploy:landing:picture
pnpm deploy:landing:manacore
pnpm deploy:landing:manadeck
pnpm deploy:landing:zitare
# Deploy all landing pages
pnpm deploy:landing:all
# List all projects
pnpm cf:projects:list
Adding New Landing Pages
- Create the landing page in
apps/{project}/apps/landing/ - Add
wrangler.toml:name = "{project}-landing" compatibility_date = "2024-12-01" pages_build_output_dir = "dist" - Add deploy script to root
package.json:"deploy:landing:{project}": "pnpm --filter @{project}/landing build && npx wrangler pages deploy apps/{project}/apps/landing/dist --project-name={project}-landing" - Create Cloudflare project:
npx wrangler pages project create {project}-landing --production-branch=main
Custom Domains
# Add custom domain to a project
npx wrangler pages project add-domain chat-landing chat.mana.how
Organization Landing Pages
Organizations can have their own landing pages at {slug}.mana.how, built and deployed automatically by the mana-landing-builder service.
# Start the builder service
pnpm dev:landing-builder
How it works:
- Org admin configures landing page at
/organizations/{id}/landingin the Manacore web dashboard - Config is stored in
organizations.metadata.landingPage(mana-core-auth) - On publish, the builder service generates a static Astro site from the config
- Site is deployed to Cloudflare Pages as
org-{slug}→{slug}.mana.how
Available themes: classic (dark, professional), warm (light, inviting)
Available sections: Hero, About/Features, Team, Contact, Footer
See services/mana-landing-builder/CLAUDE.md for full documentation.
ManaScore (Production Readiness)
ManaScore is the internal quality assessment system for all ManaCore apps. Each app is rated on a 0-100 scale across 8 categories plus extended metrics.
Location: apps/manacore/apps/landing/src/content/manascore/
Live: https://manacore-landing.pages.dev/manascore
Methodology: https://manacore-landing.pages.dev/manascore/about
Core Categories (8)
Backend, Frontend, Database, Testing, Deployment, Documentation, Security, UX
Extended Metrics
| Metric | Description |
|---|---|
| Score Trend | Historical score changes with sparkline visualization |
| Lighthouse | Performance, Accessibility, Best Practices, SEO |
| Dependency Health | Outdated packages, vulnerabilities by severity |
| API Conformity | 7 checks (responses, errors, pagination, versioning, docs, health, validation) |
| Cross-App Consistency | Shared package usage (auth, ui, theme, branding, i18n, error-tracking) |
| Analytics Maturity | 5 checks (page views, custom events, auth tracking, landing, dashboard) |
Ecosystem Health Score
Measures consistency and unification across all apps (vs. ManaScore which rates each app individually).
Dashboard: /manascore/ecosystem
Script: node scripts/ecosystem-audit.mjs — scans the monorepo, generates ecosystem-health.json
12 metrics with weighted average: Shared Packages (20%), Icon Consistency (10%), i18n (10%), Styles (10%), Local-First (8%), Error Boundaries (8%), TypeScript Strict (7%), Tests (7%), Modals (5%), Error Handling (5%), PWA (5%), Maintainability (5%).
Maturity Levels
| Level | Score | Meaning |
|---|---|---|
| Prototype | 0-25 | Proof of concept |
| Alpha | 26-50 | Basic functionality |
| Beta | 51-70 | Functional with gaps |
| Production | 71-85 | Stable, deployable |
| Mature | 86-100 | Fully production-ready |
Server Access
Mac Mini Production Server
The production environment runs on a Mac Mini, accessible via Cloudflare Tunnel.
Domain: mana.how
SSH: ssh mana-server (requires cloudflared and SSH config)
# SSH config (~/.ssh/config)
Host mana-server
HostName mac-mini.mana.how
User till
ProxyCommand /opt/homebrew/bin/cloudflared access ssh --hostname %h
Useful Commands
ssh mana-server # Connect to server
cd ~/projects/manacore-monorepo
./scripts/mac-mini/status.sh # Check all services
./scripts/mac-mini/deploy.sh # Pull & restart containers
./scripts/mac-mini/build-app.sh todo-web # Build & deploy single app
./scripts/mac-mini/build-app.sh --base # Rebuild base images
./scripts/mac-mini/health-check.sh # Run health checks
docker compose -f docker-compose.macmini.yml logs -f # View logs
Docker Base Images
All apps build on shared base images to reduce build time and memory usage:
sveltekit-base:local(docker/Dockerfile.sveltekit-base) — All shared packages for SvelteKit web appshono-server(docker/Dockerfile.hono-server) — Hono/Bun compute server template
Rebuild base images after shared package changes: ./scripts/mac-mini/build-app.sh --base
For detailed server documentation, see docs/MAC_MINI_SERVER.md.
Windows GPU Server
A Windows PC with an NVIDIA RTX 3090 (24 GB VRAM) handles GPU-intensive AI workloads.
Hostname: mana-server-gpu
IP: 192.168.178.11 (LAN only, no Cloudflare Tunnel yet)
SSH: ssh mana-gpu
# SSH config (~/.ssh/config)
Host mana-gpu
HostName 192.168.178.11
User tills
Hardware: NVIDIA GeForce RTX 3090, 24 GB VRAM, CUDA 12.9
Software: Python 3.11, Git, Windows 11
Working directory: C:\mana\ (services, venvs, models)
Planned services:
- Ollama (LLM inference with GPU acceleration)
- Mana STT (Speech-to-Text, Port 3020)
- Mana TTS (Text-to-Speech, Port 3022)
- Mana Image Gen (FLUX image generation, Port 3023)
For setup documentation, see docs/WINDOWS_GPU_SERVER_SETUP.md.
Adding Dependencies
# Add to workspace root (dev tools only)
pnpm add -D <package> -w
# Add to specific project
pnpm add <package> --filter memoro
# Add to specific app within project
pnpm add <package> --filter @memoro/mobile
# Add to shared package
pnpm add <package> --filter @manacore/shared-utils
Environment Variables
Centralized Development Environment
All development environment variables are managed from a single file: .env.development
# First-time setup: generates all app-specific .env files
pnpm setup:env
# This also runs automatically after `pnpm install`
The script reads .env.development and generates platform-specific .env files for each app with the correct prefixes:
- Expo mobile:
EXPO_PUBLIC_*prefix - SvelteKit web:
PUBLIC_*prefix - Hono/Bun server: No prefix
Key Files
.env.development- Central source of truth (committed to git)scripts/generate-env.mjs- Generation scriptapps/**/apps/**/.env- Generated files (gitignored)
Adding New Variables
- Add the variable to
.env.development - Update
scripts/generate-env.mjsto map it to the appropriate apps - Run
pnpm setup:envto regenerate
Platform Prefix Patterns
Mobile (Expo):
EXPO_PUBLIC_SUPABASE_URL=...
EXPO_PUBLIC_SUPABASE_ANON_KEY=...
EXPO_PUBLIC_MIDDLEWARE_API_URL=...
Web (SvelteKit):
PUBLIC_SUPABASE_URL=...
PUBLIC_SUPABASE_ANON_KEY=...
Server (Hono/Bun):
PORT=...
DATABASE_URL=...
MANA_CORE_AUTH_URL=...
Project-Specific Documentation
- docs/LOCAL_DEVELOPMENT.md - Database setup and
dev:*:fullcommands - docs/ENVIRONMENT_VARIABLES.md - Complete environment setup guide
- docs/DATABASE_MIGRATIONS.md - Migration best practices, CI/CD, rollback procedures
Each project has its own CLAUDE.md with detailed information:
apps/manacore/CLAUDE.md- Multi-app ecosystem, auth detailsapps/manadeck/CLAUDE.md- Card/deck managementapps/chat/CLAUDE.md- Chat API endpoints, AI modelsapps/picture/CLAUDE.md- AI image generationservices/mana-core-auth/- Central authentication serviceservices/mana-search/CLAUDE.md- Search & content extraction service (Go)services/mana-crawler/CLAUDE.md- Web crawler service (Go)services/mana-notify/CLAUDE.md- Notification service (Go)services/mana-llm/CLAUDE.md- Central LLM abstraction serviceservices/mana-landing-builder/CLAUDE.md- Org landing page builder service
Navigate to the specific project directory to work on it.
Code Quality Infrastructure (TODO)
A detailed plan for code quality tooling is available at .claude/plans/proud-dancing-moon.md. When ready to implement:
Planned Setup
- Pre-commit hooks: Husky + lint-staged (format + lint on commit)
- Commit messages: Commitlint with Conventional Commits (
feat:,fix:,docs:, etc.) - CI Pipeline: GitHub Actions PR checks (lint, format, type-check, tests)
- Formatting: Tabs, single quotes, 100 char width (unified across all projects)
- Test coverage: 80% minimum for new code (once testing infrastructure is in place)
Key Files to Create
.husky/pre-commit # Run lint-staged
.husky/commit-msg # Run commitlint
commitlint.config.js # Conventional commit rules
.github/workflows/pr-check.yml # CI pipeline
packages/eslint-config/ # Shared ESLint configuration
Current State
- Testing: ~128 test files across all projects (unit + e2e + integration)
- Linting: Husky + lint-staged on pre-commit (format + lint)
- CI: PR validation (type-check, lint, format, tests) + CD pipeline with deploy tracking
- Pre-commit: Husky runs lint-staged on every commit