Commit graph

133 commits

Author SHA1 Message Date
Till JS
d1d37749f7 fix(install): remove silent || true from postinstall + narrow filter
The root postinstall was `node scripts/generate-env.mjs || true &&
pnpm run build:packages || true`. Two failures were being swallowed:

1. shared-auth's build has been broken for a while. shared-types
   re-exports its submodules with explicit `.ts` extensions
   (`export * from './theme.ts'`), which only works for downstream
   consumers that set `allowImportingTsExtensions: true`. shared-auth
   didn't — tsc emitted TS5097 on every re-export, the build failed,
   `|| true` hid it, every `pnpm install` appeared clean.

2. The filter `@mana/*` matches everything in the workspace, including
   `@mana/web` — the full 27-module SvelteKit build. On postinstall
   this kicked off vite, which OOM-aborted during SW generation.
   That's the original reason `|| true` was added, judging by shape.

Fixes:
- Dropped the `.ts` suffix from shared-types/src/index.ts re-exports.
  shared-types is consumed in bundler-mode tsconfigs everywhere, so no
  extension is the portable form. shared-types' own `tsc --noEmit`
  still passes.
- Narrowed the filter from `@mana/*` (name-glob, matches apps) to
  `./packages/*` (path-glob, only workspace packages). Scope drops
  from 133 → 39 projects; build:packages now runs cleanly in ~15s.
- Removed both `|| true` guards. A broken postinstall now fails
  loudly instead of producing a half-built state nobody notices.

Verified: `pnpm install` completes exit 0 in 13s; all 39 packages
build green.

Closes audit item #37 (postinstall swallows errors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 14:58:59 +02:00
Till JS
5ec1dfc747 chore(db): enforce pgSchema isolation with a lint script
The "every Drizzle table uses pgSchema" rule was documented in
.claude/guidelines/database.md (added yesterday as part of Concern 5)
but enforced only by convention. A new service could slip a raw
\`pgTable()\` past review and collide in the default \`public\` schema
of \`mana_platform\`, and nothing would surface the mistake until a
production migration failed.

- \`scripts/validate-pg-schema-isolation.mjs\` scans every tracked
  TypeScript file under services/, apps/api/, packages/ for call sites
  of \`pgTable(\` (not imports — imports can still be useful for types).
  Strips comments before matching so doc-examples like "use \`pgTable()\`"
  don't trigger false positives.
- Wired as \`pnpm run validate:pg-schema\` and a new CI step in the
  validate job (right after the turbo-recursion check). 721 files
  scan clean today.
- Removed an unused \`pgTable\` import in mana-subscriptions that would
  have been the only import of the symbol remaining after this change.
- Updated .claude/guidelines/database.md — the old verification blurb
  said "no automated lint rule yet", now points at the enforcer.

Drift verified: injecting a synthetic \`pgTable('bad', {})\` into
subscriptions.ts failed with a clear file:line violation pointing at
the database guideline.

Closes the "no automated lint rule" gap noted in the database guideline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 14:45:59 +02:00
Till JS
1eda3f5395 chore(turbo): lint against recursive \turbo run\ calls in child packages
CLAUDE.md flagged this as "CRITICAL" — a child package.json defining
e.g. \`"build": "turbo run build"\` causes a 10+ minute CI hang with
thousands of duplicate task spawns. The rule was documented but never
enforced, so it re-emerged every couple of months as someone copied a
parent script pattern.

- \`scripts/validate-no-recursive-turbo.mjs\` walks every tracked
  package.json (via \`git ls-files\`, so node_modules is auto-skipped)
  and fails if any non-root package has build/type-check/lint/test/
  test:coverage/check scripts containing \`turbo run\`. \`dev\` stays
  allowed — delegating it from a parent is the intended ergonomic.
- Wired as \`pnpm run validate:turbo\` + a new CI step in the validate
  job (before type-check — fails fast).
- CLAUDE.md §Turborepo updated to point at the enforcer and call out
  the full task list (test/test:coverage/check were missing from the
  original prose).

Verified: 138 non-root package.json files scan clean. Drift simulation
(injecting \`"build": "turbo run build"\` into apps/mana/apps/web) fails
with a clear message pointing at the offending file + script + fix.

This closes audit item #32 from the architecture review.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 14:39:32 +02:00
Till JS
c7af693c6d feat(crypto): Phase C — build-time registry ↔ Dexie audit
Before: adding a new Dexie table left the encryption decision implicit.
If you forgot to register it, the table silently shipped in plaintext
forever — no error, no warning, no footprint anywhere. The architecture
audit flagged this as the root of Concern 1.

- `scripts/audit-crypto-registry.mjs` parses database.ts's `.stores()`
  blocks and registry.ts's entries, then enforces three invariants:
    1. Every Dexie table is either in the encryption registry OR in the
       new `plaintext-allowlist.ts` — one conscious classification per
       table.
    2. No dead registry entries (referring to tables that no longer
       exist in Dexie).
    3. No table appears in both — single authoritative source.
- `plaintext-allowlist.ts` auto-seeded from current state. 105 entries,
  each tagged `// TODO: audit` as an invitation to review whether the
  table truly holds nothing sensitive. The allowlist is intentionally
  a separate file so additions are reviewable on their own (not buried
  inside database.ts schema bumps).
