Skip to main content

Routing model

DualMind Lab uses file-based routing — each page is a separate HTML file. There is no client-side router.

Route map

URL PathHTML FileAuth RequiredDescription
/index.htmlYesMain chat and arena interface
/loginlogin/index.htmlNoAuthentication page (Google OAuth)
/leaderboardleaderboard/index.htmlNoModel rankings and statistics
/modelsmodels/index.htmlYesBrowse available AI models
/shareshare/index.htmlNoView shared threads (public)
/aboutabout/index.htmlNoAbout DualMind Lab
/careerscareers/index.htmlNoCareers page
/how-it-workshow-it-works/index.htmlNoHow the platform works
/faqfaq/index.htmlNoFAQ page
/privacyprivacy/index.htmlNoPrivacy policy
/termsterms/index.htmlNoTerms of service
/cookiescookies/index.htmlNoCookie policy

Auth protection

Pages that require authentication check for a valid Supabase session on load:
// Pattern used in protected pages
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
    window.location.href = '/login';
}

Cloudflare Worker URL handling

In production, the Cloudflare Worker (worker.js) provides clean URL support:
  1. API proxy: /api/* routes are forwarded to the backend
  2. Exact match: Tries to serve the exact path from static assets
  3. HTML fallback: For extensionless paths, appends .html (e.g., /users/users.html)
  4. SPA fallback: Unmatched non-API routes serve index.html
worker.js
// Extensionless fallback
const hasExtension = pathname.split('/').pop()?.includes('.');
if (!hasExtension && env.ASSETS) {
    htmlUrl.pathname = pathname.replace(/\/$/, '') + '.html';
    let assetResponse = await env.ASSETS.fetch(new Request(htmlUrl, request));
    if (assetResponse && assetResponse.status !== 404) return assetResponse;
}
Navigation between pages uses standard <a> tags. The sidebar and topbar are included as HTML partials in the admin panel but are inline in the main frontend.

Query parameters

PageParameterUsage
/share?thread={threadId}Thread ID to display
/?thread={threadId}Resume specific thread