Skip to content
ward logoWardAuth
Typed authorization policies with wildcard matching, deterministic precedence, and decision tracing.
Version
v2.0.0
Size
2.7 KB gzip
Dependencies
Zero dependencies
BrowserNode ≥22SSRDeno
createWardallowdenyruleForowns View all 13 exports

Why Ward?

Ward keeps authorization policies declarative and decision ordering deterministic. Define rules once, then explain or trace every permission decision without embedding role checks across handlers.

ts
// Before
const canUpdate = user.roles.includes('editor') && post.authorId === user.id;

// After
import { createWard, owns } from '@vielzeug/ward';

const ward = createWard([
  { role: 'editor', resource: 'posts', action: 'update', effect: 'allow', when: owns('authorId') },
]);

const decision = ward.explain({ principal: user, resource: 'posts', action: 'update', data: post });
const canUpdate = decision.allowed;
FeatureWardCASLAccessControl
Bundle size2.7 KBLarger policy engineLarger policy engine
Zero dependencies
Deterministic precedencePriority, specificity, deny, orderRule-dependentRole-grant dependent
Decision tracingtrace() candidates and winnerManual inspectionManual inspection

Use Ward when your application needs typed role/resource/action policies with explainable, deterministic outcomes.

Consider framework-specific authorization when your application only needs one framework's built-in route or component guard layer.

Installation

sh
pnpm add @vielzeug/ward
sh
npm install @vielzeug/ward
sh
yarn add @vielzeug/ward

Quick Start

Create a small policy and handle both allowed and denied decisions at the request boundary.

ts
import { createWard } from '@vielzeug/ward';

const ward = createWard([
  { role: 'viewer', resource: 'posts', action: 'read', effect: 'allow' },
  { role: 'editor', resource: 'posts', action: 'update', effect: 'allow' },
]);

const decision = ward.explain({
  principal: { id: 'u1', roles: ['editor'] },
  resource: 'posts',
  action: 'update',
});

if (decision.allowed) console.log('Update post');
else console.log(decision.reason);

Features

  • createWard() creates immutable typed policy instances.
  • allow(), deny(), and ruleFor() build role/resource/action rules.
  • WILDCARD and ANONYMOUS model broad or unauthenticated access explicitly.
  • owns() and predicate constrain rules with synchronous request data.
  • explain(), trace(), and detectConflicts() make policy decisions diagnosable.
  • forUser() creates a principal-bound view for repeated checks.
  • checkAll() evaluates multiple resource/action pairs in one call.

Documentation

See Also

  • Wayfinder — route middleware can enforce Ward decisions during navigation.
  • Conduit — inject a Ward policy into application services.
  • Herald — publish authorization outcomes as typed application events.