Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tuturuuu.com/llms.txt

Use this file to discover all available pages before exploring further.

apps/web routing has two jobs:
  • map browser-friendly URLs onto the App Router file structure
  • canonicalize workspace identity before pages and APIs touch the database
For the complete permission model that sits on top of this routing layer, see platform/architecture/workspaces-permissions.

File Structure Versus Browser URLs

The dashboard uses the App Router structure:
apps/web/src/app/[locale]/(dashboard)/[wsId]/...
Users do not need to think about the internal route groups. What they see in the browser is effectively:
/{workspace-segment}/...
where workspace-segment is one of:
  • a workspace UUID
  • personal
  • internal

Proxy Responsibilities

apps/web/src/proxy.ts is the routing coordinator at the edge. It is responsible for:
  • locale negotiation
  • auth-cookie propagation during redirects
  • onboarding and root-path redirect behavior
  • canonical workspace slug redirects
  • workspace-aware request preprocessing before the App Router handles the page

Workspace Slug Canonicalization

Two workspace IDs are intentionally rewritten into stable slugs:
  • the current user’s personal workspace UUID -> personal
  • ROOT_WORKSPACE_ID -> internal
That means the proxy may receive a UUID but still redirect the browser onto a slugged URL for consistency.

Why this matters

  • links are shorter and more stable
  • users do not need to memorize internal UUIDs for personal/internal workspaces
  • page code can still resolve those slugs back to real UUIDs with shared helpers

Default Workspace Redirect

When an authenticated user hits /, the proxy uses getUserDefaultWorkspace() to find the best landing workspace. Resolution order:
  1. user_private_details.default_workspace_id, if it still exists and the user is a member
  2. the user’s personal workspace
The redirect target becomes:
  • personal for a personal workspace
  • internal for the root workspace
  • the UUID for a normal workspace

Page-Level Workspace Resolution

Server pages should not parse workspace slugs manually. Use WorkspaceWrapper, which calls getWorkspace() and gives the page:
  • workspace
  • canonical UUID wsId
  • isPersonal
  • isRoot
If the lookup fails, the wrapper calls notFound(). This makes page code simpler because the page receives the normalized UUID even when the URL used personal or internal.

API-Level Workspace Resolution

Route handlers should use normalizeWorkspaceId(). That helper:
  • accepts the raw route segment
  • converts personal into the authenticated user’s personal workspace UUID
  • converts internal into ROOT_WORKSPACE_ID
  • preserves request-scoped auth when passed a request-derived Supabase client
The common rule is:
  • URLs may use slugs
  • database reads and writes should use canonical UUID workspace IDs

Routing And Authorization Are Separate Layers

Routing tells the app which workspace the request targets. Authorization tells the app whether the current actor may view or mutate something inside that workspace. That separation is deliberate:
  • WorkspaceWrapper / getWorkspace() handle membership and identity resolution for pages
  • normalizeWorkspaceId() handles route-param normalization for APIs
  • getPermissions() handles feature-level access control
Do not collapse those responsibilities into one helper or assume a normalized workspace ID automatically means the caller has feature permissions.