@gomagentic/verdict-sdk
v0.1.1
Published
Verdict client SDK for JavaScript/TypeScript: one interface (check, batchCheck, authorize, explain, simulate) over embedded engines, hybrid SyncedVerdict, or a remote PDP.
Maintainers
Readme
@gomagentic/verdict-sdk
One client interface over embedded, hybrid, and remote authorization.
Part of Verdict — a serverless-first authorization engine. Policies (RBAC / ABAC / ReBAC) compile once and decide in microseconds, embedded in your app, behind a central PDP, or synced to the edge.
Install
npm install @gomagentic/verdict-sdkThe client interface
Every deployment mode satisfies one interface, VerdictClient. Application code depends on it and never on the mode, so you swap the implementation and keep the call sites:
interface VerdictClient {
check(request: CheckRequest): Promise<Decision>;
batchCheck(request: BatchCheckRequest): Promise<BatchDecision>;
authorize(input: AuthorizeInput): Promise<boolean>; // one action, default-deny boolean
explain(request: CheckRequest): Promise<Decision>; // check() plus the full trace
simulate(policyId: string, request: CheckRequest, options?: SimulateOptions): Promise<Decision>;
}check/batchCheck— evaluate one or many requests and return theDecision/BatchDecisionshapes from@gomagentic/verdict-core.authorize— a convenience overcheckfor exactly one action: takes{ principal, resource, action, context?, tenantId? }and returns a default-deny boolean.explain— the same evaluation ascheck, carrying the full trace (matched rule, conditions, missing attributes).simulate— a control-plane dry run against a policy's draft (default) or published state viaSimulateOptions.useDraft. It requires the PDP; embedded clients reject it.
Two helpers back the boolean path and are exported directly: toCheckRequest(input) turns an AuthorizeInput into a CheckRequest, and decisionAllows(decision, action) reads a single action's effect off a Decision.
Remote PDP
RemoteClient talks to a PDP over HTTP. It uses the global fetch unless you pass fetchImpl:
import { RemoteClient, decisionAllows } from "@gomagentic/verdict-sdk";
const client = new RemoteClient({
baseUrl: "https://pdp.example.com",
token: process.env.VERDICT_TOKEN!, // API key (check scope) or OIDC bearer
retries: 2, // network errors and 5xx retry; 4xx never does
});
const decision = await client.check({
principal: { id: "u_42", roles: ["employee"], attr: { department: "eng" } },
resource: { kind: "leave", id: "lv_9", attr: { managerId: "u_42" } },
actions: ["approve"],
});
decisionAllows(decision, "approve"); // true | false
// Or the single-action boolean shortcut:
const ok = await client.authorize({
principal: { id: "u_42", roles: ["employee"] },
resource: { kind: "leave", id: "lv_9" },
action: "approve",
});Network errors and 5xx retry with linear backoff (decision calls are idempotent); 4xx is authoritative, never retried, and surfaces the PDP's error taxonomy as a VerdictError. retryDelayMs, sleep, and fetchImpl are injectable for tuning, testing, and exotic runtimes. Pass tenant only when using tenantless (admin) credentials.
Embedded
EmbeddedClient wraps an in-process engine — anything satisfying LocalEvaluator (synchronous check / batchCheck / explain). A Verdict instance from @gomagentic/verdict-engine satisfies it structurally, so the SDK depends on no engine package:
import { Verdict } from "@gomagentic/verdict-engine";
import { EmbeddedClient } from "@gomagentic/verdict-sdk";
const engine = await Verdict.fromBundle(bundle);
const client = new EmbeddedClient(engine); // same VerdictClient interfacesimulate() rejects on embedded clients — drafts live in the control plane. For local what-if runs, compile an ephemeral program with @gomagentic/verdict-dsl.
Hybrid
For hybrid and edge deployments, back an EmbeddedClient with a SyncedVerdict from @gomagentic/verdict-sync instead of a plain Verdict. It also satisfies LocalEvaluator structurally, so decisions stay local and zero-network while policy bundles sync from a control plane. The call sites are unchanged.
Documentation
License
Apache-2.0
