Skip to content

Why Kerberos.js? ​

An embedded, zero-dependency authorization engine for Node.js and the browser: Cerbos-style policies (RBAC + ABAC), a SpiceDB-inspired "Zanzibar-lite" resolver (ReBAC) and Cerbos-compatible query plans — all in-process, no server to deploy, ~38 KB min+gzip. The API deliberately stays as close to Cerbos as possible: if you know Cerbos, you already know Kerberos.js.

Why in-process? ​

Authorization as a library, not a service. Cerbos and SpiceDB are excellent engines, but each runs as a separate Go server: another deployment, another network hop on every check, another thing that can be down. In a JavaScript stack, Kerberos.js gives you the same policy models with zero infrastructure — decisions are a function call, policies ship (and roll back) atomically with your code, and there is no PDP to keep in sync. A centralized service remains the right choice for polyglot stacks — see When NOT to use it.

Policies are data plus the full power of JavaScript. In-process policies use plain JS functions for conditions, variables and outputs — no expression-language ceiling. Policies stored in a cache/database use the same shapes with safe, eval-free $expr expressions. Local testing needs no emulator: the /tests subpath runs Cerbos-style declarative test suites against the real engine.

One engine everywhere. The browser build contains zero Node builtins, so the same policies that guard your API also gate your UI (hide buttons, filter menus) — without maintaining a second source of truth. Serverless and edge runtimes get the same benefit: no cold-start dependency on an external PDP.

Compared with the alternatives ​

How Kerberos.js compares with the policy engines (Cerbos, SpiceDB, OPA) and the JavaScript authorization libraries it is benchmarked against. ✅ built in · ⚠️ partial or with a caveat · ❌ not built in · — does not apply. A ❌ means the feature is not built in; most of them can still be written by hand on top.

ArchitectureKerberos.jsCerbosSpiceDBOPACASLcasbinAccessControleasy-rbac@rbac/rbac
Runs aslibraryservice (PDP)service + databaseservice, Go library or WASMlibrarylibrarylibrarylibrarylibrary
LanguagesJS / TS8 SDKs6 SDKs + HTTPany (REST), GoJS / TS10+ portsJS / TSJS / TSJS / TS
In the browser✅⚠️ WASM, Hub-built bundles❌⚠️ WASM SDK + compiled .wasm✅⚠️ needs Node polyfills¹⚠️ bundles, not documented⚠️ bundles, not documented⚠️ default logger needs process
Runtime dependencies0— (server)— (server)2 (WASM SDK)410201 (zod)²
Check APIasyncasync (network)async (network)async (REST) · sync (WASM)syncasync + enforceSyncsync + asyncasyncasync
LicenseMITApache-2.0Apache-2.0Apache-2.0MITApache-2.0MITMITMIT
Policy modelKerberos.jsCerbosSpiceDBOPACASLcasbinAccessControleasy-rbac@rbac/rbac
Policy formatCerbos-style documentsYAML / JSON documentsschema DSL + relationship tuplesRegoJS builder / JSON rulesmodel .conf + policy rowsbuilder / grants JSONrole maprole map
Roles with inheritance✅ parentRoles✅ parentRoles✅ modeled as relations⚠️ written in Rego⚠️ no roles: map them to rules✅ role hierarchy✅ extend✅ inherits✅ inherits
Attribute conditions✅ JS functions / $expr✅ CEL✅ CEL caveats✅ Rego✅ MongoDB-style, resource only✅ matcher expressions✅ .where() expressions✅ JS when✅ JS when
Relationships (ReBAC)✅ built-in resolver❌ pass them as attributes✅ the core model⚠️ graph.reachable, model it yourself❌⚠️ via role links❌❌❌
Derived roles✅✅⚠️ computed permissions❌ plain Rego rules❌❌❌ own ownership only❌❌
Explicit deny✅ deny beats allow✅ deny beats allow⚠️ exclusion operator⚠️ precedence written in Rego⚠️ cannot, rule order decides✅ configurable effect✅ within a role chain³❌ allow-only❌ allow-only
Field-level permissions⚠️ per-field actions or outputs⚠️ per-field actions or outputs❌⚠️ column masks (server)✅ fields❌✅ attribute filtering❌❌
Scopes / tenants✅ scope chain⁴✅ scope chain❌ model tenants as objects❌ structure packages yourself❌✅ domains⚠️ groups and categories❌⚠️ one role map per tenant
Policy versions✅✅❌⚠️ bundle revisions❌❌❌❌❌
Outputs with the decision✅✅❌✅ any JSON❌ reason string only❌❌❌❌
Queries and dataKerberos.jsCerbosSpiceDBOPACASLcasbinAccessControleasy-rbac@rbac/rbac
Policies as data, changed at runtime✅ any cache, eval-free $expr✅ disk, git, blob, DB, Hub✅ schema + relationships via API✅ bundles, policy API (WASM: rebuild)✅ JSON rules✅ DB adapters + watchers✅ grants from DB rows⚠️ loaded once at start⚠️ updateRoles; when stays code
Batch checks✅ checkResources✅ CheckResources✅ CheckBulkPermissions⚠️ design it into the policy❌✅ batchEnforce❌❌❌
Database filtering✅ planResources + Cerbos ORM adapters✅ PlanResources + ORM adapters⚠️ LookupResources returns ids✅ Compile API → SQL (server)✅ rulesToCondition, Prisma, Mongoose❌❌❌❌
"Who can access this?"⚠️ relations only (lookupSubjects)❌✅ LookupSubjects⚠️ via partial evaluation⚠️ one user's rules✅ implicit-permission APIs⚠️ a role's actions❌❌
Explains a decision✅ includeMeta✅ includeMeta✅ debug trace✅ explain / trace (server)✅ relevantRuleFor✅ enforceEx⚠️ deny reason code❌❌
Consistency tokens❌— (stateless)✅ ZedTokens——————
Tooling and operationsKerberos.jsCerbosSpiceDBOPACASLcasbinAccessControleasy-rbac@rbac/rbac
Policy test runner✅ test DSL + CLI (Cerbos suite format)✅ cerbos compile✅ zed validate✅ opa test❌❌❌❌❌
Attribute schema validation✅ JSON Schema / Zod✅ JSON Schema⚠️ typed relations and caveats⚠️ static type checks❌❌❌❌❌
Audit / decision logs✅ structured logger + events✅⚠️ paid tiers only✅ decision logs (server)❌⚠️ console logger✅ access events❌⚠️ logger callback
OpenTelemetry✅ traces + metrics✅✅ traces✅ (server)❌❌❌❌❌
Integrations⚠️ Cerbos ORM adapters, no middleware✅ React, ORM adapters— (clients)⚠️ REST client SDK✅ React, Vue, Angular, Prisma, Mongoose✅ server middlewares, Casbin.js⚠️ NestJS (separate package)⚠️ Express middleware✅ Express, NestJS, Fastify
Managed control plane—✅ Cerbos Hub (free tier + paid)✅ AuthZed Cloud (paid)⚠️ OPA Control Plane (no UI)—————
Reads Cerbos policies✅ YAML + CEL importer✅ native❌❌❌❌❌❌❌
  1. casbin's README shows a browser import, but its main entry does not bundle for the browser without Node polyfills (pnpm size:compare). Casbin.js is its separate frontend package.
  2. @rbac/rbac describes itself as zero-dependency but depends on zod.
  3. AccessControl's deny wins within a role and the roles it inherits. When a check names several roles, a grant from one of them beats a deny from another.
  4. Kerberos.js implements Cerbos's OVERRIDE_PARENT scope behaviour. The parental-consent mode and the other gaps are listed in DIVERGENCES.md.

