Skip to content

Authorization that speaks your language

Decouple your code from authorization logic and express any access control model with Verdict.

Free and open source under the MIT license.

Get started

Prove out your product, not your permissions

  • Replace scattered handler checks with a single call
  • Keep your database schema focused on your domain
  • Evolve access rules without touching application code
const allow = await verdict.authorize(user, "read", report);
See how the model works

Write access control rules that match your product language

Combine roles, relationships, attributes, time windows, and inheritance in one readable policy.

RolesRelationshipsHierarchyAttributesTime windowsRestriction & Revocation
Explore the language

Every access path in a single query

Powered by a deductive Datalog engine, Verdict compiles each check into a single SQL query with no application query waterfalls.

PolicyFacts
SQL
See how it executes

Clearer for humans, safer for coding agents

Review small, self-contained policy changes instead of auditing sprawling permission diffs across handlers and queries.

policy.vd
allow(user, "edit", document) if
  has_role(user, "editor", document) and
  not is_archived(document);

Test your rules without your database

Verify allowed and denied scenarios in milliseconds with inline test blocks, no backend server or database fixtures required.

policy.vd
test "editors can edit unarchived documents" {
  has_role(User:maya, "editor", Document:roadmap);
  assert allow(User:maya, "edit", Document:roadmap);
}
Explore testing

Run Verdict on your own terms

Own your access model with a portable, open-source engine built on databases you already trust.

Verdict Server
Verdict Postgres
Compare runtimes

Free your code from authorization logic

Verdict replaces procedural permission queries with declarative rules, application facts, and simple authorization checks.

Step 1

Write your rules

Bring your access model together in one readable policy file. State what grants access using your product's own terms, without writing the lookup procedure to check it.

policy.vd
allow(user, "read", report) if
  shared_with(report, user);

allow(user, "read", report) if
  in_workspace(report, workspace) and
  member(user, workspace);

Step 2

Supply your facts

Your policy only needs the slice of application data that affects access. Express relationships like direct shares and workspace membership as simple facts that match directly against your rules.

Facts
shared_with(Report:q3_revenue, User:maya);
in_workspace(Report:q3_revenue, Workspace:acme);
member(User:liam, Workspace:acme);
member(User:, Workspace:);
in_workspace(Report:, Workspace:);

Step 3

Delete your code

Replace branching logic and database queries with one verdict.authorize() call. When access requirements change, your policy updates while application code stays the same.

access.ts · diff
const rep = await db.report.findUnique({
  where: { id: report.id },
  include: { shares: true, workspace: { include: { members: true } } },
});
const isShared = rep?.shares.some((s) => s.userId === user.id);
const isMember = rep?.workspace.members.some(
  (m) => m.userId === user.id,
);
const allow = isShared || isMember;
const allow = await verdict.authorize(user, "read", report);
if (!allow) return new Response("Forbidden", { status: 403 });

A flexible language for every pattern

Model classic access patterns or define custom rules specific to your domain in one unified policy language.

Derive permissions from roles

Define roles on any resource and effortlessly derive the actions they permit, without maintaining per-user permission lists.

policy.vd
has_permission(user, "read", document) if
  has_role(user, "viewer", document) or
  has_role(user, "editor", document);

has_permission(user, "edit", document) if
  has_role(user, "editor", document);

Test your rules without your database

Inline tests let you validate scenarios inside your policy without tracing application code or setting up database fixtures.

policy.vd
allow(user, "edit", document) if
  has_role(user, "editor", document) and
  not is_archived(document);

test "editors cannot edit archived documents" {
  has_role(User:alice, "editor", Document:roadmap);
  is_archived(Document:spec);
  has_role(User:alice, "editor", Document:spec);

  assert allow(User:alice, "edit", Document:roadmap);
  assert not allow(User:alice, "edit", Document:spec);
}

Combine patterns, or invent your own

Compose rules and synthesize intermediate facts to create the exact access control paths your application needs.

Carry roles through nested hierarchies

Propagate roles across any depth of nested folders and files with recursive rules, then map inherited and direct roles alike to the actions they permit.

Hierarchy + Roles + Permissions

policy.vd
// Define folder ancestry across any nesting depth
is_ancestor(folder, item) if
  parent(item, folder);

is_ancestor(ancestor, item) if
  parent(item, folder) and
  is_ancestor(ancestor, folder);

// Roles assigned on any ancestor folder carry down to everything inside it
has_role(user, role, item) if
  is_ancestor(folder, item) and
  has_role(user, role, folder);

// Map inherited or direct roles to file and folder permissions
has_permission(user, "read", item) if
  has_role(user, "viewer", item) or
  has_role(user, "editor", item);

has_permission(user, "edit", item) if
  has_role(user, "editor", item);

The deductive engine that solves access for you

A logic compiler for access control

Verdict's logic engine eliminates procedural code by compiling your declarative rules and access checks into specialized SQL queries. Instead of hand-writing SQL or processing data across multiple database hops in your application, every check runs in a single query right next to your data.

Dynamically optimized for every request

Verdict's runtime compiler tailors a unique query plan to the exact arguments of each request, giving you optimized SQL across every access path, even with large, complex rule sets, without having to design or hand-tune a single query.

Publish new rules with zero rollout time

Because Verdict compiles queries on the fly at request time, you can hot-swap policies directly in running processes, shipping new rules instantly without code changes, database migrations, or redeployments.

Request & matching rules
// Authorization request
allow(User:liam, "read", Report:q3_revenue)

// Matching rules in policy.vd
allow(user, "read", report) if
  shared_with(report, user);

allow(user, "read", report) if
  in_workspace(report, workspace) and
  member(user, workspace);
Compiled SQL query
SELECT EXISTS (
  SELECT 1 FROM shared_with
  WHERE report = 'Report:q3_revenue'
    AND "user" = 'User:liam'
  UNION ALL
  SELECT 1 FROM in_workspace
  JOIN member ON member.workspace = in_workspace.workspace
  WHERE in_workspace.report = 'Report:q3_revenue'
    AND member."user" = 'User:liam'
) AS allowed;

Always make the right decision with Verdict

Verdict Server

A central authority for access control

Combine Verdict's powerful logic engine with an embedded fact store purpose-built for authorization.

APIs & ProtocolsCachingVerdict EngineSQLite

A single control point

Manage and store authorization facts with one consistent view for your entire system. Rules, data, and decisions all in one place.

Read and write facts from any application

Write facts and request authorization checks from any stack. And combine facts from different applications to one shared store.

Update policy without redeploying

Publish policy updates across every application instantly. Eliminate version skew and waiting for code deployments to fix rules.

Explain every decision

Audit authorization across systems without piecing together application code and scattered data. Unified audit logs provide complete traces and source facts as proof.

Stay in control of access with Verdict Server

Need facts in your own database?

Use the same policy language and fact model with your own Postgres infrastructure.

Verdict Postgres

Run Verdict authorization queries against your existing database

Use Verdict in your application and evaluate rules directly against your Postgres database.

Your ApplicationVerdict EngineYour PostgresDatabase

Start building with Verdict

The clearer way to think about access.

Get started