MyBotBoxMyBotBox
Architecture

Request Pipeline

The shared authentication, authorization, and rate-limit gates every API request passes.

Every API request runs the same gauntlet before any business logic executes. These gates are single, reusable implementations — not per-route code — so security behaves consistently across the whole surface.

The gates

Middleware — checks session presence, applies the Content Security Policy, and short-circuits during maintenance mode.

Authentication — verifies the Firebase session cookie (or an internal JWT for service-to-service calls) and reconciles the database user. Fails with 401.

Authorization (RBAC) — resolves the caller's highest grant for the target entity (read < write < admin), scoped to the workspace. Fails with 403.

Rate limiting — a sliding-window counter on expensive and public routes. Fails with 429.

Handler — the validated request reaches Zod-checked business logic.

Request flow

sequenceDiagram
    autonumber
    actor C as Caller
    participant E as Edge
    participant G as API Guards
    participant A as Auth
    participant Z as RBAC
    participant L as Rate Limiter
    participant H as Handler

    C->>E: HTTPS request
    E->>G: Route handler
    G->>A: requireApiAuth()
    A-->>G: session or null
    alt not authenticated
        G-->>C: 401 Unauthorized
    else authenticated
        G->>Z: requireWorkspaceAccess(min)
        Z-->>G: permission or null
        alt insufficient permission
            G-->>C: 403 Forbidden
        else authorized
            G->>L: checkRateLimit(key)
            alt limit exceeded
                L-->>C: 429 Too Many Requests
            else within limit
                G->>H: validated request
                H-->>C: 200 + data
            end
        end
    end

Rate-limit store

The limiter uses a process-local in-memory window by default. When a Redis URL is configured, it switches to a Memorystore-backed sliding window (atomic Lua) shared across all server instances, so a single user can't exceed their limit by spreading requests across replicas.

The Redis client connects lazily and the limiter fails open to memory — a cache outage can never crash startup or 500 a request. It degrades to per-instance limits until Redis recovers.

Tenant isolation

Authorization is always workspace-scoped. A grant is a row keyed by (userId, entityType, entityId), and the executor threads userId and workspaceId through the entire run — so a workflow can only ever read and write data inside its own tenant boundary. See Multi-tenancy.