"OPA" covers both the server and the WebAssembly SDK (@open-policy-agent/opa-wasm): rows marked "(server)" are not available in the SDK. Cerbos runs in the browser through @cerbos/embedded-client, whose WebAssembly bundles are built by Cerbos Hub. Compared versions: Kerberos.js 4.3, Cerbos 0.55, SpiceDB 1.56, OPA 1.21 with opa-wasm 1.10, CASL 7.0, casbin 5.51 (Node), AccessControl 3.1, easy-rbac 4.0 and @rbac/rbac 2.2, checked in October 2026 against each project's documentation and installed package. Throughput, cold start and bundle size of the same libraries are on the benchmarks page.

Where each one fits:

  • Kerberos.js — a JS/TS stack with no authorization infrastructure, decisions in the browser or at the edge, Cerbos-style policies and query plans.
  • Cerbos — polyglot backends and centrally governed policies, with Cerbos Hub as the control plane.
  • SpiceDB — large relationship graphs that need Zanzibar-grade consistency.
  • OPA — one general-purpose policy language across your stack, not only application authorization.
  • CASL — permissions defined in code and shared with the UI, with field-level rules and ORM filtering.
  • casbin — the same access model across many languages, with storage adapters.
  • AccessControl, easy-rbac, @rbac/rbac — a role map with a few conditions, without policy documents.

When NOT to use Kerberos.js ​

  • Polyglot backends — if Go/Python/Java services need the same decisions, a central PDP (Cerbos) beats reimplementing policies per language.
  • Zanzibar-grade consistency — the built-in ReBAC resolver reads current in-memory/cache state and deliberately has no revision tokens; if the New Enemy Problem matters for your threat model, use SpiceDB.
  • Non-engineering policy ownership — policies here live in code/storage you control; if compliance teams need a managed policy workflow and UI, that is Cerbos Hub's territory.

Features ​

AreaWhat you get
Policy engineResource / principal / role policies (with parentRoles inheritance), derived roles, conditions, variables & constants, outputs, scopes & policy versions
APIsisAllowed, checkResources, planResources (Cerbos-compatible query plans)
Dynamic policiesCache-agnostic storage with a safe, eval-free $expr codec (jsep AST allowlist)
ReBACRelation-backed derived roles + a built-in Zanzibar-lite resolver (@alexify/kerberos/relations)
ObservabilityAudit logs (console / structured / Pino), OpenTelemetry traces + metrics, decision metadata
DXPluggable validation (Zod / JSON Schema + Ajv / TypeBox), testing DSL (/tests), hand-maintained TypeScript types, browser build

Version 4.x

See the CHANGELOG for everything that changed since 3.1.0: verified Cerbos compatibility (a conformance corpus replayed against a live PDP, plus the /cerbos YAML + CEL policy importer), attribute-schema enforcement, policy-as-code tooling (the /loader subpath and the kerberos CLI), typed policy authoring, and the code-review hardening waves — a Conditions fail-open fix, restored Node-ESM named exports, and a synchronous evaluation driver (~2.5× on simple isAllowed). 4.2.0 closes the parity gaps a differential campaign against a live PDP turned up — CEL-style strict comparisons and Cerbos's resource-kind sanitization — see its upgrade notes. 4.3.0 is a performance release: rules indexed by action and role, derived roles and the scope walk evaluated only as far as the decision needs (which also aligns unreached-scope behaviour with Cerbos), and ReBAC batches that share unfinished subproblems.

Released under the MIT License.