npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@atlasent/sdk

v2.16.0

Published

TypeScript SDK for the AtlaSent authorization API

Readme

@atlasent/sdk

Execution-time authorization for AI agents, in TypeScript. Two methods, zero runtime dependencies, one function call per decision.

npm i @atlasent/sdk

Quickstart

import { AtlaSentClient } from "@atlasent/sdk";

const client = new AtlaSentClient({ apiKey: process.env.ATLASENT_API_KEY! });

const gate = await client.deployGate({
  context: { repo: "atlasent/api", commit: process.env.GIT_SHA },
});

if (!gate.allowed) {
  console.error("Deploy blocked:", gate.reason);
  process.exit(1);
}

// runDeploy();

That's it. deployGate() performs the V1 Deploy Gate sequence against production.deploy: evaluate() calls POST /v1-evaluate, receives a permit when allowed, then verifyPermit() calls POST /v1-verify-permit before your deployment can run. A clean deny is returned as a block result — network / server / auth failures are thrown.

Why two calls? (the mental model)

AtlaSent is authorize-before-execute, not after-the-fact logging. The two-step pattern is intentional:

  1. evaluate() asks the policy engine: "should this action run?" Returns a decision and, when allowed, a single-use permit token — a cryptographic proof that evaluation happened.
  2. verifyPermit() consumes the permit server-side before the action executes. This is what makes the audit chain tamper-evident: every execution is hash-linked to the evaluation that authorized it, and no permit can be replayed.

You rarely call them separately. deployGate() wraps both steps for deploy workflows. The Python SDK's protect() wraps them for arbitrary actions. Use the raw two-step form only when something external — a human approval, a change-window check — needs to happen between evaluate and execute (evaluate → wait → verify → run).

Simple V1 surface

client.evaluate({ agent, action, context? })
  // → { decision: "allow" | "deny" | "hold" | "escalate", permitId, reason, auditHash, timestamp }

client.verifyPermit({ permitId, agent?, action?, context? })
  // → { verified, outcome, permitHash, timestamp }

client.deployGate({ agent?, action?, context? })
  // defaults action to "production.deploy" and returns { allowed, reason, evidence }

verifyPermit() confirms a previously-issued permit server-side. Signed/offline permit artifacts never imply deployment authorization by themselves.

Decision replay

Re-evaluate a recorded decision against its originally-pinned policy bundle and engine version. Side-effect-free: no audit row is written, no permit is minted (ADR-016 mode: "replay" sentinel). Useful for compliance review, regression-testing bundle changes, and post-incident investigation.

Two surfaces exist; pick the one that matches your call site:

// SDK-canonical (preferred for new code) — wire DECISION_CHANGED is normalized
// to POLICY_DRIFT; 409 replay_not_eligible returns ENGINE_DRIFT or BUNDLE_MISSING
// instead of throwing. You can always `switch` on the variance kind.
const r = await client.replay({ evaluationId: "dec_abc123" });
switch (r.varianceKind) {
  case "NONE":             /* replay agrees */                          break;
  case "POLICY_DRIFT":     /* same envelope/bundle, different decision */ break;
  case "ENVELOPE_DRIFT":   /* recorded envelope no longer hashes */     break;
  case "ENGINE_DRIFT":     /* original engine retired beyond archival */ break;
  case "BUNDLE_MISSING":   /* original eval had no bundle pinned */     break;
  case "CHAIN_TAMPER":     /* audit-chain v5 detector tripped */        break;
}
// Raw-wire surface — variance values pass through verbatim
// (NONE / DECISION_CHANGED / ENVELOPE_DRIFT); 409 throws AtlaSentError
const result = await client.replayDecision("dec_abc123");
if (result.variance === "DECISION_CHANGED") {
  console.warn(
    `Decision ${result.decision_id} drifted: ` +
    `${result.original_decision} → ${result.replay_decision}`,
  );
}

/v1/decisions/:id/replay is alpha per atlasent-api/docs/STABLE_V2_PROMOTION.md — wire shapes can shift without a deprecation cycle until it graduates to stable v1.

CI deploy-gate pattern

import { AtlaSentClient } from "@atlasent/sdk";

const client = new AtlaSentClient({ apiKey: process.env.ATLASENT_API_KEY! });

const evaluation = await client.evaluate({
  agent: "ci-deploy-bot",
  action: "production.deploy",
  context: { service: "billing-api", commit: process.env.GIT_SHA },
});

if (evaluation.decision !== "allow") {
  console.error("Deploy blocked:", evaluation.reason);
  process.exit(1);
}

const verification = await client.verifyPermit({
  permitId: evaluation.permitId,
});

if (!verification.verified) {
  console.error("Permit verification failed — aborting");
  process.exit(1);
}

// runDeploy();

See examples/deploy-gate.ts for a complete CI-shaped script.

Constructor options

