Vier Cloudflare-Zonen für mana e.V. Schweizer Verein in Gründung:
- mana-ev.ch (apex) → Astro-Landing (mana-landing :3088)
- www.mana-ev.ch → 301 → mana-ev.ch
- mana-ev.{com,de,at} + jeweils www → 301 → mana-ev.ch
DNS via CF-API (32 Operationen): Default-A/AAAA-Records von
domainssaubillig auf Hetzner gelöscht (Apex, www, Wildcard pro Zone),
durch CNAME → 1435166a-...cfargotunnel.com ersetzt. Wildcard nicht
recreated — saubere Konfig, nur explizite Subdomains gehen.
Tunnel-Config (cloudflared-config.yml): 8 neue Hostnames mit
service-Rules. Nginx-Config (docker/nginx/landings.conf): ein
server-Block für 7 Redirect-Hostnames.
Aufgedeckte Pfad-Korruption: cloudflared-launchd-plist und
docker-compose-Volumes verweisen noch auf ~/projects/mana-monorepo/
statt managarten/. Mit Symlink (Tunnel) und Datei-Kopie (Nginx)
notdürftig geflickt — siehe OFFENE_PUNKTE.md für saubere Lösung.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mac-Mini-Drift in Source-Control bringen — war seit 2026-05-08 live
auf dem Server, aber uncommitted (während des managarten-Renames via
stash gerettet).
Cloudflared-Tunnel:
- verein.mana.how → :3088 (Verein-Landing, live seit 2026-05-09)
- share.mana.how → :3072 (Föderations-Share-Service, Phase F)
- mcp.mana.how → :3069 (MCP-Gateway, exposing tool-registry)
- cardecky-api.mana.how → :3191 (Port-Korrektur, war fälschlich :3072)
- cardecky.mana.how → :5181 (Port-Korrektur, war :5180)
- nutriphi.mana.how → :3087, nutriphi-api.mana.how → :3086
docker-compose.macmini.yml:
- mana-auth CORS_ORIGINS: nutriphi.mana.how + nutriphi-api.mana.how
- Neuer Service mana-share (Build aus ../mana/services/mana-share,
Föderations-Backbone Phase F, Port 3072, eigene DB-Tabellen in
mana_platform)
- Neuer Service mana-mcp (Build aus ../mana/services/mana-mcp,
MCP-Gateway, Port 3069)
Beide Services bauen aus dem mana-platform-Repo (../mana/services/...),
nicht aus managarten — managarten orchestriert nur via Compose.
Two long-uncommitted Mac Mini drifts cleaned up:
1. cloudflared-config.yml — git.mana.how → :3030 (Forgejo). The
route has been live for weeks (HTTP 200), just never committed.
2. .gitignore — exclude secrets/ (private keys: mana-ai mission-grant
RSA keypair lives there; must NEVER be committed) and *.bak-*
files (operator backup workflow on the Mac Mini).
services/mana-auth/drizzle/ on the Mac Mini was Mac-Mini-side
generated state for the (now deleted) mana-monorepo mana-auth
service; cleanup fell out with the Phase 7 deletion.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three new ingress rules for the Zitare repo (Code/zitare/, separate
repo, deployed under ~/projects/zitare-deploy/ on the Mac Mini).
Ports follow mana/docs/PORTS.md: 3083 api / 3084 app / 3085 com.
zitare.com is a separate Cloudflare zone — the tunnel route for
that hostname needs a one-time `cloudflared tunnel route dns
1435166a-0e3f-4222-8de6-744f32cea5c9 zitare.com` to point the CNAME
at this tunnel. Same for the two .mana.how subdomains, which sit on
the existing mana.how zone.
Code-only: no Mac Mini deploy in this commit. The actual reload
needs ./scripts/mac-mini/sync-tunnel-config.sh after the matching
mana-auth/CORS_ORIGINS + sso-origins changes are committed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2f-1 hatte verdaccio von der Mini auf die GPU-Box verlegt — das
Storage-Volume kam dort aber nie an. Der GPU-Container war leer (keine
htpasswd, keine @mana/*-Pakete), externe `npm install @mana/foo` lief
auf 404. Rollback statt Storage-Migration nachzuholen, weil:
- Mini's Standalone-Verdaccio (~/projects/verdaccio/) hat alle Daten
inklusive claudebot-Service-Account und 9 published Pakete
- npm-Reads sind ohnehin niedrig (CI-builds), Mini-Disk hat Platz
- Vereinfacht den User-/Token-Pflad-Lebenszyklus (eine Quelle, keine
Sync-Choreografie)
Cleanup:
- DNS npm.mana.how zurück auf Mini-Tunnel via Cloudflare-API
- Mini cloudflared-config.yml: npm.mana.how-Ingress wieder eingetragen
- GPU-Box: verdaccio-Container + 3 Volumes entfernt (mana_verdaccio-storage,
mana_verdaccio-plugins, verdaccio-storage)
- infrastructure/docker-compose.gpu-box.yml: verdaccio-Service-Block raus
- infrastructure/verdaccio/config.yaml: gelöscht (war GPU-spezifischer
Bundle, der Code/mana hat die kanonische Kopie für Mini)
- docs/PLAN_OPTION_C.md: Phase 2f markiert als ⚠️ teilweise zurückgerollt
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Web-Research-Orchestrator (16+ search-/LLM-providers) auf die GPU-Box
verlagert. Cross-LAN für mana-auth/mana-credits/mana-llm/mana-search/
postgres/redis (192.168.178.131). research.mana.how routet jetzt zum
mana-gpu-server-Tunnel (CF config v29). Mini-Container-Count 42 → 41.
PUBLIC_MANA_RESEARCH_URL in mana-app-web auf https-URL umgestellt —
Mini-Container können 192.168.178.11 nicht direkt erreichen (Colima-NAT),
daher Cross-LAN-Bridge via Cloudflare-Tunnel wie bei mana-ai.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2f-3 (final of the 2f-trio). The background tick-loop runner is
the most coupled of the three: it queries mana-api, mana-llm, and
mana-research, and writes through to the mana_sync DB. Wired up via
cross-LAN host-IPs to those Mini-side services + the existing RSA
key-pair for Mission-Grant decryption (MANA_AI_PRIVATE_KEY_PEM moved
into /srv/mana/.env on the GPU-Box; the matching MANA_AI_PUBLIC_KEY_PEM
stays on mana-auth's env-set as before).
Bonus rationale: AI Mission Runner now sits in the same compose
network as the GPU-Box's gpu-llm/gpu-ollama tasks, so future
"agent talks to local LLM" paths skip the Cloudflare round-trip.
Tunnel: mana-ai.mana.how repointed at the mana-gpu-server tunnel
(config v28). The Mini-side ingress was removed in the same step.
OTEL_EXPORTER_OTLP_ENDPOINT cleared since Tempo was retired in 2c.
Mini-side: container stopped + removed from docker-compose.macmini.yml.
Running count went from 39 → 42 because of unrelated services that
re-appeared on the latest CD pull (cards-server, memoro-web), but the
actual mana-ai service is gone — net move accomplished.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires cards-server into the Mac-mini stack so we can deploy alongside
the rest of the Mana services.
- Dockerfile mirrors the mana-credits 2-stage pattern (node+pnpm
installer → bun runtime), exposes :3072, includes a /health
healthcheck.
- docker-compose.macmini.yml: new cards-server block right after
mana-credits — depends on postgres + mana-auth, 128m mem, all the
env knobs from the Phase-α config (author payout BPS, community-
verified thresholds, sibling-service URLs).
- cloudflared-config.yml: cards-api.mana.how → :3072. Distinct from
cards.mana.how (the user-facing PWA) so the API surface is clearly
separated.
- sso-origins.ts: cards-api.mana.how added to PRODUCTION_TRUSTED_ORIGINS.
- mana-auth CORS_ORIGINS in compose: cards-api.mana.how added.
Restored whopxl.mana.how that had drifted out — sso-config.spec.ts
had been flagging it but the missing entry surfaced when I added
cards-api. spec is back to 8/8 green.
Deploy plan (next steps, not in this commit):
1. ./scripts/mac-mini/build-app.sh cards-server
2. docker exec mana-app-cards-server bun run db:push (creates the
`cards` schema + 16 tables in mana_platform)
3. ./scripts/mac-mini/sync-tunnel-config.sh
4. Smoke: curl https://cards-api.mana.how/health → 200
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2f-1 cutover. npm.mana.how DNS now CNAMEs to mana-gpu-server
tunnel (config v27), Mini-side route entry no longer needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two cleanups against the status-page DOWN list:
photon-self (photon.mana.how route):
mana-geocoding's /health/photon-self pings the photon backend, which
lives as a Docker container on the GPU-Box (port 2322). PHOTON_SELF_API_URL
was http://192.168.178.11:2322 — Mini-host can hit that fine but
Mini-Docker-containers can't (Colima-NAT-quirk we keep running into).
Routed photon through the mana-gpu-server tunnel (config v26) and
flipped the env var to https://photon.mana.how. Probe goes UP, geocoding
for sensitive queries (privacy:'local' provider tier) actually works
now too — was effectively orphaned before.
whopxl removed everywhere it still lingered:
Container hasn't existed on the Mini in months (no compose service,
no source dir under apps/, no listener on :5100 — only the dead
cloudflared route + a stale CORS_ORIGINS entry on mana-auth). Cleaned
cloudflared-config.yml, prometheus.yml blackbox-web target, and the
mana-auth CORS list. Old DNS CNAME for whopxl.mana.how stays for now;
no harm.
Plus while we were here: who-api.mana.how/api/decks was the right probe
for who-server's deck catalogue (root /api/decks lives on who-api, not
who.mana.how which is the SSR shell).
Live: status.mana.how shows 58/59 UP; the last 'whopxl' entry will
fall off after VM's TSDB rolls past the probe_success staleness window.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Audit revealed status.mana.how was probing only the unified mana-app
path-routes (mana.how/{module}) plus a couple of GPU services. None
of the standalone deployments were monitored, and three probe targets
were stale.
Changes:
- prometheus.yml blackbox-web: drop mana.how/{context,who} (context
module was dropped 2026-04-29; mana.how/who never existed —
/who is a standalone stack on its own subdomain). Add the eight
hosts that DO have separate deployments today: whopxl, manavoxel,
memoro (landing), cards (Phase-1 spinoff), who.mana.how/cantina,
npm (Verdaccio).
- prometheus.yml blackbox-api: add memoro-api/health,
memoro-audio/health, who-api.mana.how/api/decks,
admin.mana.how/health (admin's root is auth-walled, only /health
returns 200).
- prometheus.yml blackbox-gpu: add gpu-llm.mana.how/health (was
missing; gpu-stt/tts/img/video were in, gpu-llm was somehow not).
- cloudflared-config.yml: restore who.mana.how → :5092 +
who-api.mana.how → :3092. The DNS CNAME points at the Mini tunnel
but the route entries had been lost during a previous compose
cleanup, so every who.* request was hitting the catch-all 404 and
the standalone Bun stack was effectively orphaned at the edge
(PM2 + LaunchAgent all healthy on Mini, just no public route).
Live state after rollout: status.mana.how shows 57/59 services UP,
the two remaining DOWN are pre-existing — photon-self (Phase-2c
cross-LAN routing limitation, documented in PLAN_OPTION_C.md) and
whopxl-web (container not running on the Mini, separate issue).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2e cleanup. status-page-gen + a dedicated nginx now run on the
GPU-Box (sparse repo clone provides the generator script + mana-apps.ts,
hourly git-pull via systemd timer). Container queries VictoriaMetrics
locally over docker-network ('http://victoriametrics:9090'), no public
vm.mana.how endpoint required — that hostname is also gone from the
GPU tunnel config (v25 → v26 effectively, removed in same PUT that
added status.mana.how).
DNS for status.mana.how now points at the mana-gpu-server tunnel.
Mini-tunnel ingress for it is removed; the previous 'mana-status-gen'
container on the Mini was stopped + rm'd.
Side benefit: closes the inode-stale-bind-mount bug that took status.
mana.how down for a few hours — single-file bind mounts on the Mini
break whenever the CD git-checkout rewrites the source file. The
GPU-Box mounts the same files but the systemd timer git-pulls in-
place, preserving the inode.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2c+2d cleanup. The 14 services that moved to the GPU-Box stack
(grafana, victoriametrics, loki, tempo, promtail, alertmanager,
vmalert, pushgateway, blackbox-exporter, alert-notifier, umami,
glitchtip + worker, forgejo) are now stopped on the Mini and stable
on the GPU box, so the rollback insurance can come out:
- docker-compose.macmini.yml: drop 14 service blocks (-369 lines) +
the now-orphan named volumes (victoriametrics_data, loki_data,
alertmanager_data, grafana_data, tempo_data).
- cloudflared-config.yml: drop the four hostnames whose DNS already
points at the mana-gpu-server tunnel — Mini-tunnel ingress for them
has been dead routing since 2026-05-06, removing the rules just makes
the file match reality. The hostnames now live in the GPU tunnel's
dashboard config (token-managed).
Containers + volumes stay on the Mini for now; running
`docker compose -f docker-compose.macmini.yml --env-file .env.macmini up -d --remove-orphans`
on the box drops them in one go when ready.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Builds out the Cards spinoff end-to-end so the standalone app at
cards.mana.how shares its data layer with the in-mana cards module
through a single pure-utility package.
Why a spinoff and not just a deeper module: per the GUIDELINES, Cards
gets its own brand + URL while reusing mana-auth, mana-sync, and the
mana-credits/billing stack. The in-mana module under mana.how/cards
stays untouched as the integrated experience.
Phase 0 — mana-modul foundation
• New tables cardReviews + cardStudyBlocks (Dexie v61) + plaintext
classification in the crypto registry.
• LocalCard learns a {type, fields} shape; legacy front/back columns
kept as a back-compat mirror so older builds keep rendering.
• FSRS v6 scheduler + Cloze parser + Markdown render pipeline.
• UI in apps/mana/.../routes/(app)/cards/ gets a learn session
(learn/[deckId]), 4-type card editor, due-counter, markdown lists.
Phase 1 — standalone (apps/cards/apps/web)
• SvelteKit 2 + Svelte 5 + Tailwind 4, port 5180.
• Own Dexie 'cards' DB with a slim 5-table schema.
• Own sync engine: pending-changes hooks, 1 s push / 5 s pull against
POST /sync/cards, server-apply with suppression to avoid ping-pong.
• Auth-Gate via @mana/shared-auth-ui (LoginPage / RegisterPage).
• Encryption hooks at every write/read/apply path, currently no-op
stubs — flipping to real vault-backed AES-GCM is a single-file
change in src/lib/data/crypto.ts.
Shared package — @mana/cards-core
• Pulls types, cloze, card-reviews, FSRS wrapper, and Markdown
renderer out of the mana module so both frontends import from one
source. mana-modul keeps thin re-export shims so consumers don't
need to change imports.
• 19 vitest tests carried over from the mana module.
Server-side wiring
• cards.mana.how added to mana-auth PRODUCTION_TRUSTED_ORIGINS and
its CORS_ORIGINS env (sso-config.spec.ts stays green).
• New cards-web container in docker-compose.macmini.yml (mirrors
manavoxel-web pattern, 128m, depends on mana-auth healthy).
• cloudflared-config.yml repoints cards.mana.how from :5000 (the
unified mana-web container) to :5180. mana.how/cards is unchanged.
Cleanup
• Removed an unrelated 2026-03/04 NestJS+Supabase+Expo experiment
that was lingering under apps/cards/ (apps/landing, supabase/,
.github/workflows, MANA_CORE_*.md, etc.). It predated this plan
and would have confused future readers.
Validation
• svelte-check on mana-web: 0 errors over 7697 files
• svelte-check on cards-web: 0 errors over 3481 files
• vitest on cards-core: 19/19 pass
• pnpm check:crypto: 214 tables classified
• bun test sso-config.spec.ts: 8/8 pass
• vite build on cards-web: green
Not done in this commit (deliberate)
• Real encryption (vault roundtrip) — Phase 2.
• WebSocket-driven pull (5 s polling for now).
• Mobile/landing standalone surfaces — Phase 2/3.
• The actual production cutover on the Mac mini (build, deploy,
cloudflared sync) — config is staged, deploy is a user action.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
memoro ist seit längerem ein eigener Repo (Code/memoro/) mit eigenem
Compose-Stack auf dem Mini (~/projects/memoro-deploy/). Der Tunnel
zeigte bisher trotzdem auf die unified mana web app (Port 5000) — d.h.
memoro.mana.how rendert nur das Mana-Dashboard, nicht die echte
Memoro-Marketing-Landing.
Vier Hostnames in einem eigenen Memoro-Block:
memoro.mana.how → :3120 (Astro-Landing, Marketing-Site)
memoro-app.mana.how → :3130 (SvelteKit-SPA, Web-App)
memoro-api.mana.how → :3110 (API)
memoro-audio.mana.how → :3101 (Audio-Service)
memoro-app vs memoro auf erster Subdomain-Tiefe gelassen damit
Cloudflare Universal SSL ohne Wildcard-Konfig greift.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two new public hostnames pointing at containers that live in the
separate mana-platform repo (Code/mana, ~/projects/mana-platform on
the Mac Mini):
- admin.mana.how → :3071 (mana-admin, Verein backoffice)
- npm.mana.how → :4873 (Verdaccio, private @mana/* npm registry)
Both deployed alongside the legacy stack via
infrastructure/docker-compose.macmini.yml in the mana-platform repo.
No change to existing routes.
Arcade lives as its own pnpm workspace at ~/Documents/Code/arcade
now, with no @mana/* coupling. This drops every reference and the
games/ directory from the monorepo.
Removes:
- games/ directory (89 files: web + server + 22 HTML games + screenshots)
- @arcade/web, @arcade/server pnpm workspace entries (games/* globs)
- arcade scripts in root package.json (4 scripts)
- arcade.mana.how from mana-auth trusted origins + CORS_ORIGINS
- arcade entries in mana-apps registry, app-icons, URL overrides
- arcade.mana.how from cloudflared tunnel + prometheus blackbox probes
- arcade-web service block in docker-compose.macmini.yml
- generate-env.mjs entries for arcade server + web
- BRANDING_ONLY 'arcade' entry in registry consistency spec
- dead arcade translation keys in GuestWelcomeModal (DE+EN)
- arcade mention in CLAUDE.md, authentication guideline, MODULE_REGISTRY
Verified:
- services/mana-auth/src/auth/sso-config.spec.ts: 8/8 pass
- pnpm install regenerates lockfile cleanly (-536 lines)
- no remaining 'arcade' refs outside historical snapshot docs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Modul, Routen und Public-Domain heißen jetzt einheitlich "feedback":
- App-Registry: id 'community' → 'feedback', name 'Community' → 'Feedback',
Icon Megaphone → HeartHalf (passt zum bereits-globalen heart-half-Icon
am Module-Header und im PillNav-Usermenü)
- Modul-Config: communityModuleConfig → feedbackModuleConfig
- Routen-Refs: alle href/goto-Aufrufe in Modul-Views, MyWishesView,
Onboarding-Wish, Profile-MyWishes auf /feedback umgestellt
- /feedback/+layout: Brand "Mana Community" → "Mana Feedback", Megaphone
→ HeartHalf, "In Mana öffnen"-CTA zeigt jetzt auf /?app=feedback
- Public-Mirror Domain: community.mana.how → feedback.mana.how
(cloudflared-config.yml + docker-compose.macmini.yml CORS_ORIGINS +
PUBLIC_MANA_ANALYTICS_URL_CLIENT). DNS muss separat angelegt werden.
- Settings-Section: Hilfe-Text nennt jetzt feedback.mana.how
Internal: community_show_real_name + community_karma DB-Spalten bleiben
(Migration nicht im Scope dieses Renames). Settings-Search-Index-Kategorie
'community' bleibt ebenfalls — sie spiegelt das DB-Schema, nicht den
User-Begriff.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
analytics.mana.how DNS already existed as a non-CNAME record — picking
the user-facing 'community.mana.how' subdomain instead. Added the
tunnel ingress + matched the CORS origin + client-side env var.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 2 feedback hub needs a public hostname so the browser-side
FeedbackHook + /community page can talk to mana-analytics. Internal
docker URL stays for SSR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two subdomains the webapp references in its SSR-injected config but
that had no tunnel entry:
- events.mana.how → mana-events on :3065. The container itself was
also missing (defined in compose but never started); started
today so the route now terminates somewhere real.
- research.mana.how → mana-research on :3068. The webapp was built
with PUBLIC_MANA_RESEARCH_URL empty, which made research fetches
fall back to mana.how and 404. The env-var side is still pending
a rebuild, but the tunnel side is live now.
Cloudflare CNAMEs already created via `tunnel route dns`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Public ingress for the Mission Key-Grant audit endpoint
(/api/v1/me/ai-audit) so the Workbench "Datenzugriff" tab can reach
mana-ai from the browser. Background tick + /metrics stay internal;
only the JWT-gated user endpoint is exposed.
Requires a Cloudflare DNS record pointing mana-ai.mana.how at the
tunnel CNAME (one-off: \`cloudflared tunnel route dns
1435166a-0e3f-4222-8de6-744f32cea5c9 mana-ai.mana.how\`), then sync
via scripts/mac-mini/sync-tunnel-config.sh.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After running scripts/mac-mini/rebuild-tunnel.sh, the old remotely-
managed tunnel bb0ea86d-... was deleted and a new locally-managed
tunnel 1435166a-... took its place. The script's in-place sed of
the repo file didn't actually persist (the server-side ~/.cloudflared/
config.yml was patched, but the repo file ended up identical to HEAD
because the dev box had a stale checkout that got pulled over).
This commit catches the repo file up to the new tunnel id so a fresh
clone + setup-cloudflared-service.sh run wires the right credentials
file from the start. cloudflared has been running fine on the new
tunnel id since the rebuild — it auto-resolved the credentials from
~/.cloudflared/cert.pem when the in-config tunnel id pointed at a
deleted tunnel — but the file should match reality regardless.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two related AI-infrastructure hardenings landing together because both
touch the same nutriphi/planta route definitions:
═══ 1. Wire-format schema versioning ═══
Adds AI_SCHEMA_VERSION + AiResponseEnvelope<T> in @mana/shared-types so
every AI structured-output endpoint speaks a single envelope dialect:
{ schemaVersion: '1', data: <validated object> }
Backend wraps via a small `envelope()` helper in each module's routes.ts;
frontend api.ts unwraps via `unwrapEnvelope<T>()` which throws an
AiSchemaVersionMismatchError if the server returns a version this
client wasn't compiled against.
Why this matters before launch:
- Catches stale-cache scenarios immediately ("client v1 talking to
server v2") with an actionable error in the network panel, not a
cascade of "field is undefined" bugs further down the stack
- Forces explicit version bumps when we make non-additive schema
changes — the bump rules are documented inline next to the constant
- Cheap to remove if it ever feels overkill: drop the envelope() call
on the backend and the unwrapEnvelope on the frontend, ~10 lines
═══ 2. Anthropic prompt-caching directive (forward-compat) ═══
Adds `providerOptions: { anthropic: { cacheControl: { type: 'ephemeral' } } }`
on the system message in nutriphi + planta routes via a SYSTEM_CACHE_HINT
constant. This is a NO-OP today because:
- mana-llm currently routes to Gemini, not Claude
- Our system prompts are ~50 tokens, well under Anthropic's 1024-token
cache minimum
Kept anyway because it's ~5 lines per route and lights up automatically
when either condition flips (e.g. when we add per-user dietary preferences
as system context, pushing prompts past the threshold). The day we point
mana-llm at Claude Sonnet, every existing call site already has caching
enabled — no scavenger hunt through the routes.
System messages had to migrate from the `system:` shorthand to a full
messages[] entry to attach providerOptions, which is a tiny readability
loss but the only way to get per-message metadata into the AI SDK.
═══ Tests ═══
13 new cases in apps/mana/apps/web/.../nutriphi/ai-schemas.test.ts cover:
- AI_SCHEMA_VERSION presence + AiSchemaVersionMismatchError shape
- MealAnalysisSchema acceptance/rejection (confidence bounds, missing
nutrients, optional food fields, default empty arrays)
- PlantIdentificationSchema (every-field-optional design, defaults,
confidence range)
(Test file lives in the web app rather than packages/shared-types
because the latter has no test runner configured — adding vitest there
just for these would be overkill.)
Total nutriphi + planta suite: 62/62 passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reconciles the in-repo cloudflared-config.yml with the actually-loaded
ingress map on the Mac Mini production tunnel — the previous repo file
was missing 30+ hostnames (per-app subdomains, mana-api, sync, llm,
media, credits, subscriptions, etc.) because it was last updated
before the unified Mana web app rollout. Adds the new mana-api.mana.how
ingress for apps/api on port 3060 so the unified backend has a public
client URL for the SvelteKit web app's PUBLIC_MANA_API_URL_CLIENT.
Drops the dead matrix.mana.how / element.mana.how routes — the matrix
subsystem was removed in 2514831a3 and those services no longer exist.
Adds scripts/mac-mini/sync-tunnel-config.sh — the one-command flow for
shipping a tunnel-config change: pull on the server, validate the
yaml, kickstart cloudflared via launchctl. setup-cloudflared-service.sh
already wires the launchd plist with --config <repo-path> pointing at
this file, so a fresh Mac Mini install + setup script + sync script
gives you a fully reproducible tunnel.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>
All web app subdomains (chat.mana.how, todo.mana.how, etc.) were removed
when the unified app launched, but monitoring configs still referenced them.
Update blackbox targets to use mana.how/route URLs, remove stale API backend
routes from cloudflared, clean up CORS origins, and fix status page generator
to handle route-based URLs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New GPU service for fast text-to-video generation using LTX-Video (~2B params)
on the RTX 3090. Generates 480p clips in 10-30 seconds, uses ~10GB VRAM.
Includes Cloudflare Tunnel route, Prometheus monitoring, and health checks.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- docker-compose.macmini.yml: calc-web service on port 5026
- cloudflared-config.yml: calc.mana.how → localhost:5026
- mana-auth trusted origins: add https://calc.mana.how
- mana-credits & mana-user CORS: add https://calc.mana.how
- Dockerfile port updated from 5018 to 5026 (5018 used by zitare)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Forgejo v11 on port 3041 (git.mana.how via Cloudflare Tunnel)
- Forgejo Runner for CI/CD (GitHub Actions compatible)
- Built-in Docker registry and LFS support
- Registration disabled (admin-only)
- SSH access on port 2222
- Go Services CI workflow (.forgejo/workflows/go-services.yml)
- Setup script: scripts/mac-mini/setup-forgejo.sh
Replaces GitHub dependency for CI/CD. GitHub can remain as
mirror/backup while Forgejo becomes the primary Git host.
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>
Create web Dockerfile and add both manadeck-backend (port 3009) and
manadeck-web (port 5023) to docker-compose.macmini.yml. Add Cloudflare
tunnel routes for manadeck.mana.how and manadeck-api.mana.how.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These apps were running on the Mac Mini but not accessible from the
internet because their ingress rules were missing from the tunnel config.
Also created DNS CNAME records for storage-api and planta-api.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two infrastructure improvements for tech independence:
1. Cloudflare Fallback Documentation (docs/CLOUDFLARE_FALLBACK.md):
- Plan B: WireGuard + Caddy on Hetzner VPS (€3.79/mo)
- Complete Caddyfile with all 30+ subdomains
- Step-by-step failover checklist (~15 min to switch)
- Plan C: Direct IP with ISP
2. Self-Hosted Landing Pages (eliminates Cloudflare Pages dependency):
- Nginx container (mana-infra-landings) on port 4400
- Multi-site config: each subdomain → separate dist/ folder
- Build script: scripts/mac-mini/build-landings.sh
- Cloudflare Tunnel ingress rules for 10 landing page domains
- Storage: /Volumes/ManaData/landings/ on external SSD
- Domains: it, chats, pics, zitares, presis, clocks,
manadeck, nutriphi, citycorners, docs
Migration path: Build landings locally, set Cloudflare DNS to
tunnel instead of Pages, then decommission CF Pages projects.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dockerfile, docker-compose service (port 5100), Caddy and cloudflared
routing for the WhoPixels game. PORT is now configurable via env var.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Combines LightWrite (beat/lyrics editor) and Mukke (iOS music player) into
a single web-based music workspace app. Archives the old Mukke mobile app.
- Rename: @lightwrite/* → @mukke/*, all branding, configs, Dockerfiles
- New DB schemas: songs, playlists, playlist_songs + songId FK on projects
- New backend modules: SongModule, PlaylistModule, LibraryModule
- New web: app shell with sidebar, library (songs/albums/artists/genres),
web player (queue/shuffle/repeat/MediaSession), playlists, search,
upload, dashboard, album/artist/genre detail pages
- Auth: add forgot-password + reset-password pages, extend auth store
- Tests: 40 backend unit tests (song, playlist, library services)
- Config: env generation, MinIO bucket, docker-compose prod, cloudflare
- Docs: update CLAUDE.md, auth guidelines with SvelteKit checklist
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>