Rewrite the central authentication service from NestJS to Hono + Bun.
Uses Better Auth's native fetch-based handler — no Express conversion.
Key architecture changes:
- Better Auth handler mounted directly on Hono (app.all('/api/auth/*'))
- No NestJS DI, modules, guards, decorators — plain TypeScript
- JWT validation via jose (same as extracted services)
- Email via nodemailer (simplified, German templates)
- ~1,400 LOC vs ~11,500 LOC in NestJS (88% reduction)
Service structure:
- auth/better-auth.config.ts — copied from mana-core-auth (framework-agnostic)
- auth/stores.ts — in-memory stores for email redirect URLs
- email/send.ts — nodemailer email functions
- middleware/ — JWT auth, service auth, error handler (shared pattern)
- db/schema/ — copied from mana-core-auth (Drizzle, framework-agnostic)
Port: 3001 (same as mana-core-auth — drop-in replacement)
Database: mana_auth (same DB, same schemas)
Better Auth plugins: Organization, JWT (EdDSA), OIDC Provider,
Two-Factor (TOTP), Magic Link
Note: This is the initial version. Guilds, API keys, Me (GDPR),
security (lockout/audit), and admin endpoints will be added
incrementally. The old mana-core-auth remains until fully replaced.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract feedback, analytics, and AI modules from mana-core-auth into
standalone mana-analytics service (Hono + Bun, Port 3064).
New service (services/mana-analytics/):
- User feedback CRUD with voting
- AI-powered feedback title generation via mana-llm
- Simplified from DuckDB analytics to pure PostgreSQL
- ~550 LOC
Removed from mana-core-auth:
- feedback/ module (6 files)
- analytics/ module (4 files)
- ai/ module (3 files)
- db/schema/feedback.schema.ts
mana-core-auth now contains ONLY pure auth:
- Better Auth (JWT, Sessions, 2FA, Passkeys, OIDC, Magic Links)
- Organizations/Guilds (membership management)
- API Keys, Security, Me (GDPR), Health, Metrics
- Ready for Phase 5: Hono rewrite
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Photos NestJS backend was a proxy to mana-media that enriched
responses with local album/favorite/tag data. Now:
- Albums store → local-first via albumCollection + albumItemCollection
- Favorites → local-first via favoriteCollection (toggle in IndexedDB)
- Photo tags → local-first via photoTagCollection
- Photo listing/stats → direct mana-media API calls from frontend
- Upload → direct mana-media upload from frontend
- Delete → direct mana-media delete from frontend
Removed 27 TypeScript files, 1 Docker container, 1 port (3039).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract subscription billing into standalone mana-subscriptions service
(Hono + Bun, Port 3063). Also removes Stripe module from mana-core-auth
since subscription webhooks are the last consumer.
New service (services/mana-subscriptions/):
- Plans CRUD, subscription management, Stripe Checkout + Portal
- Invoice tracking, webhook handlers for sub/invoice events
- Internal API for plan limits (consumed by guilds service)
- ~990 LOC (vs ~1,700 in NestJS incl. Stripe module)
Removed from mana-core-auth:
- subscriptions/ module (6 files)
- stripe/ module (4 files) — no longer needed in auth
- db/schema/subscriptions.schema.ts
- guilds.service.ts: replaced direct DB plan limit query with
HTTP call to mana-subscriptions internal API
mana-core-auth now contains only:
- Auth (Better Auth, JWT, Sessions, 2FA, Passkeys, OIDC)
- Organizations/Guilds (membership only, no credits/plans)
- API Keys, Security, Me (GDPR), Health, Metrics
- Feedback + Analytics (next extraction target)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Presi NestJS backend (40 source files, 50 deps) was a CRUD wrapper
around decks, slides, and themes — all now handled by local-first sync.
Only the share-link feature requires server-side state (public URLs
without auth), so a minimal Hono + Bun server replaces the entire
NestJS backend:
- apps/presi/apps/server/ — Hono server with share routes + GDPR admin
Uses @manacore/shared-hono for auth (JWKS), health, admin, errors
- Web app API client stripped to share-only (was 270 lines → 90 lines)
- Removed from docker-compose, CI/CD, Prometheus, env generation
- NestJS backend deleted (40 TS files, 8 test specs, 3038 lines)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Todo: Replace manual fetch/state stores with useLiveQuery() for tasks,
projects, and tags. Components use Svelte context instead of store imports.
Stores reduced to mutation-only services. Removes ~200 lines of manual
state management. Enables multi-tab sync and auto-refresh on data changes.
- Tags (all 16 apps): Migrate from API-based createTagStore() to shared
local-first IndexedDB ('manacore-tags'). Tags now work offline and in
guest mode with default seed data. All apps share the same tag DB via
tagLocalStore + useAllTags() + setContext pattern.
- Cleanup: Delete unused Todo API files (projects.ts, labels.ts,
reminders.ts), remove dead labels store, clean up barrel exports.
Apps migrated: Todo, Zitare, Questions, Planta, Clock, Presi, Mukke,
Context, CityCorners, ManaDeck, Chat, Contacts, Calendar, Picture,
Storage, Photos
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both apps are fully local-first via Dexie.js + mana-sync. Their NestJS
backends were pure CRUD wrappers (20 + 31 source files) that are no
longer needed.
Changes:
- Add packages/shared-hono: JWT auth via JWKS (jose), Drizzle DB factory,
health route, generic GDPR admin handler, error middleware
- Migrate zitare lists page from fetch() to listsStore (local-first)
- Rewrite clock timers store from API-based to timerCollection (Dexie)
- Update clock +layout.svelte CommandBar search to use local collections
- Remove zitare-backend + clock-backend from docker-compose, CI/CD,
Prometheus, env generation, setup scripts
- Add docs/TECHNOLOGY_AUDIT_2026_03.md with full repo analysis
Net result: -2 Docker containers, -2 ports, -2728 lines of code
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clock app was migrated to local-first (mana-sync), remove stale
depends_on and environment references to clock-backend.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary document covering:
- Local-first status for all 19/22 apps (which stores are done vs pending)
- Service extraction progress (mana-credits, mana-user done)
- What's left to extract (subscriptions, analytics)
- Phase 5 plan (auth Hono rewrite)
- All commits from this session
- Prioritized next steps
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- mana-sync on port 3051 (Go sync server for local-first apps)
- mana-notify-go on port 3040 (Go notification service)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Required for Docker Desktop Mac (TLS proxy) and sites with
self-signed/expired certificates. Crawlers routinely need this.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the NestJS mana-notify service with a Go implementation.
Features: 4 notification channels (email/SMTP, Expo push, Matrix,
webhook), goroutine worker pool with retry/backoff (replaces BullMQ),
Go template engine (replaces Handlebars), PostgreSQL with auto-migrations
(5 tables), user preferences with quiet hours, idempotency via
externalId, batch sending, scheduled delivery, JWT + service key auth.
22 API endpoints, 1:1 compatible. Binary: 21 MB.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Port 3060 was already taken by api-gateway. Updated mana-credits to 3061.
Changes:
- docker-compose.macmini.yml: Add mana-credits service with health check,
traefik labels for credits.mana.how, depends on postgres
- docker-compose.macmini.yml: Add MANA_CREDITS_URL to mana-auth env
- Update all port references from 3060 to 3061 (config, Dockerfile, CLAUDE.md)
- Update better-auth.service.ts fallback URLs to 3061
- Update .env.development MANA_CREDITS_URL
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Configure all apps with gpu-llm.mana.how as fallback when MANA_LLM_URL
is not set. This ensures apps can use the GPU server's local LLM models
(Ollama gemma3, qwen2.5-coder) instead of cloud providers.
Apps updated:
- Chat: LLM fallback to GPU server
- Context: LLM fallback (replaces Azure OpenAI dependency)
- NutriPhi: LLM + Vision fallback (replaces Google Gemini for food analysis)
- Planta: LLM + Vision fallback (replaces Google Gemini for plant analysis)
- ManaDeck: LLM + Vision fallback for card generation
- Traces: LLM fallback for AI city guides
Vision model default: ollama/gemma3:12b (multimodal, runs on RTX 3090)
Added VISION_MODEL to .env.development
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update consumers to call the new standalone mana-credits service instead
of the credit endpoints embedded in mana-core-auth.
Changes:
- CreditClientService: Add getCreditsUrl() reading MANA_CREDITS_URL
(falls back to MANA_CORE_AUTH_URL for backward compatibility).
All credit calls now use /api/v1/internal/* endpoints.
- BetterAuthService: Replace direct DB inserts for credit balance and
guild pool init with HTTP calls to mana-credits internal API.
Replace local gift redemption with HTTP call.
- .env.development: Add MANA_CREDITS_URL=http://localhost:3060
- CLAUDE.md: Add mana-credits to services list
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix response body leak in SearXNG HealthCheck (defer resp.Body.Close)
- Handle ignored errors in HTTP request creation
- Add panic recovery in BulkExtract goroutines
- Add request body size limit (1 MB) via http.MaxBytesReader
- Add MaxHeaderBytes to HTTP server
- Sort engine list for deterministic responses
- Fix variable shadowing (r → res in loop)
- Run as non-root user in Docker container
- Log shutdown errors
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extract the credit system from mana-core-auth into a standalone service.
Uses Hono framework on Bun runtime instead of NestJS.
Service includes:
- Personal credit balance with optimistic locking
- Immutable transaction ledger
- Stripe payment integration (PaymentIntents, Checkout Sessions)
- Guild shared pools with per-member spending limits
- Gift code system (simple, personalized, split, first_come, riddle)
- Service-to-service internal API (X-Service-Key auth)
- JWT validation via JWKS from mana-core-auth (jose library)
Architecture:
- 27 files, ~2.2k LOC (vs ~4.1k in NestJS)
- Drizzle ORM schemas adapted for standalone DB (no FK to auth tables)
- Zod validation instead of class-validator
- Manual service instantiation instead of NestJS DI
- Hono middleware for JWT + service key auth
Port: 3060
Database: mana_credits (separate from mana_auth)
Next steps: Update CreditClientService URL, update mana-core-auth
registration hooks, configure Docker + Cloudflare Tunnel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Picture App:
- Update LocalImageGenService to use GPU server (gpu-img.mana.how)
- Add API key authentication (GPU_API_KEY)
- Increase timeout to 120s (VRAM may need model loading time)
Chat App:
- Add VoiceModule with STT/TTS integration via GPU server
- POST /api/v1/voice/transcribe — Upload audio, get text + word timestamps
- POST /api/v1/voice/synthesize — Send text, get audio response
- GET /api/v1/voice/health — Check GPU voice services availability
- Supports speaker diarization and language selection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaces the NestJS mana-search service with a Go implementation for
lower resource usage and faster startup. All 7 API endpoints are 1:1
compatible (search, extract, bulk extract, engines, health, metrics,
cache clear). Uses go-readability for content extraction and
html-to-markdown for Markdown conversion. Redis cache with graceful
degradation, Prometheus metrics, and structured JSON logging.
Binary: 22 MB vs ~200+ MB node_modules.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add API key authentication to all GPU services (X-API-Key header)
- /health and /docs remain public (no key needed)
- Shared key configured via GPU_API_KEY env variable
- Add VRAM auto-unload for mana-image-gen (5min) and mana-stt (10min)
- FLUX.2 pipeline freed after idle, recovering ~13GB VRAM
- WhisperX models freed after idle, recovering ~3GB VRAM
- Install Piper TTS voices (Thorsten + Kerstin) for local German TTS
- Update @manacore/shared-gpu client to support apiKey parameter
- Add GPU_API_KEY to .env.development
- Document API auth and VRAM management in setup guide
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Migration plan Phase 3: updated from 8/8 to 19/19 with full app table
- Added server-side logic column showing what remains per app
- Listed 3 apps not migrated (ManaCore, Matrix, Playground) with reasons
- CLAUDE.md: clarified 19/22 count and added "not migrated" note
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove the custom IndexedDB implementation (idb package + services/storage.ts)
and rewrite skills + achievements stores to use @manacore/local-store collections.
Changes:
- Rewrite skills.svelte.ts: all CRUD via skillCollection/activityCollection
- Rewrite achievements.svelte.ts: all persistence via achievementCollection
- Delete services/storage.ts (282 lines of custom idb code)
- Remove idb dependency from package.json
- Simplify layout comments
The stores now follow the same pattern as all other migrated apps:
reads/writes go to IndexedDB (Dexie.js), sync happens automatically
via mana-sync when authenticated.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Cloudflare Tunnel: api.mana.how → localhost:3060 (Go API Gateway)
- Prometheus: scrape targets for mana-api-gateway:3060 and mana-matrix-bot:4000
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add IndexedDB data layer to 5 more Tier 2 apps, bringing the total to
17/22 apps with local-first architecture.
For each app:
- Create local-store.ts with typed collections and sync config
- Create guest-seed.ts with onboarding data for guest mode
- Update layout with AuthGate allowGuest={true} + local store init
- Add GuestWelcomeModal for first-visit experience
App-specific changes:
- Chat: Add store init to existing AuthGate, keep session-based guest mode
- Questions: Replace manual onMount auth with AuthGate wrapper
- Mukke: Add allowGuest to existing AuthGate, audio files stay server-side
- Context: Replace manual onMount auth with AuthGate, AI stays server-side
- Photos: Add allowGuest to existing AuthGate, photos stay on mana-media
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace 21 separate NestJS Matrix bot processes (~2.1 GB RAM, ~4.2 GB Docker images)
with a single Go binary using plugin architecture (8.6 MB binary, ~30 MB RAM).
New services:
- services/mana-matrix-bot/ — Go Matrix bot with 21 plugins (mautrix-go, Redis sessions)
- services/mana-api-gateway-go/ — Go API gateway (rate limiting, API keys, credit billing)
Deleted:
- 21 services/matrix-*-bot/ directories
- packages/bot-services/ and packages/matrix-bot-common/
- Legacy deploy scripts and CI build jobs
Updated:
- docker-compose.macmini.yml: new Go services, legacy bots removed
- CI/CD: change detection + build jobs for Go services
- Root package.json: new dev:matrix, build:matrix, test:matrix scripts
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add IndexedDB data layer (Dexie.js via @manacore/local-store) to 6 more apps,
bringing the total to 12/22 apps with local-first architecture.
For each app:
- Create local-store.ts with typed collections and sync config
- Create guest-seed.ts with onboarding data for guest mode
- Update layout with AuthGate allowGuest={true} + handleAuthReady()
- Add GuestWelcomeModal for first-visit experience
- Add @manacore/local-store dependency
App-specific changes:
- Presi: Rewrite decks store from API to IndexedDB, conditional share button
- Picture: Rewrite gallery + boards pages to read from IndexedDB
- Inventar: Replace manual auth $effect with AuthGate, keep localStorage stores
- NutriPhi: Add onReady handler to existing AuthGate
- Planta: Add allowGuest + sync init to existing AuthGate
- Storage: Add local store init to existing handleAuthReady
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- CLAUDE.md: new Local-First Architecture section with data flow, migrated apps,
dev commands, and step-by-step guide for adding local-first to new apps
- Migration plan: Phase 1-3 marked as done, status updated to "in progress"
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Roll out @manacore/local-store to 4 more apps:
- Clock: alarms, timers, world clocks in IndexedDB with guest seed
- Calendar: calendars, events in IndexedDB with sample events
- Contacts: contacts in IndexedDB with 3 sample contacts
- ManaDeck: decks, cards in IndexedDB with onboarding flashcards
All apps: GuestWelcomeModal, login pill for guests, sync on auth.
Dev scripts: added dev:sync, dev:todo:server, dev:todo:local, dev:todo:full updated.
6 of 8 web apps are now local-first (Todo, Zitare, Clock, Calendar, Contacts, ManaDeck).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New lightweight server replacing NestJS for server-side compute:
- RRULE expansion (next occurrence, validation, DoS protection)
- Reminders (CRUD with reminder time calculation)
- Admin (GDPR user data counts + deletion)
- JWT auth middleware + service key auth for admin
- Port 3019, ~10 packages vs ~50 for NestJS
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add missing devlog for March 25 (analytics, help system, CityCorners,
ManaLink, infra). Expand March 26 devlog to include all 61 commits
(Storage sprint, Passkeys/2FA, Tags, Zitare) alongside architecture planning.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix credits.service.spec and credits.controller.spec which failed
because CreditsService now depends on GuildPoolService. Add mock
provider and update useCredits → useCreditsWithSource references.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Favorites and lists stores rewritten to read/write IndexedDB
- Guest seed data: 3 pre-favorited quotes + sample list
- Layout: zitareStore.initialize(), sync on login, GuestWelcomeModal
- PillNav shows login button for guests (empty userEmail)
- No auth checks in stores — all writes are local
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update branchen pages (privat, selbststaendige, vereine), memoro-de content,
and onboarding CreditsStep to use new Mana Quelle S-XXL tiers and Mana Trank prices.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>