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>
New unified-app module under apps/mana/apps/web/src/lib/modules/cycles.
Adds three Dexie tables (cycles, cycleDayLogs, cycleSymptoms) in db v7,
SYNC_APP_MAP entry, app-registry registration, branding (icon + entry +
APP_URLS), and a /cycles route.
Includes phase derivation (menstruation/follicular/ovulation/luteal),
heuristic next-period and fertile-window prediction (rolling mean over
last 6 cycles), 10 default symptoms, and 33 unit tests covering the
pure utilities.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New 'events' module for planning gatherings with guest lists and RSVPs,
distinct from the personal calendar. Events surface in the calendar via
TimeBlock with sourceModule='events'. Guests, RSVPs and a publish stub
work fully local-first; the public RSVP server lands in Phase 1b.
Adds a new Dreams module to the unified Mana app for capturing dream
journal entries with mood, lucid status, recurring symbols, and
timeline insights. Founder-tier gated for now.
- Dexie schema v5 with dreams, dreamSymbols, dreamTags
- Mutation store with auto symbol counting on create/update/delete
- ListView with quick capture, inline editor, mood picker, lucid
toggle, monthly grouping, insights ribbon, context menu
- Workbench registration with note → dream drop transform
- New 'dream' DragType, dreams app icon, mana-apps catalog entry
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- shared-branding/mana-apps: drop duplicate `mana` and obsolete `inventar` URL entries
- web/app.d.ts: move __BUILD_HASH__/__BUILD_TIME__ ambient declarations into declare global so they survive module-scoping
- web: remove dead supabase template (routes/api/example, lib/server/middleware) — locals.session no longer exists post auth migration
- habits/queries: drop stale Record<string,string> cast on LocalHabit (legacy emoji field)
- shared-stores/toggle-field: cast to Dexie UpdateSpec instead of Partial<T> for newer dexie types
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The ManaCore→Mana rename converted both `manaCoreSvg` and the existing
`manaSvg` to the same identifier, leaving two `const manaSvg = ...`
declarations and two `mana:` keys in APP_ICONS. This broke any consumer
of the package with a duplicate-symbol error at SSR build time.
Removed the legacy ManaCore icon (4-circle quartet) and kept the
current Mana brand icon (single droplet). Removed the duplicate
APP_ICONS entry as well.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- New SyncChange / FieldChange / SyncOp types replace `any[]` in
applyServerChanges. The wire format is now self-documenting and
TypeScript catches malformed callsites at compile time.
- isValidSyncChange() validates incoming server payloads at the boundary:
malformed entries are dropped with a single warn log, valid ones are
applied. A bad row from the server can no longer corrupt IndexedDB.
Hand-rolled type guards keep us free of a runtime-validation dep.
- applyServerChanges() and readFieldTimestamps() are now top-level
exports (extracted out of createUnifiedSync's closure) so they can be
imported directly by tests. Behaviour is unchanged — the closure
variant inside the sync manager just resolves the module-level
symbol now.
- New sync.test.ts covers:
* pure isValidSyncChange and readFieldTimestamps cases
* field-level LWW: server-newer wins, split outcome when local-newer
on one field and server-newer on another
* insert with __fieldTimestamps stamping
* soft-delete LWW guard
* malformed-entry drop with valid entries surviving
* sync-loop guard: server-applied writes don't generate _pendingChanges
- fake-indexeddb added as devDependency for the integration tests.
Note: the monorepo's vitest install is currently tangled across mixed
@vitest/* package versions in the lockfile, so `pnpm test` fails before
reaching this file. The tests are written to pass on any vitest 4.x once
that's untangled — needs its own dedicated cleanup pass.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rename the music module from "Mukke" to "Music" across the entire
codebase: API routes, web app module, shared packages, search provider,
dashboard widgets, i18n keys, app registry, and route paths.
Add POST /api/v1/music/cover/upload endpoint that uploads cover art
images through mana-media for deduplication, thumbnails, and Photos
gallery visibility.
Dexie table names (mukkePlaylists, mukkeProjects) kept unchanged to
preserve existing IndexedDB data.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New local-first places module for the workbench: browser Geolocation API
tracking, place management (CRUD, favorites, tags, categories), OSM map
preview in detail view, and proximity-based visit detection.
Also allows geolocation in Permissions-Policy header (self only).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Notes: lightweight markdown notes with search, color tags, pinning,
inline create, auto-save editor, and grid/detail views.
Finance: income/expense tracking with categories, monthly overview,
category breakdown bars, quick-add form, and transaction history.
Both modules include workbench ListView, full-page routes, entity
descriptors for drag/drop, and database/sync registration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New habit tracking module: define habits (emoji, color, daily target), tap to log with timestamp, view streaks and 7-day charts. Includes workbench ListView with inline creation, full-page detail view, and drag/drop entity integration.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove deleted PlaygroundLogo export, migrate onclick|stopPropagation
to Svelte 5 syntax, fix @const placement, and increase PWA max file
size to 8 MiB for unified app bundle.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These apps were either stubs (voxelava, worldream), planning-stage
(reader), dev-tools (playground), or inactive (bauntown). Removing
to reduce monorepo surface area. All recoverable from git history.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
mana-apps.ts:
- Change all APP_URLS from subdomains to internal paths
(e.g., https://todo.mana.how → https://mana.how/todo)
- Keep separate subdomains only for games (arcade) and matrix
PillNavigation, AppDrawer, GlobalSpotlight:
- Detect internal URLs and navigate directly instead of window.open
- External URLs (games, matrix) still open in new tab
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update Memoro status to published with founder-tier access. Add comprehensive analytics event tracking for all apps.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Set accurate status levels (beta/planning) instead of blanket 'development',
mark Cards/Picture/Zitare/Wisekeep as no longer coming soon.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ManaCore as first entry in MANA_APPS so the dashboard at mana.how
gets a tier badge. Map mana.how → manacore and inventar → inventory
in subdomain aliases.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Mukke, Photos, Planta, SkillTree, Playground, Arcade to mana-apps.ts
with icons and APP_URLS. Fix manadeck→cards subdomain alias in status
page generator so the tier badge renders for the renamed app.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Delete apps/memoro/apps/backend/ (NestJS) and apps/memoro/apps/audio-backend/
(NestJS) — all functionality has been ported to the new Hono/Bun servers
(apps/server/ and apps/audio-server/).
Also clean up root and memoro package.json scripts to remove references
to the old @memoro/backend and @memoro/audio-backend packages.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Introduces a tiered access control system so apps can be released
gradually (founder → alpha → beta → public) without extra infrastructure.
Users are gated at the AuthGate level based on their tier vs the app's
requiredTier. All apps remain deployed and reachable, but only users
with sufficient tier can enter.
- Add accessTier enum + column to users schema (default: 'public')
- Add tier claim to JWT payload in better-auth config
- Add requiredTier field to ManaApp interface + all 25 apps
- Add hasAppAccess(), getAccessibleManaApps(), ACCESS_TIER_LABELS
- Update AuthGate with tier check + access denied screen
- Update getPillAppItems + Home page to filter by user tier
- Update all 22 app layouts to pass user tier to PillNav
- Add admin API: GET/PUT /api/v1/admin/users/:id/tier
- Document access tier system in CLAUDE.md
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move from apps-archived/ to apps/
- Delete NestJS API, Docker files, old docs, browser extension
- Create Hono/Bun server with content extraction (Mozilla Readability)
and AI feed API reading from mana-sync's sync_changes
- Create local-first store (articles, categories) with guest seed data
- Rewrite web app: Feed page, Saved articles with URL extraction,
auth pages using shared-auth-ui, AuthGate with guest mode
- Add news to shared-branding (app icon, mana-apps registry)
- Add CLAUDE.md, dev scripts, root CLAUDE.md entry
- 0 type errors on both server and web
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New calculator app with standard, scientific, programmer, unit converter,
currency, finance, date, and percentage modes. Includes 5 visual skins:
Modern, HP-35 (1972), Casio fx (1985), TI-84 (2004), and Minimal.
Local-first with IndexedDB history, keyboard support, safe math parser.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New app for professional time tracking with timer, projects, clients,
and reports. Local-first architecture with IndexedDB + mana-sync.
- Timer store with start/stop/resume, auto-save every 10s
- 6 local-store collections: clients, projects, timeEntries, tags, templates, settings
- TimerCard with live counter, project selector, billable toggle
- EntryItem with inline-expand editing, EntryList with day grouping
- EntryForm modal with quick-duration buttons (15m-4h)
- QuickStart pills from recent entries
- Projects page: card grid, color coding, budget progress, inline CRUD
- Clients page: billing rates, project rollup, inline CRUD
- Reports page: stats grid, billable breakdown, project/daily charts
- i18n: German + English
- Registered in shared-branding with icon, URLs, dev scripts
- Guest seed: 2 clients, 3 projects, 5 time entries, 4 tags
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix todo dev port in APP_URLS (5188, was swapped with inventory 5189)
- Use APP_URLS for all links instead of hardcoded localhost ports
- TasksTodayWidget: add priority dots, subtask progress, label tags,
completed/total counter, clickable task links
- TasksUpcomingWidget: add priority dots, label tags, overdue/today
date highlighting with colored badges, clickable task links
- Extend Task type with labels and subtasks for richer widget display
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New project with three apps:
- Landing (Astro): static site with SVG illustrations, location data
- Backend (NestJS, port 3025): CRUD API for locations + favorites, Drizzle ORM, auth via mana-core-auth
- Web (SvelteKit, port 5196): Tailwind 4, PillNav, auth (login/register/SSO), Leaflet map, favorites with optimistic updates, theme/settings
Infrastructure: DB init SQL, setup-databases.sh, generate-env.mjs, root package.json scripts, Dockerfiles, docker-compose.macmini.yml (backend:3025, web:5022), Cloudflare wrangler.toml.
Branding: registered in shared-branding (AppId, APP_BRANDING, APP_ICONS, MANA_APPS, CitycornersLogo).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The production domain is mana.how, not manacore.app. Updated all
references across shared-branding APP_URLS, app configs, landing pages,
docs, help content, calendar iCal UIDs, and deploy scripts.
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>
- Add lightwrite to AppId type
- Add lightwrite branding config (orange theme, music note icon)
- Create LightWriteLogo.svelte component
- Update login/register pages to use proper logo component
- Fixes type-check errors in @lightwrite/web
- Add PlaygroundLogo to shared-branding package
- Add playground to APP_BRANDING, APP_ICONS, and APP_URLS
- Replace custom login/register pages with shared-auth-ui components
- Update authStore with resendVerificationEmail and improved signUp
- Add Caddy reverse proxy entry for playground.mana.how
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove padding-bottom from floating-mode layout, handle padding in components
- Add min-h-0 to flex containers for proper overflow handling
- Add bottom padding to RoomList and MessageInput for nav clearance
- Fix Timeline scrolling with proper min-h-0 on flex-1 container
- Add matrix app to shared-branding (icon, config, URLs)
- Fix File icon import shadow conflict in MessageInput
Note: Skipped type-check due to pre-existing error in @todo/web
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add ability to resend verification email when login fails with
"Email not verified" error. Implemented across all 14 apps using
Mana Core Auth.
Changes:
- Add POST /api/v1/auth/resend-verification endpoint to mana-core-auth
- Add resendVerificationEmail method to shared-auth client
- Update LoginPage component with resend UI and translations
- Add resendVerificationEmail to all app auth stores
- Add translations for de, en, fr, es, it
- Add PlantaLogo to shared-branding
- Migrate planta login to shared LoginPage component
- Add SkillTreeLogo to shared-branding (emerald green theme)
- Add login, register, forgot-password pages using shared-auth-ui
- Initialize authStore in layout alongside skillStore
- Add shared-auth-ui, shared-i18n, shared-branding dependencies
- German translations as default
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Questions branding to shared-branding package (logo, colors)
- Create QuestionsLogo.svelte component
- Refactor login page to use shared LoginPage component
- Refactor register page to use shared RegisterPage component
- Refactor forgot-password page to use shared ForgotPasswordPage component
- Fix Svelte 5 class: directive on components (+page.svelte)
The Questions app now uses the same auth UI as Calendar, Chat, and other apps.