3.1 KiB
| type | parent | status | tags | |||
|---|---|---|---|---|---|---|
| project-doc | Tia | verified |
|
🏗️ Tia — Architecture
Verified from CLAUDE.md + memory files.
Stack
Next.js 16 (App Router, Turbopack) · PostgreSQL 16 + pgvector (pgvector/pgvector:pg18 on prod) · Drizzle ORM · LiteLLM gateway → MiniMax (minimax-2.7) · Cloudflare R2 · Resend · Tailwind v4 · Dokploy (auto-migrate on deploy).
Route groups (Decision Log#TD-005)
(marketing)/→/,/pricing,/privacy,/terms— static, no DB/auth imports(app)/→ authenticated app with ThemeProvider + FamilyProvider + BottomNav; dashboard at/home
Auth
- User:
/login→POST /api/auth/signin(bcrypt) → session row → httpOnlytia_session; routes userequireFamily() - Admin:
/admin-login→tia_admin_session; admin layout is a server component callingverifyAdminSession()
Data & context
FamilyProviderresolves family/child from session:{ familyId, child, children, tier, memberCount, updateChildImage }- Quota logic in
src/lib/quota.ts(pure fns + queries); usage always derived viaSUM(Decision Log#TD-001)
Migrations (the big gotcha) (Decision Log#TD-003)
migrate.mjsruns beforeserver.js; reads all SQL indrizzle/, applies any not in__drizzle_migrationswhenmust beDate.now()(> 1779539431897) or Drizzle silently skips it- Hot-apply via
POST /api/debug-migration(headerx-run-migration: yes), idempotent steps
Media (R2) (Decision Log#TD-004)
- 3-step upload: init → server-proxied
PUT /api/upload→ save URL - Never direct cross-origin PUT; never raw
*.r2.devin<img>— use/api/img?key=proxy
Observability (admin)
error_events table + logError() + POST /api/errors; /admin/{health,errors,audit,ai} over audit_log, ai_usage (p95 via percentile_cont).
Related
Tia · Tia - Decisions · Self-Hosting · Engineering Overview
Auth evolution (important nuance)
Two phases appear across the history:
- DB sessions — bcrypt +
sessionstable + httpOnlytia_sessioncookie;requireFamily() - NextAuth v5 — Google OAuth (public) + magic link (invite-only); used by the marketing-era build Hardening sprint H1 explicitly "replaced broken password hash." Treat auth as migrating/hardened — confirm the current source of truth.
DB hardening (sprint H2)
- Non-superuser role
tia_app(LOGIN, only SELECT/INSERT/UPDATE/DELETE — no DROP/CREATE/ALTER ROLE) - Separate
DATABASE_URL_SUPERUSERfor migrations only - Per-request session context
app.current_family_idset in a transaction (src/db/scoped.ts) → foundation for Row-Level Security (RLS)
Memories vision pipeline (sprint G2)
Photo is mandatory; a vision model does the metadata heavy-lifting: photo → vision caption/tags → memories.vision_embedding vector(1536) (pgvector, ivfflat index memories_embedding_idx). Requires the pgvector/pgvector:pg18 image (Deployment Checklist).
Hosting
Hetzner server via Dokploy; output: 'standalone'; PWA shell installable.