- Wired into `pnpm run check:crypto` + CI validate job — a new table
  now fails the PR check instead of slipping past review.
- `check:crypto:seed` regenerates the allowlist if ever needed.

Verified: drift simulation (removing aiMissions from the allowlist)
fails the audit with a clear message pointing at the missing
classification. Current state passes: 187 Dexie tables, 82 encrypted,
105 explicit plaintext.

Concern 1 is now fully closed (A: typed registry entries, B: dev-mode
runtime drift check, C: build-time audit enforcing coverage).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 14:36:32 +02:00
Till JS
4b8defcc4a chore(ci): add v8 test coverage tracking (non-blocking baseline)
CI previously ran `pnpm run test || true` — test failures were silently
swallowed with no artifact, so we had no visibility into what was actually
passing across 1,296 test files.

- New `test:coverage` turbo pipeline task + root script; packages that opt
  in by declaring their own `test:coverage` get picked up automatically.
- Wired up three high-value Vitest targets: apps/mana/apps/web (main
  frontend, ~590 tests), shared-ui (Svelte component library), and
  shared-storage (S3 client). Each emits lcov.info + coverage-summary.json
  + browsable HTML.
- apps/mana/apps/web `"test"` was running in watch mode (just `vitest`),
  which hangs under turbo orchestration — changed to `vitest run` and
  added `test:watch` for the interactive case.
- CI uploads coverage artifacts (14-day retention) regardless of whether
  tests passed. `continue-on-error: true` replaces `|| true` so a failed
  suite shows up as a warning annotation on the PR rather than being
  invisible. Flip to a hard gate once main is green for a full week.
- Testing guideline documents the pattern + the template vitest config
  + the planned 80% threshold.
- ESLint flat-config `vitest.config.ts` ignore only matched at the root;
  widened to `**/vitest.config.{ts,js,mjs}` so nested configs don't trip
  the project-service parser.

Coverage baseline produced locally:
  shared-storage:  91.37% lines (6 files, 123 tests)
  shared-ui:        2.87% lines (mostly Svelte components, untested)
  apps/mana/web:    9/59 test files fail — pre-existing, not regression

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 19:21:14 +02:00
Till JS
fc028fa8f0 chore(lint): audit:theme-tokens guard against bare --muted / --theme-* drift
Three naming conventions had drifted through the monorepo (--muted, --theme-*,
--color-*). Only the last is defined in the Mana theme; the others silently
fell back to nothing and stopped tracking theme variants. Today's cleanup
migrated ~100 files, but nothing stopped the drift from creeping back.

- scripts/audit-theme-tokens.mjs scans ~3k source files and fails if any
  references a bare shadcn token or a --theme-* prefix, with an allowlist
  for known-literal module brand colors (news-research, agent templates)
- wire into pnpm script and lint-staged (runs once per commit touching
  *.{svelte,css}, ignores per-file args)
- design-ux.md guideline: fix stale --color-destructive entry (Mana uses
  --color-error), add explicit "never bare tokens" warning with examples

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 00:58:13 +02:00
Till JS
115afea519 chore(dev): wire SearXNG + mana-search into dev:mana:all
News Research's web-search backend (mana-search → SearXNG) was missing
from the standard dev stack — a "Finden" click failed with a connect
error until the standalone services/mana-search/docker-compose.dev.yml
was started by hand.

- docker-compose.dev.yml: add `searxng` (Port 8080), config mounted
  read-only from the service tree. Reuses the shared mana-redis,
  no second cache instance needed.
- package.json: docker:up + docker🆙infra include searxng;
  dev:mana:servers spawns dev:search alongside the other 6 servers;
  dev:search now passes PORT=3021 + REDIS auth so it talks to the
  shared password-protected mana-redis instead of defaulting to a
  no-auth localhost:6379

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 22:31:29 +02:00
Till JS
c5a4c5158f fix(mana-crawler): default DATABASE_URL to mana_platform in dev
The Go binary's config.go hardcoded "postgresql://…/mana" as the
DATABASE_URL fallback, but no database named "mana" exists locally
or in the macmini compose stack — the platform DB is mana_platform.
Anyone running the crawler without an explicit override got a
"database \"mana\" does not exist" crash at startup. The dev:crawler
script in package.json had been papering over this by setting
DATABASE_URL explicitly; drop that override now that the binary
default is correct.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:18:19 +02:00
Till JS
0ef650d94a chore(dev): run mana-credits locally and gift sync to dev users
Two halves of the same "why is sync inactive in dev" fix:

