Skip to content

Repository files navigation

Vengtoo Node SDK

TypeScript/JavaScript client for Vengtoo — works with both Vengtoo Cloud and the Vengtoo Agent.

Zero dependencies. Requires Node.js 18+ (uses native fetch).

Install

npm install @vengtoo/sdk

Usage

Cloud Mode

import { Vengtoo } from "@vengtoo/sdk";

const vengtoo = new Vengtoo({ apiKey: "azx_..." });

const allowed = await vengtoo.check({ id: "user:123", type: "user" }, "read", {
  type: "document",
  id: "doc:456",
});

OAuth2 Client Credentials

For service-to-service auth, pass clientId + clientSecret (secret is prefixed azx_cs_). The SDK exchanges credentials at the token endpoint, caches the JWT in memory, refreshes ~60s before expiry, and retries once automatically on a 401.

const vengtoo = new Vengtoo({
  clientId: "my-client-id",
  clientSecret: "azx_cs_...",
});

Equivalent curl for the underlying token exchange:

curl -X POST https://api.vengtoo.com/v1/oauth/token \
  -d grant_type=client_credentials \
  -d client_id=my-client-id \
  -d client_secret=azx_cs_...

Providing both apiKey and OAuth credentials is rejected at construction. A bad clientId / clientSecret surfaces as a VengtooOAuthError (distinct from VengtooError) with a message pointing you at the OAuth exchange.

Agent Mode (local)

const vengtoo = new Vengtoo({ baseUrl: "http://127.0.0.1:8181" });

Full Evaluation Response

const resp = await vengtoo.evaluate({
  subject: { id: "user:123", type: "user" },
  action: { name: "read" },
  resource: { type: "document", id: "doc:456" },
  context: { ip: "10.0.0.1" },
});
// resp.decision, resp.context?.reason, resp.context?.policy_id, resp.context?.access_path

Batch Evaluation

Evaluate up to 50 checks in one round-trip (AuthZEN 1.0). Top-level fields act as defaults that individual items inherit:

const resp = await vengtoo.evaluateBatch({
  subject: { id: "user:123", type: "user" }, // default for all items
  action: { name: "read" },
  evaluations: [
    { resource: { type: "document", id: "doc:1" } },
    { resource: { type: "document", id: "doc:2" } },
  ],
});
// resp.evaluations[i].decision, positional

Human-in-the-Loop Approvals

When a policy requires human approval, evaluate() returns reason_code: "authorization_pending". evaluateWithApproval() handles the wait — polling at the server-recommended interval until a human approves or denies in the Vengtoo dashboard:

const resp = await vengtoo.evaluateWithApproval(req, {
  timeoutMs: 5 * 60_000,
  onPending: (authReqId, expiresIn) =>
    console.log(`waiting for approval ${authReqId} (expires in ${expiresIn}s)`),
  signal: abortController.signal, // optional: cancel the wait
});
// Terminal reason_codes: "approved_by_human", "access_denied",
// "approval_timeout" (no human answered), "polling_error" (network),
// "canceled" (your signal aborted). Never throws for these — always fails closed.

Delegations

Grant an agent the delegator's permission scope for exactly the duration of a task — created before, revoked after, even when the task throws. A failed revocation is reported in the thrown error, never swallowed:

const result = await vengtoo.withDelegation(
  {
    delegator_id: userEntityId,
    delegate_id: agentEntityId,
    scope: ["invoices:read", "invoices:submit"], // optional: attenuate further
    description: "Q3 invoice processing run", // optional: shows in audit/dashboard
  },
  async (delegationId) => runWorkflow(),
);

The delegate's effective permissions are always the intersection of its own policies and the delegator's — scope narrows that further, it can never escalate.

Express Middleware

Two layers, two jobs: the middleware is the route-level perimeter ("may this caller touch this API area at all?") — it evaluates type-level policies and catches routes you forgot to protect. For per-object decisions, call check()/evaluate() inside the handler where the resource is known.

The middleware takes a subject extractor — you tell it where your authentication layer put the caller's identity (mount authn before this):

app.use(
  "/documents",
  vengtoo.middleware("document", "read", (req) => {
    const user = (req as any).user; // set by your authn middleware
    if (!user) throw new Error("unauthenticated");
    return { id: user.id, type: "user" };
  }),
);

Or from a trusted gateway header — only safe when an edge component you control authenticates callers and sets the header; if clients can reach this service directly, they can impersonate anyone:

vengtoo.middleware("document", "read", (req) => {
  const id = req.headers["x-user-id"]; // set by our API gateway
  if (!id || Array.isArray(id)) throw new Error("missing identity header");
  return { id, type: "user" };
});

An extractor throw (or a subject with no id/external_id) → 401. Policy deny → 403. Authorization infrastructure failure → 500, fail closed.

Options

new Vengtoo({
  apiKey: "azx_...", // API key for cloud mode
  baseUrl: "http://127.0.0.1:8181", // Custom URL (agent mode)
  timeout: 5000, // Per-request timeout in ms (default: 10000)
  maxRetries: 3, // Max retries on 5xx/429 (default: 2)
});

Error Handling

import { VengtooError, VengtooOAuthError } from "@vengtoo/sdk";

try {
  await vengtoo.evaluate(req);
} catch (err) {
  if (err instanceof VengtooOAuthError) {
    /* bad clientId/clientSecret */
  }
  if (err instanceof VengtooError) {
    err.isAuthError; // 401 — bad API key or expired token
    err.isForbidden; // 403
    err.isNotFound; // 404
    err.isServerError; // 5xx — retries exhausted
  }
}

The SDK automatically retries on 5xx and 429 responses (default: 2 retries, honoring the server's Retry-After hint). Other 4xx errors are never retried. With OAuth, a 401 triggers one token refresh before failing.

Types

interface Subject {
  type: string; // required
  id?: string; // id or external_id required
  external_id?: string;
  properties?: Record<string, unknown>;
}

interface Resource {
  type: string; // required
  id?: string; // omit both id and external_id for type-level checks
  external_id?: string;
  properties?: Record<string, unknown>;
}

interface Action {
  name: string; // required
  properties?: Record<string, unknown>;
}

interface EvaluationRequest {
  subject: Subject;
  resource: Resource;
  action: Action;
  context?: Record<string, unknown>;
}

interface EvaluationResponse {
  decision: boolean;
  context?: EvaluationContext; // reason, reason_code, policy_id, access_path + HITL fields
}

Required on every check (matching the API and AuthZEN 1.0): subject.type, subject.id (or external_id), resource.type, and action.name. The SDK validates these locally so you get an immediate, clear error instead of a server 400.

License

Apache-2.0 — see LICENSE.

About

Official Node.js/TypeScript SDK for Vengtoo — authorization client with Express middleware

Topics

Resources

Security policy

Stars

Watchers

Forks

Packages

Used by

Contributors

Languages