new AtlaSentClient({
  apiKey: "ask_live_...", // required
  baseUrl: "https://api.atlasent.io", // default
  timeoutMs: 10_000, // default — per-request
  fetch: customFetch, // default: globalThis.fetch
});

Error handling

The SDK throws exactly one error type — AtlaSentError — with a flat shape that mirrors Stripe / Octokit / Supabase conventions:

import { AtlaSentError } from "@atlasent/sdk";

try {
  await client.evaluate({ agent: "a", action: "b" });
} catch (err) {
  if (err instanceof AtlaSentError) {
    console.error(err.code, err.status, err.requestId, err.retryAfterMs);
  }
}

| err.code | When it's thrown | | ----------------- | ---------------------------------------- | | invalid_api_key | HTTP 401 | | forbidden | HTTP 403 | | rate_limited | HTTP 429 (check err.retryAfterMs) | | bad_request | HTTP 4xx (other than 401/403/429) | | server_error | HTTP 5xx | | timeout | timeoutMs exceeded | | network | DNS / connection failure, fetch threw | | bad_response | non-JSON body or missing required fields |

Every AtlaSentError carries err.requestId — the UUID the SDK sent as X-Request-ID, correlatable in your server logs.

Design choices

  • Fail-closed. A clean DENY is returned so your code explicitly handles it; every other failure throws, so no action proceeds silently.
  • Native fetch only. No axios, no polyfills. Node 20+ has everything we need.
  • Zero runtime dependencies. Strongly typed via plain TS interfaces.
  • Bearer-token auth. Authorization: Bearer <apiKey> so request-body logs never capture the key. (The SDK also includes api_key in the body for wire compat with the current policy engine; that will be removed once the server drops body-based auth.)

Requirements

  • Node.js 20 or newer (native fetch, AbortSignal.timeout, crypto.randomUUID).
  • Browser: Chrome 103+, Firefox 100+, Safari 16+, Edge 103+. The SDK uses AbortSignal.timeout for per-request deadlines — the constructor throws a clear AtlaSentError(code: "network") on runtimes that lack it so the failure is loud rather than silent.
  • TypeScript 5.0+ for best type-inference ergonomics (older is fine — types are plain interfaces).

Hono middleware

Drop-in protection for Hono routes via the @atlasent/sdk/hono subpath export (requires hono as a peer dep):

import { Hono } from "hono";
import { atlaSentGuard, atlaSentErrorHandler } from "@atlasent/sdk/hono";

const app = new Hono();
app.onError(atlaSentErrorHandler());

app.post(
  "/deploy/:service",
  atlaSentGuard({
    action: (c) => `deploy_${c.req.param("service")}`,
    agent: (c) => c.req.header("x-agent-id") ?? "anonymous",
    context: async (c) => ({ commit: (await c.req.json()).commit }),
  }),
  (c) => c.json({ ok: true, permit: c.get("atlasent") }),
);

atlaSentGuard calls protect() under the hood — fail-closed semantics. On allow it stashes a Permit on the context (key: "atlasent", override via options.key). On deny or transport error it throws; atlaSentErrorHandler maps those to 403 / 503 responses so every guarded route shares one error-handling path.

Upcoming migration: after @atlasent/enforce reaches GA the guard API will change to accept a pre-constructed Enforce instance instead of per-route action/agent/context options. The current API is not deprecated until that ships. See the CHANGELOG for the full before/after and contract/ENFORCE_PACK.md for migration details.

Browser support

The SDK is universal and works in modern browsers with no build-time changes:

import { AtlaSentClient } from "@atlasent/sdk";

const client = new AtlaSentClient({
  apiKey: import.meta.env.VITE_ATLASENT_API_KEY,
  baseUrl: import.meta.env.VITE_ATLASENT_API_URL,
});

const result = await client.evaluate({
  agent: currentUser.id,
  action: "view_sensitive_report",
});

Auth model in browser contexts. Shipping a long-lived API key in a browser bundle exposes it in DevTools and makes it replayable if exfiltrated. The recommended options in increasing security order are:

  • Option B — browser-scoped keys (short term): Create a read-only, scope-restricted, IP-allowlisted key class from the AtlaSent console. Safe for internal dashboards where you control the network. Not suitable for public-facing apps.
  • Option A — session-token mode (recommended for atlasent-hosted surfaces): After SSO sign-in, the frontend obtains a short-lived (15-min) Bearer token from GET /v1-session/token bound to the user's scopes and tenant. The SDK handles token refresh transparently. See atlasent-api#144.

The User-Agent header is set to @atlasent/sdk/<version> browser in browser runtimes (browsers strip this header anyway — it's harmless) and @atlasent/sdk/<version> node/<node-version> in Node.

Related

  • Python SDK: same repo, ../python/. Wire-compatible.

License

Apache-2.0 — see LICENSE.