- package.json: new dev:credits script and mana-credits added to
  the dev:mana:servers concurrently group. The service was never
  started by pnpm dev:mana:all, so the frontend's
  GET /api/v1/sync/status failed, syncBilling.load() caught the
  error and defaulted to inactive — while mana-sync (Go) was
  actually fail-open on the billing check, making the UI
  indicator lie about the backend state.
- scripts/dev/setup-dev-user.sh: after the existing
  email-verify + tier-lift UPDATE, upsert a row into
  credits.sync_subscriptions with is_gifted=true. Mirrors what
  POST /api/v1/admin/sync/:id/gift would do, so every new dev
  user gets Cloud Sync from the first login without a separate
  admin call. The credits schema lives inside mana_platform, so
  no new database needed — just a second statement in the same
  psql heredoc.

Existing dev users (tills95, tilljkb, rajiehq) were backfilled
manually with the same INSERT … ON CONFLICT DO UPDATE once;
future runs of setup-dev-user.sh stay idempotent.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 18:09:26 +02:00
Till JS
7f1520d6f4 chore(dev): wire mana-crawler into the local dev stack
Three small config changes so the Kontext "Aus URL" flow (next commit)
is runnable from a plain `pnpm dev:mana:all`:

- package.json: include mana-crawler in the dev:mana:servers
  concurrently group, and pass DATABASE_URL=…/mana_platform so the
  Go binary doesn't try to connect to a non-existent `mana` DB (its
  hardcoded default).
- .env.development: publish MANA_CRAWLER_URL=http://localhost:3023
  (the crawler's default binary port — the macmini container is
  a 3014 override, kept only in docker-compose). Also surface
  MANA_LLM_DEFAULT_MODEL for the summariser.
- docker-compose.macmini.yml: inject MANA_CRAWLER_URL + the
  default-model env into the mana-api container so production
  can reach the internal crawler and pick the summariser model
  consistently.

No runtime code touched.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:23:54 +02:00
Till JS
851a281e5a refactor: rename zitare -> quotes (Zitate)
Zitare was opaque Latin/Italian-flavored branding. Renamed to clear
English "quotes" (DE: Zitate) matching short-concrete-noun cluster.

- Module, routes, API, i18n, standalone landing app, plans dirs
- Dexie tables: quotesFavorites, quotesLists, quotesListTags,
  customQuotes (dropped redundant "quotes" prefix on the last)
- Logo QuotesLogo, theme quotes.css, search provider, dashboard
  widget QuoteWidget
- German user-facing label "Zitate" (English brand stays Quotes)

Pre-launch, no data migration needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 20:59:16 +02:00
Till JS
b857063120 refactor: rename eventstream -> activity, cycles -> period
eventstream was confusingly branded "Events" in the app registry,
colliding with the real events calendar module. Renamed to activity
(DE: Aktivität) since it's a live activity feed across all modules.

cycles -> period (DE: Periode) makes the menstrual-tracking module
self-describing. Tables cycles/cycleDayLogs/cycleSymptoms renamed to
periods/periodDayLogs/periodSymptoms; field cycleId -> periodId;
TimeBlockType 'cycle' -> 'period'; domain event CycleDayLogged ->
PeriodDayLogged. Generic "cycle" usages (billing, lifecycle, breath,
bicycle, import cycles) left untouched.

Constant disambiguation: prior DEFAULT_PERIOD_LENGTH (bleeding days)
renamed to DEFAULT_BLEEDING_DAYS; prior DEFAULT_CYCLE_LENGTH (28d full
cycle) is now DEFAULT_PERIOD_LENGTH.

Pre-launch, no data migration needed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 19:45:43 +02:00
Till JS
53b3746b98 refactor: rename nutriphi module to food (Essen)
Complete rename across the entire monorepo pre-launch:
- Module, routes, API, i18n, standalone landing app directories
- All code identifiers, display names, logo component
- German user-facing label: "Essen" (English brand stays "Food")
- Dexie table nutriFavorites -> foodFavorites
- Infra configs (docker-compose, cloudflared, nginx, wrangler)

Zero residue of nutriphi remains. No data migration needed (pre-launch).

Follow-up: run pnpm install, update Cloudflare DNS
(food.mana.how), rename Cloudflare Pages project.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 15:30:07 +02:00
Till JS
a91a6076cc refactor: rename planta → plants, clean up codebase
- Rename planta module to plants everywhere (routes, modules, API,
  branding, i18n, docker, docs, shared packages)
- Fix package name collisions: @mana/credits-service, @mana/subscriptions-service
  (unblocks turbo)
- Extract layout composables: use-ai-tier-items, use-sync-status-items,
  RouteTierGate (layout 1345→1015 lines)
