Introduction
The type-safe policy engine for modern applications.
AuthRail is a deterministic, framework-agnostic policy engine built for the modern web. It allows you to define, test, and execute authorization logic with complete type safety and predictable results.
Unlike traditional role-based access control (RBAC) systems that are often tightly coupled to database schemas or specific frameworks, AuthRail treats authorization as a pure, observable function:
(Context) => Decision;You provide the context (user data, resource attributes, environment flags), and AuthRail provides a structured decision (allow, deny, or redirect).
Why AuthRail?
Most authorization solutions are either too simple (manual if/else clusters) or too complex (domain-specific languages like Rego). AuthRail provides the perfect balance:
- 100% Type-Safe: Built with TypeScript from the ground up. Your context, rules, and results are fully typed, preventing runtime errors in your permission logic.
- Ordered Middleware: Logic is evaluated in the exact order you define it. This predictability eliminates common security pitfalls like "shadow" overrides or race conditions.
- Zero Magic, Zero Side-Effects: AuthRail never touches your database or global state. It doesn't use hidden singletons or context providers. You provide the data, it provides the answer.
- Framework & Runtime Agnostic: Run the same authorization logic in your React components, Next.js Server Actions, Express middleware, or even at the Edge (Cloudflare Workers, Vercel Edge).
- Deterministic Debugging: Every decision is traceable. When a user is denied access, AuthRail can show you exactly which middleware triggered the block.
The Mental Model
Think of AuthRail as a series of checks (middleware) that a request must pass through. We call this chain a Rail.
- Context: You assemble the
Contextobject—this is the source of truth for the evaluation. - Rail: The context enters a
Rail(a predefined pipeline of checks). - Middleware: Each function in the rail inspects the context.
- If it returns a Decision (
denyorredirect), evaluation stops immediately. - If it returns nothing (
void), the context moves to the next check.
- If it returns a Decision (
- Decision: If the context survives every check in the rail, the final result is
allow.
A Simple Example
import { createRail, requireAuth, requireRole } from "authrail";
// 1. Define your Rail (Shared between Frontend & Backend)
export const adminRail = createRail("admin", [
requireAuth("/login"), // Block & redirect if not logged in
requireRole("admin"), // Block if user exists but isn't an admin
]);
// 2. Evaluate anywhere
const result = await adminRail.evaluate({
user: currentUser, // e.g., from your auth provider
path: "/admin/settings",
});
// 3. Act on the structured decision
if (result.decision.type === "redirect") {
navigate(result.decision.to);
} else if (result.decision.type === "deny") {
notify("Access Restricted");
}What AuthRail is Not
- Not an Authentication Provider: We don't handle login, OAuth, or passwords. We work with your authentication layer (Clerk, Auth0, Supabase, NextAuth).
- Not a Database: We don't store your user records or permission tables. You fetch your data, pass it into the context, and AuthRail applies the rules.
Ready to get started? Head over to the Quick Start guide.