YYayaw

Architecture

High-level architecture and main technical modules.

Overview

Yayaw uses the Next.js App Router with locale-prefixed routes (/en, /fr). The product is organized around an authenticated multi-tenant dashboard, a public CMS runtime, and a server-only control plane for trusted automation.

Key reader paths:

Main Building Blocks

Frontend and Routing

  • App routes in src/app
  • Locale middleware and routing in src/i18n and src/lib/middleware
  • Dashboard UI blocks under src/blocks
  • Shared route/navigation configuration drives sidebar, command menu, breadcrumbs, and route visibility.

Dashboard Naming Convention

  • Page title defines the page topic (for example: Media, Billing, Authorization).
  • Block title defines what entity is managed and implies the action (for example: Assets manager).
  • Block description explains, in one short sentence, what users can do in that block.

Authentication and Authorization

  • Better Auth configuration in src/config/better-auth.config.ts
  • Auth server integration in src/lib/server/services/auth/auth-drizzle.ts
  • Better Auth API keys are enabled for control-plane access with explicit control-plane:read, control-plane:write, control-plane:publish, and control-plane:admin permissions
  • Better Auth admin capabilities include impersonation and SCIM provisioning (@better-auth/scim)
  • Organization invitation onboarding is passkey-first and uses hashed invite onboarding tokens stored outside the Better Auth invitation table
  • Organization role synchronization in src/lib/server/services/authz
  • Internal authorization model stays in Yayaw (Permix + Drizzle):
    • groups and memberships
    • explicit scoped bindings in group_role_bindings
    • role policies in role_policies
  • Explainable authorization engine in src/lib/server/authz:
    • authorizeDetailed(...) for allow/deny + decision chain
    • can(...) as the boolean facade used by routes and actions
  • Admin AuthZ custom server actions for list/detail/bulk/simulate/explain/audit in src/lib/server/actions/authz/authorization-admin-actions.ts