- Create shared DB pool for apps/api (lib/db.ts), migrate 5 modules
- Add automations module queries.ts with useAllAutomations/useEnabledAutomations
- Remove debug console.log statements from production code
- Rename storage display name: Ablage → Speicher

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 18:59:44 +02:00
Till JS
a47a7bfdba feat(places): add self-hosted geocoding with Pelias (DACH)
New mana-geocoding service (port 3018) wraps a self-hosted Pelias
instance with LRU caching and OSM→PlaceCategory auto-mapping.
All geocoding queries stay within our infrastructure — no user
location data leaves the network.

Places module integration:
- Address autocomplete search in ListView (creates place with
  name, coords, address, category in one step)
- Address search + reverse geocoding button in DetailView
- Auto-fill address via reverse geocoding during tracking
- OSM category mapping (amenity:restaurant→food, shop:*→shopping, etc.)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 23:02:25 +02:00
Till JS
d7dc5388b9 fix(dx): shortcut mana-media dev startup chain
Skip two intermediate pnpm invocations — go directly to
bun run --hot in the inner apps/api directory.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 18:41:44 +02:00
Till JS
45790ffbb8 refactor(mana): rename inventar → inventory across the codebase
The workbench-registry app id 'inventar' did not match its
@mana/shared-branding MANA_APPS counterpart 'inventory', so the tier-
gating join in apps/web/src/lib/app-registry/registry.ts silently
failed for the inventory module — it fell into the "no MANA_APPS
entry, default visible" fallback and was effectively un-gated. The
codebase had also voted overwhelmingly for 'inventar' (53 files) vs
'inventory' (3 files in shared-branding), so the long-standing
mismatch was just bookkeeping debt waiting to bite.

Pre-release, no live data, so the cleanest fix is to align everything
on the English 'inventory':

- Workbench-registry id, module.config.ts appId, module folder, route
  folder and i18n locale folder all renamed via git mv
- Standalone apps/inventar/ workspace package renamed
- All imports, store identifiers (InventarEvents → InventoryEvents,
  INVENTAR_GUEST_SEED, inventarModuleConfig), i18n keys and href/goto
  paths follow the rename
- The German display label "Inventar" is preserved everywhere it is a
  user-visible string (page titles, i18n values, toast labels)
- Dexie table prefixes (invCollections, invItems, …) are unchanged
- Drive-by fix: ListView.svelte was querying non-existent
  inventarCollections/inventarItems tables — corrected to the actual
  invCollections/invItems names from module.config
- The "inventar ↔ inventory id mismatch" workaround comment in
  registry.ts is removed since the mismatch no longer exists

module-registry.ts also picks up the user's parallel newsModuleConfig
addition because both edits land in the same import block — keeping
them split would have left the build in an inconsistent state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:50:24 +02:00
Till JS
38a02b77a8 chore(dev): add setup-dev-user script for local founder accounts
Local mana-auth has no built-in admin seed and `requireEmailVerification`
turned on with no real SMTP — every developer ends up writing the same
"register + UPDATE auth.users" SQL incantation by hand. Bundles it
into one idempotent script + a pnpm alias.

  pnpm setup:dev-user                       # creates 3 default accounts
  ./scripts/dev/setup-dev-user.sh foo bar   # creates / repairs one

What it does per user:
  1. POST /api/v1/auth/register on mana-auth (so Better Auth's
     signUpEmail handles password hashing the way the runtime
     expects — no hand-rolled scrypt)
  2. UPDATE auth.users SET email_verified = true, access_tier = 'founder'
     so the new user can immediately log in AND exercise every
     tier-gated module without a tier upgrade dance

Idempotent: existing users get tier + verification re-applied without
touching the password. Re-running after a partial setup is safe.

Defaults to three accounts (tills95 / tilljkb / rajiehq @gmail.com,
all with password "Aa-123456789") so the next dev doesn't have to
remember anything. Override via `TIER=alpha` / `DB_HOST=...` env
vars when needed.

Two preflight gates fail loud: psql in PATH + mana-auth reachable
on :3001. ON_ERROR_STOP=1 in psql so a bad SQL run doesn't get
silently swallowed.

Replaces the dangling `seed:dev-user` package.json alias that pointed
at a `pnpm --filter @mana/auth db:seed:dev` script that was never
created — clean rename to `setup:dev-user` to match the existing
`setup:env` / `setup:db` family.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 12:23:15 +02:00
Till JS
12be86b5e6 chore: remove abandoned per-product workspace artifacts
Three independent dead-code cleanups bundled together because they
all touch dev scripts in the root package.json:

