@gomagentic/verdict-server
v0.1.1
Published
Verdict remote PDP and management API (Hono). Runs on Workers, Lambda, Deno, Bun, Node, and containers.
Maintainers
Readme
@gomagentic/verdict-server
The remote PDP and management API. Runs on Workers, Lambda, Deno, Bun, Node.
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.
It's built on Hono, so the same createApp() runs unmodified on Cloudflare Workers, AWS Lambda, Deno, Bun, Node, and containers. It serves the REST decision API (check / batch / explain), bundle sync for hybrid-mode clients, and full policy / tenant / API-key management — with API-key, console-session, and OIDC/JWT auth plus optional per-identity rate limiting.
Install
npm install @gomagentic/verdict-serverIt composes the rest of the control plane: @gomagentic/verdict-store (the control-plane store + bundle artifacts), @gomagentic/verdict-control (policy lifecycle, signing, keys, accounts), and @gomagentic/verdict-engine (the evaluator). It depends on hono for HTTP.
Create the app
createApp(options) takes a ServerOptions — at minimum a ControlPlaneStore — and returns a CreatedApp: the Hono app (with .fetch), plus the live engines (an EngineManager), policyService, and apiKeys services. Build a store, then serve app.fetch however your runtime expects.
import { serve } from "@hono/node-server";
import { applyMigrations, SQLITE_MIGRATIONS, SqlStore } from "@gomagentic/verdict-store";
import { NodeSqliteDriver } from "@gomagentic/verdict-store/node";
import { generateSigningKeyPair } from "@gomagentic/verdict-control";
import { createApp } from "@gomagentic/verdict-server";
// Persistent SQLite control plane (swap for PostgresDriver / D1Driver in production).
const driver = new NodeSqliteDriver("./verdict.db");
await applyMigrations(driver, SQLITE_MIGRATIONS); // schema ships with the package
const store = new SqlStore(driver);
// In production, load the signing key from a secrets manager.
const signing = await generateSigningKeyPair("pdp-key-1");
const { app, policyService } = createApp({
store,
adminToken: process.env.VERDICT_ADMIN_TOKEN ?? "dev-admin-token",
signing: { keyId: signing.keyId, privateKeyJwk: signing.privateKeyJwk },
cors: { origin: "*" }, // lock to explicit origins in production
});
serve({ fetch: app.fetch, port: 8787 });On Workers (or any runtime with a global fetch handler), the same app is the default export:
const { app } = createApp({ store, /* … */ });
export default app; // Hono exposes app.fetch — Workers calls it directlycreateApp also accepts oidc, rateLimit, auditSink, bundleOverflow (object storage for artifacts over the D1 2 MB row cap), accounts, passwordIterations, and engineCache — see the source for the full ServerOptions.
API surface
All routes are under /v1 and require Authorization: Bearer <token> except GET /healthz. Full reference: docs/spec/rest-api.md.
Decisions (scope check)
POST /v1/check— bodyCheckRequest, returnsDecision.POST /v1/check/batch— bodyBatchCheckRequest, returnsBatchDecision.POST /v1/explain— a check with the evaluation trace forced on.
Bundle sync (scope check, for hybrid-mode clients)
GET /v1/bundles/latest— latest readyBundleEnvelope;ETag+If-None-Match→304.GET /v1/bundles/:version— immutable fetch of a specific version.GET /v1/bundles/watch— SSE stream, onebundleevent per rebuild.
Policy management (scopes policy:read / policy:write)
POST /v1/policies,GET /v1/policies,GET /v1/policies/:idPUT /v1/policies/:id(optimistic concurrency viaIf-Match),DELETE /v1/policies/:idPOST /v1/policies/:id/validate,/simulate,/publish,/rollbackGET /v1/policies/:id/versions
Console accounts & projects (self-serve onboarding, disable with accounts: false)
POST /v1/auth/signup,/login(open),/logout,GET /v1/auth/mePOST /v1/projects,GET /v1/projects
Administration (scope admin)
POST /v1/tenants,GET /v1/tenantsPOST /v1/api-keys,GET /v1/api-keys,DELETE /v1/api-keys/:idGET /v1/audit,GET /v1/metricsGET /healthz— unauthenticated liveness.
Auth & rate limiting
- API keys — tenant-bound
vk_live_…/vk_test_…secrets; the middleware resolves them to anAuthContext({ tenantId, scopes, identity }). Consolevs_…sessions authenticate the same way, tenantless and gated on project membership. - OIDC / JWT — pass the
oidcoption to haveOidcVerifier(OidcOptions) verify RS256/ES256 bearer tokens against a cached JWKS (iss/aud/exp/nbfchecks), yielding aVerifiedTokenwhose tenant and scopes come from configurable claims. - Rate limiting — pass the
rateLimitoption to enableRateLimiter(RateLimitOptions), an in-process per-identity token bucket. On refusal it returns aRateLimitVerdictand the request gets429withRetry-After.
Documentation
License
Apache-2.0
