Skip to main content

Overview

Conductor’s UnifiedRouter provides a powerful, secure-by-default routing system with integrated authentication, rate limiting, and type-specific defaults. Key Features:
  • 🔒 Secure by Default - All routes require explicit auth configuration
  • 🎯 Type-Specific Defaults - Different defaults for pages, APIs, webhooks, forms, docs
  • 📍 Path-Based Rules - Centralized routing configuration in conductor.config.ts
  • 🚀 Default Path Resolution - Auto-resolve routes from directory structure
  • Priority-Based - Control route matching order
  • 🛡️ Multi-Method Auth - Bearer, API keys, cookies, Unkey, custom validators
  • 🔢 Rate Limiting - Per-user, per-IP, or per-API-key
  • 👮 RBAC - Role and permission-based access control

Quick Start

Simplest Route

Use default to auto-resolve path from file location:
# File: members/api-docs.yaml
name: api-docs
type: docs

route: default  # Auto-resolves to /api-docs

Public Route

route:
  path: /docs
  auth:
    requirement: public

Authenticated Route

route:
  path: /api/users
  methods: [GET, POST]
  auth:
    requirement: required
    methods: [bearer, apiKey]

Role-Based Route

route:
  path: /admin/dashboard
  auth:
    requirement: required
    methods: [cookie]
    roles: [admin]
    onFailure:
      action: redirect
      redirectTo: /login

Architecture

Resolution Priority

Routes are resolved in this order:
  1. Member Config - Highest priority
  2. Path Rules - From conductor.config.ts
  3. Type Defaults - Based on member type (page, api, docs, etc.)
  4. Global Defaults - Fallback configuration

Route Matching Priority

Routes are matched in this order:
  1. Priority Number - Lower numbers match first (e.g., 10 before 50)
  2. Static vs Dynamic - Static routes before dynamic (:param)
  3. Path Length - Longer paths before shorter
  4. Registration Order - First registered wins (if all else equal)