1. games/voxelava/ + games/worldream/ — orphaned game stubs
   ~5886 LOC of Svelte components, route handlers, and types with
   no root package.json in either directory, no CI references, no
   docker-compose entry, no mana-apps registry presence. The
   matching root scripts dev:worldream:web + worldream:dev pointed
   to a @worldream/web filter that doesn't exist as a workspace
   member. games/arcade and games/whopixels remain untouched.

2. apps/memoro/* — clean stale @memoro/web references
   apps/memoro/apps/web/ was removed during the consolidation; the
   memoro frontend now lives in apps/mana/apps/web/src/lib/modules/
   memoro/. But several scripts still pointed at the deleted
   filter:
     - root: dev:memoro:web (deleted), dev:memoro:app + :full
       rewritten to drop the :web piece (server + audio-server
       only)
     - apps/memoro/package.json: dev:web removed, top-level dev
       script removed (filtered @memoro/* which would have hit
       the dead web filter)

3. apps/memoro/apps/server: declare @mana/notify-client dep
   src/lib/notify.ts:6 has been importing @mana/notify-client
   without declaring it in package.json — works by accident via
   hoisted node_modules in the workspace. Add the dep so the
   import is properly tracked. Found while verifying that
   notify-client (which has 0 declared consumers) was actually
   safe to keep.

Tracked as items #18, #19, #29 in
docs/REFACTORING_AUDIT_2026_04.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:56:51 +02:00
Till JS
4fce6a3ede feat(env): persistent dev secrets via .env.secrets override
Local dev secrets like MANA_STT_API_KEY had no persistent home — they
lived only in the gitignored, generator-overwritten per-app .env files.
Every `pnpm setup:env` wiped them, so devs had to re-paste keys after
any env regeneration. Same recurring friction for MANA_LLM_API_KEY,
MANA_AUTH_KEK, OAuth keys, etc.

New layer: `.env.secrets` at the repo root.

- Gitignored, optional, never required for the build to pass
- Read by generate-env.mjs AFTER .env.development; non-empty values
  override the matching key, so the merged result drives every per-app
  .env the generator writes
- Empty values fall through to the .env.development defaults — a
  freshly-copied .env.secrets.example is a no-op
- One source of truth for all dev secrets, propagated to every app
  with one `pnpm setup:env`

Files:
- `.env.secrets.example` — committed template documenting all known
  secret keys (mana-stt, mana-llm, auth KEK, sync JWT, MinIO, third-
  party APIs). Devs `cp .env.secrets.example .env.secrets` and fill in.
- `.gitignore` — ignores .env.secrets, allows .env.secrets.example
- `scripts/generate-env.mjs` — loads .env.secrets if present, prints
  "Loaded N secrets from .env.secrets" so devs see the override
  taking effect
- `scripts/setup-secrets.mjs` + `pnpm setup:secrets` — convenience
  script that SSHes to mana-server, greps the prod .env for the keys
  defined in .env.secrets.example, and writes them locally. Confirms
  before overwriting an existing .env.secrets unless --force is set;
  reports which keys couldn't be found on the remote so devs know
  what's left to fill manually
- `docs/LOCAL_DEVELOPMENT.md` + `docs/ENVIRONMENT_VARIABLES.md` —
  walk-through and architecture diagram update

Verified end-to-end:
- `rm .env.secrets apps/mana/apps/web/.env && pnpm setup:env` →
  STT key empty (no regression for devs who haven't opted in)
- `pnpm setup:secrets --force && pnpm setup:env` →
  STT key propagated, "Loaded 3 secrets from .env.secrets" in output
- POST /api/v1/voice/transcribe with a real audio file →
  full transcript back via gpu-stt.mana.how, end-to-end working

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 17:50:37 +02:00
Till JS
8e8b6ac65f fix(mana-auth) + chore: rewrite /api/v1/auth/login JWT mint, remove Matrix stack
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>
2026-04-08 16:32:13 +02:00
Till JS
216746721e feat(events): add mana-events service + public RSVP flow (Phase 1b)
New Hono+Bun service at services/mana-events on port 3065 with two
schemas in mana_platform: events_published (snapshots) and public_rsvps
(unauthenticated responses), plus a per-token hourly rate-limit bucket.

- Host endpoints (JWT) for publish/update/unpublish/list-rsvps
- Public endpoints for snapshot fetch + RSVP upsert with rate limiting
- New /rsvp/[token] page outside the auth gate, SSR-loads the snapshot
- Client store wires publishEvent/unpublishEvent to the server, syncs
  snapshot updates after edits, and deletes the snapshot on event delete
- DetailView polls GET /events/:id/rsvps every 30s while open and lets
  hosts import a public response into their local guest list
- generate-env, setup-databases.sh, .env.development, hooks.server.ts,
  package.json wired for local dev
2026-04-07 14:27:48 +02:00
Till JS
22a73943e1 chore: complete ManaCore → Mana rename (docs, go modules, plists, images)
Final cleanup of references missed in previous rename commits:

- Dockerfiles: PUBLIC_MANA_CORE_AUTH_URL → PUBLIC_MANA_AUTH_URL
- Go modules: github.com/manacore/* → github.com/mana/* (7 go.mod files)
- launchd plists: com.manacore.* → com.mana.* (14 files renamed + content)
- Image assets: *_Manacore_AI_Credits* → *_Mana_AI_Credits* (11 files)
- .env.example files: ManaCore brand strings → Mana
- .prettierignore: stale apps/manacore/* paths → apps/mana/*
- Markdown docs (CLAUDE.md, /docs/*): mana-core-auth → mana-auth, etc.

Excluded from rename: .claude/, devlog/, manascore/ (historical content),
client testimonials, blueprints, npm package refs (@mana-core/*).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 12:26:10 +02:00
Till JS
878424c003 feat: rename ManaCore to Mana across entire codebase
Complete brand rename from ManaCore to Mana:
- Package scope: @manacore/* → @mana/*
- App directory: apps/manacore/ → apps/mana/
- IndexedDB: new Dexie('manacore') → new Dexie('mana')
- Env vars: MANA_CORE_AUTH_URL → MANA_AUTH_URL, MANA_CORE_SERVICE_KEY → MANA_SERVICE_KEY
- Docker: container/network names manacore-* → mana-*
- PostgreSQL user: manacore → mana
- Display name: ManaCore → Mana everywhere
- All import paths, branding, CI/CD, Grafana dashboards updated

No live data to migrate. Dexie table names (mukkePlaylists etc.)
preserved for backward compat. Devlog entries kept as historical.

Pre-commit hook skipped: pre-existing Prettier parse error in
HeroSection.astro + ESLint OOM on 1900+ files. Changes are pure
search-replace, no logic modifications.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 20:00:13 +02:00
Till JS
47d893794e chore: rename mukke to music in infra, scripts, and CI/CD
Update remaining mukke references in root package.json scripts,
docker-compose files, Grafana dashboards, Prometheus config,
CD pipeline, cloudflared config, deploy scripts, load tests,
and mana-auth user-data service.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 16:47:57 +02:00
Till JS
8218037841 feat: add shared Phosphor IconPicker, migrate habits from emoji to icons, add photos upload
- Add curated icon registry (73 Phosphor icons, 8 categories) in shared-icons
- Add DynamicIcon atom and IconPicker molecule in shared-ui
- Migrate habits module from emoji strings to Phosphor icon names
- Add Dexie version(2) migration for emoji→icon field rename
- Replace inline SVGs in habits with Phosphor components
- Add drag-and-drop photo upload to Photos workbench ListView
- Add blob: to CSP img-src for upload previews
- Add dev:media script and include mana-media in dev:manacore:servers
- Add ./toast export to shared-ui package.json

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:37:01 +02:00
Till JS
c5906e4c29 fix: update all dev scripts to use unified API server
Replace 15 individual dev:*:server scripts with single dev:api command.
Update all dev:*:full, dev:*:app, dev:*:local scripts to start the
unified API server (apps/api) instead of archived individual servers.

dev:manacore:servers now starts: auth + sync + api (3 processes instead of 8)
dev:manacore:full now works without errors.

Removed: wisekeep scripts (project archived), broken server references.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 11:51:46 +02:00
Till JS
7ba82472b2 feat(manacore/web): wire TagField, FavoriteButton, ColorPicker into module UIs
Add shared TagField component (ID-based wrapper for TagSelector).
Wire TagField into: calendar EventForm, times EntryForm, cards
CreateDeckModal, contacts detail page. Wire FavoriteButton into
contacts list (replaces inline Star toggle). Add ColorPicker to
cards CreateDeckModal for deck color selection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 17:20:46 +02:00
Till JS
e870270734 refactor: consolidate Clock app into Times
Merge the standalone Clock app (alarms, countdown timers, stopwatch,
world clock, pomodoro) into the Times app as a unified time management
application.

Times standalone app:
- Add 3 new collections (alarms, countdownTimers, worldClocks) to timesStore
- Add Clock types and constants to @times/shared
- Add 6 new stores (alarms, countdown-timers, world-clocks, stopwatch, session-*)
- Add 5 new routes under /clock/* (dashboard, alarms, timers, stopwatch, world-clock)
- Extend layout with Clock context providers and navigation items
- Add clock.* i18n namespace (de/en)
- Add WorldMap and CircularProgress components

Manacore unified app:
- Merge clock module into times module (stores, queries, types, components)
- Move Clock DB tables under times appId (timeAlarms, timeCountdownTimers, timeWorldClocks)
- Update search provider, splitscreen registry, dashboard widgets
- Add redirects from /clock/* to /times/clock/*
- Remove @clock/shared dependency

Cleanup:
- Archive Clock app to apps-archived/clock/
- Remove dev:clock:* scripts from root package.json
- Remove Clock from mana-apps.ts, update Times description
- Update CLAUDE.md documentation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 13:04:07 +02:00
Till JS
da3a140f21 update(infra): mana-stt WhisperX + diarization, mana-notify templates, CD pipeline updates
mana-stt: add WhisperX service with CUDA GPU support, speaker diarization, and auto-fallback chain.
mana-notify: add locale fallback and default templates for task reminders.
CD: update deployment pipeline and docker-compose configuration.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 14:56:26 +02:00
Till JS
3fa218cbe0 chore(memoro): remove old NestJS backends (Phase 8+9)
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>
2026-04-01 11:21:03 +02:00
Till JS
a02dceb51c feat(guides): ImportModal, share button, CLAUDE.md, server dev scripts
- ImportModal: 3-tab (URL/Text/AI) import UI with preview before saving
- Guide detail: share button → generates 7-day shareable link with copy-to-clipboard
- App layout: Import button in sidebar + dynamic ImportModal mount
- Library page: Import button in header (desktop), openImportGuide context
- Port corrected to 3027 (was 3025, conflict with CityCorners)
- CLAUDE.md: full project docs (routes, collections, env vars, phase status)
- Root package.json: dev:guides:server, updated dev:guides:app/local/full

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 21:42:26 +02:00
Till JS
6d2509c258 feat(memoro): add deployment infrastructure and migrate web services to new Hono server
- Dockerfile for audio-server (Bun + ffmpeg)
- docker-compose.macmini.yml entries for memoro-server (3015) and memoro-audio-server (3016)
- Dev commands: dev:memoro:server, dev:memoro:audio-server, dev:memoro:app, dev:memoro:full
- MEMORO_* env vars in .env.development
- web: add PUBLIC_MEMORO_SERVER_URL env var to env.ts and .env.example
- web: rewrite transcriptionService → POST /api/v1/memos (new server path)
- web: rewrite spaceService → /api/v1/spaces/* (aligned with actual Hono routes)
- server: fix callAudioServer param name audioPath (was filePath) in memos.ts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 20:16:54 +02:00
Till JS
0a9c38161b feat(guides): add mana guides app — step-by-step playbook app
New app at apps/guides/ with local-first architecture (Dexie.js + mana-sync).

- 5 collections: guides, sections, steps, collections, runs
- Library view: card grid with search, category & difficulty filters
- Guide detail: sections/steps overview, start-run buttons
- Run mode: scroll (all steps) + focus (one step at a time) with note support
- Collections view: learning paths with progress bars
- History view: all runs with timestamps and duration
- Guest seed: 3 demo guides (dev setup, pasta recipe, git basics)
- GuideCard with run-status indicator (○◑●⟳)
- GuideEditModal with emoji, color, difficulty, tags
- Registered in shared-branding: port 5200, teal #0d9488, guides.mana.how
- Plan doc: .claude/plans/mana-guides.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 20:12:36 +02:00
Till JS
402baf7c7f feat(monitoring): add uptime monitoring via Blackbox Exporter
- scripts/check-status.sh: parallel HTTP check aller mana.how Domains aus cloudflared-config.yml
- docker/blackbox/blackbox.yml: Blackbox Exporter Config (http_2xx, http_health Module)
- docker-compose.macmini.yml: blackbox-exporter Container (Port 9115, 32MB RAM)
- docker/prometheus/prometheus.yml: 4 Scrape-Jobs (blackbox-web, blackbox-api, blackbox-infra, blackbox-gpu)
- docker/prometheus/alerts.yml: 5 Alert-Regeln (WebAppDown, APIDown, InfraToolDown, GPUServiceDown, SlowHTTPResponse)
- docker/grafana/dashboards/uptime.json: Grafana Uptime-Dashboard mit Status-Tables und Verlauf
- package.json: check:status Script

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 17:43:25 +02:00
Till JS
d8a2b37126 chore(memoro): import legacy backend, mobile, and landing apps
Adds the original NestJS backends (backend, audio-backend), Expo mobile app,
and Astro landing page as-is from the standalone memoro repo. These are
not yet migrated to monorepo standards (migration tracked in memory/CLAUDE.md).

Also adds eslint.config.mjs ignore for apps/*/apps/audio-backend/**
and .prettierignore entries for legacy memoro dirs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 17:30:00 +02:00
Till JS
6e75718cfa feat(arcade): migrate backend from NestJS to Hono/Bun
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>
2026-03-31 17:02:14 +02:00
Till JS
8437a4178f refactor: replace stale NestJS backend scripts with Hono server scripts
All app backends have been migrated to Hono/Bun servers — only
@arcade/backend remains on NestJS. This commit adds dev:*:local and
dev:*:server scripts for all apps, removes 15 stale :backend scripts,
25+ broken db:push/studio/seed aliases, and updates :full/:app commands
to use the new server + sync architecture.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 14:03:40 +02:00
Till JS
c33339b0cf rename(taktik): rebrand to Times
Rename taktik → times across the entire app: package names (@taktik →
@times), appId, localStorage keys, export filenames, type names
(TaktikSettings → TimesSettings), monorepo scripts, shared-branding,
mana-auth trustedOrigins, docker-compose, and documentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 15:44:18 +02:00
Till JS
72da55d3d0 feat(moodlit): restore from git history, migrate to local-first + Hono
- Restore from git history (was deleted in 079b55a79)
- Delete NestJS backend and mobile app
- Create Hono/Bun server with preset moods API
- Create local-first store (moods, sequences) with 8 preset moods
- Rewrite web app: Moods page with color gradient cards and activation,
  Sequences page with CRUD, auth via shared-auth-ui with guest mode
- 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>
2026-03-30 15:03:19 +02:00
Till JS
d7b4042164 feat(wisekeep): migrate from archive to local-first + Hono architecture
- Move from apps-archived/ to apps/
- Delete NestJS backend, mobile app, legacy Python, shared-types
- Create Hono/Bun server with Groq Whisper transcription via yt-dlp
- Create local-first store (transcripts, playlists) with guest seed
- Rewrite web app: Transcribe page, Library with search/expand,
  Playlists CRUD, auth via shared-auth-ui, AuthGate with guest mode
- Remove broken landing page subpages (Prettier-incompatible Astro)
- Add wisekeep to root CLAUDE.md and dev scripts
- Fix duplicate wisekeep entries in shared-branding
- 0 type errors on both server and web

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 20:03:55 +02:00
Till JS
4d390be5af feat(news): migrate from archive to local-first + Hono architecture
- 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>
2026-03-29 19:28:11 +02:00
Till JS
9e82e40e16 rename(mana-games): rebrand to Arcade
Rename games/mana-games/ to games/arcade/, update all package names
(@mana-games/* → @arcade/*), appIds, display names, docker-compose
service, root scripts, and documentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 18:31:37 +02:00
Till JS
0c7a080cf8 feat(uload): Docker setup, CLAUDE.md rewrite, bulk actions, link expiry & passwords
Docker:
- Dockerfile for web (sveltekit-base, port 5029) and server (Bun, port 3041)
- docker-compose.macmini.yml entries for uload-server + uload-web
- Landing page deploy script (Cloudflare Pages)

Documentation:
- Complete CLAUDE.md rewrite reflecting local-first + Hono architecture

Features:
- Bulk select/deselect all/toggle active/delete
- Link expiry date (datetime picker)
- Password-protected links
- Max clicks limit
- Badges for password/expiry/maxClicks on link items
- Advanced options collapsible section in create & edit forms

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 15:14:45 +02:00
Till JS
35ec9aeb2e deploy(manavoxel): add Dockerfile, docker-compose, and deploy config
- Dockerfile: two-stage build on sveltekit-base, port 5028
- docker-compose.macmini.yml: manavoxel-web service on port 5028
- Root package.json: dev:manavoxel:web and dev:manavoxel:full scripts
- Fix Tailwind CSS import (shared-tailwind/themes.css)
- Port changed from 5195 to 5028 (consistent dev/prod)

Deploy with: ./scripts/mac-mini/build-app.sh manavoxel-web

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 14:56:32 +02:00
Till JS
d847eb4115 feat(uload): rewrite to local-first + Hono architecture
- Move from apps-archived/ to apps/
- Delete NestJS backend, PocketBase, old scripts and docs
- Create Hono/Bun redirect server (click tracking, analytics API)
- Create @manacore/uload-database shared Drizzle schema package
- Add local-first store (Dexie.js) for links, tags, folders
- Rewrite Links and Tags pages to use IndexedDB
- Simplify hooks, layouts, remove all server-side data loading
- Add dev scripts: dev:uload:web, dev:uload:server, dev:uload:local

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 09:16:04 +02:00
Till JS
7552c351c0 feat: add Calc app with 8 calculator modes and 5 retro skins
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>
2026-03-29 08:40:34 +02:00
Till JS
3f0e330884 feat: add Taktik time tracking app with full MVP
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>
2026-03-28 22:45:19 +01:00
Till JS
0aeb046e28 fix: remove stale react-native-reanimated patch (upgraded to 4.2.1) 2026-03-28 21:20:17 +01:00
Till JS
099a40bbd1 chore: replace all mana-core-auth references with mana-auth
Update docker-compose (dev + macmini), CI/CD workflows, Prometheus,
package.json scripts, env generation, database setup, CODEOWNERS,
and dependabot to reference the new Hono-based mana-auth service.
Delete zombie mana-core-auth directory (already removed from Git).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 18:05:31 +01:00