# HabitusNet — Full Integration Reference Last updated: 2026-03-31 See also: https://habitus.net/llms.txt (summary) Design token upstream: https://brandsyncup.com/.well-known/llms.txt --- ## Architecture Hybrid SPA + API on Cloudflare Workers. 1. API routes (/api/*) handled by Workers functions 2. Static assets (React SPA) from dist/client/ via STATIC_ASSETS binding 3. All non-API routes serve index.html (SPA fallback) ### Key bindings (wrangler.toml) - STATIC_ASSETS: React SPA - HABITUS_IMAGES: R2 bucket for images - REALTIME_DASHBOARD: Durable Object (WebSocket) - COLLABORATIVE_EDITOR: Durable Object - NOTIFICATION_HUB: Durable Object - FIREBASE_KEY_CACHE: KV namespace - RATE_LIMIT: KV namespace - AI: Workers AI binding --- ## Authentication Firebase Identity Platform with multi-tenant support. - Firebase Project ID: habitus-net (NOT habitusnet-admin) - Firebase Tenant ID: habitusnet-admin-qvedx - Auth method: Google OAuth (primary) - Token format: Firebase ID token (JWT) - Token delivery: Authorization: Bearer {token} - Token encoding: URL-safe Base64 (must convert to standard Base64 before parsing) ### Session management - POST /api/auth/session/create — create session (sets httpOnly cookie) - POST /api/auth/session/validate — check session - POST /api/auth/session/refresh — refresh session - POST /api/auth/session/list — list active sessions - POST /api/auth/session/revoke — revoke a session ### Backend token verification pattern ``` const firebaseProjectId = env.FIREBASE_ADMIN_PROJECT_ID || env.FIREBASE_PROJECT_ID || 'habitus-net'; ``` Never use VITE_FIREBASE_PROJECT_ID in backend code (build-time only). Never use 'habitusnet-admin' as fallback (wrong project). Never use 'habitusnet-admin-qvedx' as project ID (that's the tenant ID). ### Frontend auth guard pattern All admin React Query hooks must guard with: ``` const { idToken } = useAuth(); return useQuery({ ..., enabled: !!idToken }); ``` --- ## Database Neon PostgreSQL via @neondatabase/serverless. ### Schema layout - habitus.* — Application data (platforms, blog_posts, case_studies, authors, etc.) - neon_auth.* — Authentication (users, sessions, audit_logs) - profiles — User profiles with RBAC ### Tenant isolation RLS enforced via src/lib/tenant-context.ts: - Sets PostgreSQL session variables app.current_user_id and app.current_tenant_id - R2 storage uses tenant-prefixed paths: orgs/{org_id}/path/to/file - AuthContext.userId must be a Neon UUID, not a Firebase UID ### Data access pattern ``` Component -> useQuery hook -> Data module (neon-*.ts) -> Neon client -> PostgreSQL ``` ### Important tables - habitus.authors — voice profiles (voice_style, editorial_focus, opinion_topics, personality_summary) - habitus.blog_posts — blog content with publishing workflow - habitus.case_studies — case study content - habitus.ai_generated_images — image metadata with R2 keys - habitus.cultural_themes — DTCG token data (dtcg_tokens JSONB column) - habitus.brand_token_sets — T0/T1/T2 tier token governance - habitus.content_opportunities — trend-discovered topics ### Migrations 100 migrations applied (as of 2026-03-31). Location: migrations/NNN_description.sql Run: npm run migrate (production), npm run migrate:dev (dev) --- ## Image Storage & CDN R2 bucket "habitus-images" served at images.habitus.net. ### Upload via Worker binding Images are uploaded via the HABITUS_IMAGES R2 binding in Workers code. No public upload endpoint — uploads go through authenticated API handlers. ### AI-generated images - Path: ai-generated/{timestamp}-{index}.{ext} - Generated by /api/generate-image (Imagen 4) - Auto-persisted to R2 with metadata (prompt, model, aspect ratio) - Response includes r2Key for library reference ### Image resizing (Cloudflare Image Resizing) Since images.habitus.net is proxied through Cloudflare, on-the-fly transforms work: ``` https://images.habitus.net/cdn-cgi/image/width=1200,format=webp,quality=80/{key} ``` No configuration needed. Available on any image served through the domain. ### Upload endpoint POST /api/storage/upload — requires Firebase Bearer token - Resolves org from authenticated user (not from client data) - Paths prefixed with orgs/{org_id}/ automatically - Max file size: 10 MB --- ## Design System Two-layer architecture. See DESIGN.md for complete specification. ### Layer 1: Platform (brand-agnostic) The CMS engine. Token names, component library, cultural theme system, typography scale structure. Nothing here references HabitusNet, parchment, cadmium, or Satoshi. ### Layer 2: Instance (HabitusNet brand) Specific values: parchment surface, cadmium accent, Satoshi fonts, HN mark. A different customer would provide their own Layer 2. ### Three-tier override ``` Tier 1: Platform defaults (neutral) Tier 2: Instance brand (HabitusNet) Tier 3: Cultural content theme (swiss-intl, nihon-trad, etc.) ``` Tier 3 wins where it specifies values. ### Token source File: src/design-system/tokens/core-modern.css Format: RGB values for alpha transparency support Usage: semantic classes only (bg-primary, text-destructive, border-info/20) ### Cultural themes available 8 TypeScript theme files in src/design-system/cultural-themes/themes/: - SwissInternationalTheme.ts - NihonTraditionalTheme.ts - BauhausTheme.ts - ModernMinimalTheme.ts - CelticKnotworkTheme.ts - MediterraneanTheme.ts - IndianRangoliTheme.ts - AfricanUbuntuTheme.ts Theme CSS injected server-side by src/middleware/theme-resolver.ts. ### Upstream package status @brandsync/cultural-design-tokens (in brand-sync-up repo) is the intended shared package. Current state: v1.0.0 with mock data. Not yet published to npm. Until published, subdomain projects should reference DESIGN.md or copy core-modern.css. --- ## API Endpoints (key ones) ### Content - GET/POST /api/blog-posts — CRUD - GET/POST /api/case-studies — CRUD - GET/POST /api/authors — CRUD with voice profiles - GET /api/content/opportunities — trend-discovered topics ### AI Generation - POST /api/generate-image — Imagen 4 (rate: 10/min) - POST /api/generate-video — Gemini 2.0 Flash (rate: 5/min) ### Auth - POST /api/auth/session/{create|validate|refresh|list|revoke} ### Admin - GET /api/admin/dashboard-stats — content counts, health - GET /api/roles — RBAC roles ### Themes - GET /api/themes — public theme list - GET/PUT /api/admin/theme-settings — admin theme config ### Storage - POST /api/storage/upload — R2 file upload All endpoints return JSON with CORS headers. All inputs validated with Zod schemas. Rate limiting: 100 req/min/IP default. --- ## Billing (Stripe) - Starter: free - Professional: CHF 49/mo - Enterprise: CHF 199/mo - 14-day trials on paid plans --- ## Monitoring - Sentry: error tracking via withApiSentry() HOF - PostHog: analytics (EU region, eu.i.posthog.com) - Error Registry: internal admin dashboard at /admin?tab=errors --- ## Known limitations (2026-03-31) - Image generation may return no images if Google safety filters trigger (handled gracefully) - /api/roles returns 401 intermittently (auth token timing issue) - Session creation hits 429 rate limit when navigating admin tabs rapidly - Voice-matched content generation not yet wired (author profiles exist, generation pipeline pending) - Social graph distribution not implemented - Design token package not yet distributed to subdomain projects