Data Layer

  • Drizzle setup in src/lib/db
  • Schemas organized by domain:
    • Better Auth schema
    • Authorization schema
    • System schema
  • Dashboard sidebar parents are navigable section dashboards. /dashboard/content is the CMS health dashboard; it summarizes active organization content inventory, publication status, media storage, CMS data, and AI generation jobs before users drill into dedicated catalog screens. /dashboard/admin, /dashboard/organization, /dashboard/settings, and /dashboard/test are also valid section roots, so sidebar parents never point at hidden or missing pages. The dashboard shell uses shadcn breadcrumbs and a shared section navigation bar derived from the filtered sidebar hierarchy; the section bar lists child routes for the active dashboard section only.
  • Organization media library in system schema:
    • media_folders and media_assets are organization-scoped
    • assets are stored in public object storage (media bucket by default) with persisted publicUrl
    • storage is provider-backed: Supabase remains supported and S3-compatible storage supports MinIO, S3, and R2 style deployments
    • visual assets can store generated WebP thumbnails for library display; missing thumbnails are backfilled during media library listing and do not count toward user upload quota
    • server actions always enforce scope: { orgId } and reject cross-organization mutations
    • read/list is available to organization members, upload/edit/delete requires manager or admin role
    • upload quotas are plan-driven from billing runtime config (per-file and total organization storage)
    • page-builder image generation stores OpenAI WebP outputs through the same media upload/persist pipeline
  • Reusable sections catalog in system schema:
    • ui_section_registry_items stores stable built-in, global, and organization section identity
    • ui_section_revisions stores validated SectionDefinitionV1 JSON + diagnostics
    • ui_section_publications stores lifecycle status and the latest published revision pointer
    • section definitions store renderer ids, recipes, component references, and bindings only
    • pages use PageSection references so they follow the latest published section
    • AI section creation uses a reusable plan workbench shell:
      • src/components/ai/plan-workbench/*
      • section adapter drawer: src/blocks/dashboard/content/sections/block-ai-create-drawer.tsx
      • streaming plan endpoint: src/app/api/ai/blocks/plan/route.ts
      • persisted runs/jobs endpoints (resume/poll/cancel): src/app/api/ai/blocks/plan/jobs/*
      • persisted storage tables:
        • ui_block_ai_threads
        • ui_block_ai_runs
        • ui_block_ai_run_events
      • runner abstraction: src/lib/server/services/blocks/block-ai-plan-runner.ts
      • AI prompt contract includes explicit design-policy instructions (component reuse + project-consistent layout patterns)
      • finalize action with explicit missing-import approvals: createBlockFromAiPlanAction
    • AI creation entry points are guarded by the ai-components-enabled site setting so the UI and server actions disable together.
  • Hybrid pages catalog in system schema:
    • ui_page_registry_items stores page identity, scope/channel, path, and metadata
    • ui_page_revisions stores versioned PuckPageDocumentV1 + diagnostics
    • Page AI generation uses durable Postgres state:
      • ui_page_ai_runs
      • ui_page_ai_run_events
      • APIs under src/app/api/ai/pages/runs/*
      • Vercel Queues can wake the hosted worker route; bun run worker:page-ai polls the same DB state for self-hosted long-lived worker deployments
    • ui_page_publications stores lifecycle status and published revision pointer
    • dashboard UX is split into list/table then dedicated editor route per page
    • editor is direct Puck + shadcn (no legacy fallback path)
    • save model is autosave + publish/archive with optimistic locking (baseRevisionId)
    • conflict handling is explicit (conflict response) with no automatic merge/rebase
    • runtime reads published Puck documents directly
    • global pages resolve from /[locale]/[...slug]
    • organization member pages resolve from /[locale]/o/[orgSlug]/[[...slug]]
    • publish is blocked on diagnostics errors and reserved-route collisions
  • Global registry approval policy in system schema:
    • ui_registry_templates stores approved shadcn-compatible aliases + URL templates
    • import pipelines resolve external aliases only from approved templates
    • admin management page is available at /dashboard/admin/registry-templates
  • Generated server actions in src/lib/server/actions/database/generated

Observability and Feature Flags

  • PostHog server/client integration in src/lib/posthog and src/providers
  • Dynamic flags in src/lib/flags
  • Managed flag defaults declared in src/config/flags.config.ts and seeded by src/lib/scripts/seed.ts
  • Admin site settings are backed by code-managed feature_flags rows
  • Custom Flags are generated from non-managed feature_flags rows and only affect runtime behavior when code reads their slug explicitly
  • Runtime auth capabilities resolved in src/config/authentication-runtime.config.ts
  • Dashboard analytics are driven by a server-only view-model in src/lib/server/services/dashboard:
    • /dashboard/admin is the superadmin platform health surface with Stripe revenue, PostHog audience, registered-user growth, billing risk, and admin actions. The admin sidebar parent is a direct link to this route.
    • /dashboard is a permission-aware navigation home built from visible sidebar section roots and quick links, without duplicating organization health or provider metrics
    • organization owners/admins/managers see organization billing, seats, content inventory, usage, and management actions on organization-scoped section dashboards
    • organization members see operational organization activity and safe shortcuts only on the sections they can access
    • database, PostHog, and Stripe metric aggregates use the shared Next.js Data Cache for 5 minutes, keyed by route, timeframe, organization, and access scope, while session and authorization decisions remain per-request
    • CMS page view analytics read cms_page_viewed events for active-organization pages, and include global pages only for actors with global page:manage, including top-page rankings for /dashboard/content
    • provider-level fallbacks keep one failed metric source from breaking the page
    • deployment status is provider-backed: Vercel deployment reads remain available, while self-hosted runtimes can provide static deployment metadata through DEPLOYMENT_* variables
    • PostHog events registered from the app include organization_id when an active organization is available, enabling organization-scoped analytics

Deployment Providers

Yayaw's first portable self-host target is Docker standalone Next.js with Postgres, S3-compatible object storage, a database-backed Page AI worker, and a reverse proxy that preserves Host and X-Forwarded-* headers. Vercel remains a supported provider for hosting, queues, deployment metrics, and project-domain verification, but those capabilities are no longer assumed by the core runtime.

Runtime provider seams:

  • storage: STORAGE_PROVIDER=supabase|s3
  • organization public domains: PUBLIC_DOMAIN_PROVIDER=vercel|manual-dns
  • Page AI wake-up: PAGE_AI_QUEUE_DRIVER=direct|vercel-queue|db-worker
  • deployment metadata: Vercel runtime variables or static DEPLOYMENT_* variables

Postgres + Drizzle is the source of truth for application data. Supabase is not required for core persistence; it is only one optional object-storage provider and one local reset workflow.

Control Plane

  • Production MCP endpoint: src/app/api/mcp/route.ts
  • Local stdio launcher: src/lib/scripts/mcp/yayaw-mcp-server.ts
  • Key management script: src/lib/scripts/mcp/control-plane-key.ts
  • Shared typed operation registry: src/lib/server/services/control-plane
  • Audit storage: control_plane_audit_events
  • Production access requires the control-plane-mcp-enabled site setting, a valid Better Auth API key or OAuth access token, control-plane permissions, and underlying Yayaw authorization
  • Write tools require reason; optimistic publish flows require expectedRevisionId; destructive archive tools require confirm: true

Future feature rule:

  • Treat operational, content, configuration, publish, audit, and status workflows as control-plane candidates by default
  • Put reusable business logic behind explicit actor-aware service entrypoints
  • Keep UI server actions, MCP tools, CLI scripts, and optional HTTP APIs as thin adapters over the same service logic
  • Add typed MCP tools/resources, audit coverage, docs, LLM source updates, and focused tests in the same change when a feature becomes control-plane-capable
  • Document intentional exclusions when a feature remains UI-only

Feature Flag Contract

For product features, flags must control:

  • runtime behavior (plugins/services/actions)
  • UI exposure (cards, actions, navigation entry points)

This avoids partial disables where backend behavior is off but UI links remain visible.

Documentation Architecture

  • Human docs: Fumadocs pages under content/docs
  • LLM docs source of truth: content/llm/llm-source.md
  • Generated assistant docs:
    • AGENTS.md
    • GEMINI.md
    • .github/copilot-instructions.md

When changing product behavior, update the relevant English feature doc first, then update content/llm/llm-source.md when assistant behavior or architectural source-of-truth changes. Regenerate assistant docs with:

bun run docs:llm:generate