This commit bundles two unrelated changes that were swept together by an
accidental `git add -A` in another working session. Documented here so the
history reflects what's actually inside.
═══════════════════════════════════════════════════════════════════════
1. fix(mana-auth): /api/v1/auth/login mints JWT via auth.handler instead
of api.signInEmail
═══════════════════════════════════════════════════════════════════════
Previous attempt (commit 55cc75e7d) tried to fix the broken JWT mint in
/api/v1/auth/login by switching the cookie name from `mana.session_token`
to `__Secure-mana.session_token` for production. That was necessary but
not sufficient: Better Auth's session cookie value isn't just the raw
session token, it's `<token>.<HMAC>` where the HMAC is derived from the
better-auth secret. Reconstructing the cookie from auth.api.signInEmail's
JSON response only gave us the raw token, so /api/auth/token's
get-session middleware still couldn't validate it and the JWT mint kept
silently failing.
Real fix: do the sign-in via auth.handler (the HTTP path) rather than
auth.api.signInEmail (the SDK path). The handler returns a real fetch
Response with a Set-Cookie header containing the fully signed cookie
envelope. We capture that header verbatim and forward it as the cookie
on the /api/auth/token request, which now passes validation and mints
the JWT correctly.
Verified end-to-end on auth.mana.how:
$ curl -X POST https://auth.mana.how/api/v1/auth/login \
-d '{"email":"...","password":"..."}'
{
"user": {...},
"token": "<session token>",
"accessToken": "eyJhbGciOiJFZERTQSI...", ← real JWT now
"refreshToken": "<session token>"
}
Side benefits:
- Email-not-verified path is now handled by checking
signInResponse.status === 403 directly, no more catching APIError
with the comment-noted async-stream footgun.
- X-Forwarded-For is forwarded explicitly so Better Auth's rate limiter
and our security log see the real client IP.
- The leftover catch block now only handles unexpected exceptions
(network errors etc); the FORBIDDEN-checking logic in it is dead but
harmless and left in for defense in depth.
═══════════════════════════════════════════════════════════════════════
2. chore: remove the entire self-hosted Matrix stack (Synapse, Element,
Manalink, mana-matrix-bot)
═══════════════════════════════════════════════════════════════════════
The Matrix subsystem ran parallel to the main Mana product without any
load-bearing integration: the unified web app never imported matrix-js-sdk,
the chat module uses mana-sync (local-first), and mana-matrix-bot's
plugins duplicated features the unified app already ships natively.
Keeping it alive cost a Synapse + Element + matrix-web + bot container
quartet, three Cloudflare routes, an OIDC provider plugin in mana-auth,
and a steady drip of devlog/dependency churn.
Removed:
- apps/matrix (Manalink web + mobile, ~150 files)
- services/mana-matrix-bot (Go bot with ~20 plugins)
- docker/matrix configs (Synapse + Element)
- synapse/element-web/matrix-web/mana-matrix-bot services in
docker-compose.macmini.yml
- matrix.mana.how/element.mana.how/link.mana.how Cloudflare tunnel routes
- OIDC provider plugin + matrix-synapse trustedClient + matrixUserLinks
table from mana-auth (oauth_* schema definitions also removed)
- MatrixService import path in mana-media (importFromMatrix endpoint)
- Matrix notification channel in mana-notify (worker, metrics, config,
channel_type enum, MatrixOptions handler)
- Matrix entries from shared-branding (mana-apps + app-icons),
notify-client, the i18n bundle, the observatory map, the credits
app-label list, the landing footer/apps page, the prometheus + alerts
+ promtail tier mappings, and the matrix-related deploy paths in
cd-macmini.yml + ci.yml
Devlog/manascore/blueprint entries that mention Matrix are left intact
as historical record. The oauth_* + matrix_user_links Postgres tables
stay on existing prod databases — code can no longer write to them, drop
them in a follow-up migration if you want them gone for real.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.5 KiB
Port Schema
⚠️ PARTIALLY ASPIRATIONAL. The clean range layout below (3000–3009 core, 3010–3019 infra, 3020–3029 AI/ML, …) was drafted 2026-03-28 as a target state. Many services do match it; many don't. Authoritative per-service ports live in each
services/*/CLAUDE.mdalongside the source defaults inapp/main.py/config.ts/ etc.Real ports today (2026-04-08)
Windows GPU server (
192.168.178.11):
- mana-stt
3020(Scheduled TaskManaSTT, public:gpu-stt.mana.how)- mana-tts
3022(TaskManaTTS, public:gpu-tts.mana.how)- mana-image-gen
3023(TaskManaImageGen, public:gpu-img.mana.how)- mana-llm
3025(TaskManaLLM, public:gpu-llm.mana.how)- mana-video-gen
3026(TaskManaVideoGen, public:gpu-video.mana.how)- Ollama
11434(public:gpu-ollama.mana.how)Mac Mini (production):
- mana-auth
3001- mana-media
3015- mana-search
3021(overlaps with the planned range slot, not a host collision since search runs on Mac Mini and stt runs on the GPU box)- mana-crawler
3023(same — Mac Mini, no host collision with image-gen on GPU)- mana-notify
3040- mana-sync
3050- mana-credits
3061, mana-user3062, mana-subscriptions3063, mana-analytics3064, mana-events3065Not deployed:
mana-voice-bot(default port3024, no scheduled task, no cloudflared route, no launchd plist).No production port collisions exist today. The two latent collisions that PORT_SCHEMA.md previously warned about (image-gen ↔ video-gen on 3026, voice-bot ↔ sync on 3050) were resolved on 2026-04-08 by:
- Moving the only
mana-image-gento be the Windows-only diffusers variant on port 3023 (the Mac flux2.c variant was deleted)- Moving
mana-voice-bot's source default from 3050 to 3024Some services still don't match the planned range layout below (mana-credits is at 3061 not 3002, mana-user 3062 not 3004, etc). Either execute the move and update this doc, or accept reality and rewrite the planned tables to reflect what's actually running.
Originally drafted: 2026-03-28 Reality reconciled: 2026-04-08
Principles
- Each range has a clear purpose (100 ports per range)
- Backend 30xx and Frontend 50xx use matching last digits where possible
- No conflicts - every port assigned exactly once
- Room for growth in each range
3000-3009: Core Platform Services
| Port | Service | Runtime | Description |
|---|---|---|---|
| 3001 | mana-auth | Hono/Bun | Authentication, JWT, sessions |
| 3002 | mana-credits | Go | Credit system, billing |
| 3003 | mana-subscriptions | Go | Stripe subscriptions |
| 3004 | mana-user | Go | User profiles, settings |
| 3005 | mana-analytics | Go | Usage analytics, DuckDB |
| 3006-3009 | (reserved) |
3010-3019: Core Infrastructure Services
| Port | Service | Runtime | Description |
|---|---|---|---|
| 3010 | mana-sync | Go | Local-first data sync (WebSocket + HTTP) |
| 3011 | mana-media | NestJS | Content-addressable storage, thumbnails |
| 3012 | mana-search | Go | Web search via SearXNG |
| 3013 | mana-notify | Go | Notifications (email, push, webhook) |
| 3014 | mana-crawler | Go | Web crawler, content extraction |
| 3015 | mana-landing-builder | NestJS | Org landing page builder |
| 3016 | mana-api-gateway | Go | API keys, rate limiting, usage tracking |
| 3017-3019 | (reserved) |
3020-3029: AI/ML Services
| Port | Service | Runtime | Description |
|---|---|---|---|
| 3020 | mana-llm | Python | LLM abstraction (Ollama, OpenRouter) |
| 3021 | mana-stt | Python | Speech-to-Text (Whisper) |
| 3022 | mana-tts | Python | Text-to-Speech (Kokoro, Piper) |
| 3023 | mana-image-gen | Python | Image generation (FLUX) |
| 3024 | mana-voice-bot | Python | Voice-to-voice assistant |
| 3025-3029 | (reserved) |
3030-3059: App Compute Servers
Only apps that need server-side compute (AI, external APIs, file operations). Pure CRUD apps use mana-sync directly.
| Port | Service | Runtime | Description |
|---|---|---|---|
| 3030 | chat-server | Hono/Bun | AI chat, streaming, spaces |
| 3031 | todo-server | Hono/Bun | RRULE expansion, reminders |
| 3032 | calendar-server | Hono/Bun | CalDAV sync, Google Calendar, notifications |
| 3033 | contacts-server | Hono/Bun | Google Contacts, vCard import/export |
| 3034 | storage-server | Hono/Bun | S3 file ops, versioning, shares |
| 3035 | picture-server | Hono/Bun | Replicate AI, generation orchestration |
| 3036 | cards-server | Hono/Bun | AI card generation |
| 3037 | mukke-server | Hono/Bun | Audio processing, BPM, ID3 tags |
| 3038 | nutriphi-server | Hono/Bun | Gemini meal analysis |
| 3039 | planta-server | Hono/Bun | Gemini plant analysis |
| 3040 | presi-server | Hono/Bun | Share links |
| 3041-3059 | (reserved) |
4000-4099: Misc
| Port | Service | Description |
|---|---|---|
| 4400 | landings | Nginx static landing pages |
5000-5059: Web Frontends (SvelteKit)
| Port | Service | Corresponds to Server |
|---|---|---|
| 5000 | mana-web | Hub/Dashboard |
| 5010 | chat-web | 3030 chat-server |
| 5011 | todo-web | 3031 todo-server |
| 5012 | calendar-web | 3032 calendar-server |
| 5013 | clock-web | (local-first only) |
| 5014 | contacts-web | 3033 contacts-server |
| 5015 | storage-web | 3034 storage-server |
| 5016 | presi-web | 3040 presi-server |
| 5017 | nutriphi-web | 3038 nutriphi-server |
| 5018 | zitare-web | (local-first only) |
| 5019 | photos-web | (local-first + mana-media) |
| 5020 | skilltree-web | (local-first only) |
| 5021 | picture-web | 3035 picture-server |
| 5022 | citycorners-web | (local-first only) |
| 5023 | cards-web | 3036 cards-server |
| 5024 | mukke-web | 3037 mukke-server |
| 5025 | inventar-web | (local-first only) |
| 5026 | context-web | (local-first only) |
| 5027 | questions-web | (local-first only) |
| 5028 | planta-web | 3039 planta-server |
| 5029 | moodlit-web | (future) |
| 5030-5049 | (reserved) |
5050-5059: Playground/Dev Tools
| Port | Service | Description |
|---|---|---|
| 5050 | llm-playground | LLM testing UI |
5100-5199: Games
| Port | Service | Description |
|---|---|---|
| 5100 | whopixels | Pixel art game |
8000-8099: Monitoring Dashboards
| Port | Service | Description |
|---|---|---|
| 8000 | grafana | Metrics dashboards |
| 8010 | umami | Web analytics |
| 8020 | glitchtip | Error tracking |
9000-9199: Infrastructure & Exporters
| Port | Service | Description |
|---|---|---|
| 5432 | postgres | PostgreSQL |
| 6379 | redis | Redis cache |
| 9000 | minio (S3 API) | Object storage |
| 9001 | minio (Console) | MinIO admin UI |
| 9090 | victoriametrics | Metrics storage |
| 9091 | pushgateway | Deploy metrics |
| 9093 | alertmanager | Alert routing |
| 9100 | node-exporter | Host metrics |
| 9110 | cadvisor | Container metrics |
| 9121 | redis-exporter | Redis metrics |
| 9187 | postgres-exporter | Postgres metrics |
SearXNG (Internal Only)
| Port | Service | Description |
|---|---|---|
| 8080 | searxng | Meta-search (not exposed to host) |
Adding a New Service
- Pick the next free port in the appropriate range
- Update this document
- Update
docker-compose.macmini.yml - Update
scripts/generate-env.mjsif the service has a dev env - Update
docker/prometheus/prometheus.ymlif the service exposes metrics