Example:
Priority 1:  /static/*           (static files)
Priority 10: /admin/*            (admin section)
Priority 50: /api/users/:id      (API with params)
Priority 50: /api/users          (API exact match - wins over :id)
Priority 80: /dashboard          (page)

Default Path Resolution

The route: default option automatically resolves paths from your directory structure.

How It Works

Pages:
/pages/login/page.yaml → /login
/pages/admin/dashboard/page.yaml → /admin/dashboard
/pages/index/page.yaml → / (root)
Members:
/members/api-docs.yaml → /api-docs
/members/api/users.yaml → /api/users
/members/docs/internal/api.yaml → /docs/internal/api
Ensembles:
/ensembles/workflows/invoice.yaml → /ensembles/workflows/invoice

Examples

Simplest form:
route: default
With authentication:
route:
  path: default
  auth:
    requirement: public
Override just the path:
route:
  path: /custom-path  # Explicit path instead of default
  auth:
    requirement: required

Type-Specific Defaults

Configure default auth for each member type in conductor.config.ts:
routing: {
  auth: {
    defaults: {
      // Pages: Cookie auth with login redirect
      pages: {
        requirement: 'required',
        methods: ['cookie'],
        onFailure: {
          action: 'redirect',
          redirectTo: '/login'
        }
      },

      // APIs: Bearer/API key auth
      api: {
        requirement: 'required',
        methods: ['bearer', 'apiKey'],
        rateLimit: {
          requests: 1000,
          window: 60,
          keyBy: 'apiKey'
        }
      },

      // Webhooks: Custom signature validation
      webhooks: {
        requirement: 'required',
        methods: ['custom']
      },

      // Forms: Optional auth with rate limiting
      forms: {
        requirement: 'optional',
        methods: ['cookie'],
        rateLimit: {
          requests: 10,
          window: 300,
          keyBy: 'ip'
        }
      },

      // Docs: Optional (shows Try It when authenticated)
      docs: {
        requirement: 'optional',
        methods: ['bearer', 'apiKey']
      }
    }
  }
}

Path-Based Rules

Override defaults for specific paths:
routing: {
  auth: {
    rules: [
      // Public pages
      { pattern: '/', auth: { requirement: 'public' } },
      { pattern: '/login', auth: { requirement: 'public' } },
      { pattern: '/pricing', auth: { requirement: 'public' } },

      // Public API endpoints
      {
        pattern: '/api/v1/public/*',
        auth: {
          requirement: 'public',
          rateLimit: { requests: 100, window: 60, keyBy: 'ip' }
        }
      },

      // Admin section
      {
        pattern: '/admin/*',
        auth: {
          requirement: 'required',
          methods: ['cookie'],
          roles: ['admin'],
          onFailure: { action: 'page', page: 'error-403' }
        },
        priority: 10  // High priority
      },

      // Webhook endpoints
      {
        pattern: '/webhooks/stripe*',
        auth: {
          requirement: 'required',
          methods: ['custom'],
          customValidator: 'stripe-signature'
        }
      }
    ]
  }
}

Authentication Methods

Bearer Tokens (JWT)

route:
  auth:
    requirement: required
    methods: [bearer]
Environment variables:
JWT_SECRET=your-secret-key-min-32-chars
JWT_ISSUER=https://your-auth-server.com
JWT_AUDIENCE=your-api

API Keys

route:
  auth:
    requirement: required
    methods: [apiKey]
Environment variables:
API_KEYS_KV=your-kv-namespace
route:
  auth:
    requirement: required
    methods: [cookie]
Environment variables:
SESSIONS_KV=your-sessions-kv
SESSION_SECRET=your-session-secret

Unkey Integration

route:
  auth:
    requirement: required
    methods: [unkey]
Environment variables:
UNKEY_ROOT_KEY=your-unkey-root-key
UNKEY_API_ID=your-api-id

Custom Validators

route:
  auth:
    requirement: required
    methods: [custom]
    customValidator: stripe-signature
Built-in validators:
  • stripe-signature - Stripe webhook signatures
  • github-signature - GitHub webhook signatures
  • twilio-signature - Twilio webhook signatures
Environment variables:
STRIPE_WEBHOOK_SECRET=whsec_...
GITHUB_WEBHOOK_SECRET=your-secret
TWILIO_AUTH_TOKEN=your-token

Rate Limiting

Per-IP (Public Routes)

route:
  auth:
    requirement: public
    rateLimit:
      requests: 100
      window: 60
      keyBy: ip

Per-User (Authenticated)

route:
  auth:
    requirement: required
    rateLimit:
      requests: 1000
      window: 60
      keyBy: user

Per-API-Key

route:
  auth:
    requirement: required
    methods: [apiKey]
    rateLimit:
      requests: 5000
      window: 60
      keyBy: apiKey

Failure Handling

Redirect to Login

route:
  auth:
    requirement: required
    methods: [cookie]
    onFailure:
      action: redirect
      redirectTo: /login
      preserveReturn: true

Show Error Page

route:
  auth:
    requirement: required
    roles: [admin]
    onFailure:
      action: page
      page: error-403
      context:
        message: "Admin access required"

Return JSON Error

route:
  auth:
    requirement: required
    onFailure:
      action: error  # Returns JSON error (default)

Stealth Mode (404)

route:
  auth:
    requirement: required
    stealthMode: true  # Returns 404 instead of 401

Best Practices

1. Use Type Defaults

Configure sensible defaults in conductor.config.ts:
defaults: {
  pages: { requirement: 'required', methods: ['cookie'] },
  api: { requirement: 'required', methods: ['bearer', 'apiKey'] }
}

2. Use Path Rules for Common Patterns

rules: [
  { pattern: '/', auth: { requirement: 'public' } },
  { pattern: '/admin/*', auth: { roles: ['admin'] }, priority: 10 }
]

3. Use Default Paths

route: default  # Simple and maintainable

4. Add Rate Limiting

rateLimit:
  requests: 100
  window: 60
  keyBy: ip

5. Implement RBAC

auth:
  roles: [admin, developer]
  permissions: [api:write, users:delete]