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

@allowly/sdk

v0.6.0

Published

TypeScript SDK for the Allowly API

Readme

@allowly/sdk

TypeScript SDK for the Allowly runtime API. Check an agent action before it runs, handle allow/deny/confirm/escalate decisions, and verify signed receipts.

Requires Node.js 20 or newer. This package is ESM-only.

Install

npm install @allowly/sdk

Check an action

import { Allowly } from "@allowly/sdk";

const allowly = new Allowly({
  apiKey: process.env.ALLOWLY_API_KEY!,
});

const result = await allowly.check({
  authorizationId: "auth_...",
  actions: ["email.send"],
  resource: "gmail:thread:abc",
  context: { initiated_by: "user" },
});

const decision = result.results["email.send"];
if (decision.decision === "allow") {
  await sendTheEmail();
} else {
  // deny stops; confirm and escalate pause for your application's resolution flow.
  throw new Error(`Action not allowed: ${decision.decision} (${decision.reason})`);
}

Only allow permits execution. Unavailable checks fail closed unless that action has an explicit fail_open fallback configured.

Allowly agent identity

Create the agent in the dashboard, then run allowly agent enroll <exact-agent-id> from the Allowly CLI. Store the resulting private credential on the trusted machine that runs the agent. Define its policy, then create a new authorization for that agent. CLI-only setups can still create a live policy before enrollment. The credential identifies the agent; the authorization and policy still decide what it may do. The workspace runtime API key is still required.

import { Allowly, NativeAgentCredential } from "@allowly/sdk";

const credential = await NativeAgentCredential.fromFile("/secure/path/agent.json");
const allowly = new Allowly({
  apiKey: process.env.ALLOWLY_API_KEY!,
  agentTokenSupplier: credential.token,
});

await allowly.check({ authorizationId: "auth_...", actions: ["order.submit"] });

The SDK signs a fresh 60-second token for each request. The private key remains in your runtime; do not commit or log the credential file. The CLI registers only its public key with Allowly.

Existing Auth0 agent identity

For an authorization bound to an Auth0 machine identity, supply its short-lived access token separately from the Allowly runtime key. The supplier runs for each check or local execution. Use your existing OAuth client library for Auth0 token reuse and keep the client secret outside this SDK. New self-service Auth0 setup is unavailable. Contact us to add your own identity provider.

import { Allowly } from "@allowly/sdk";

const allowly = new Allowly({
  apiKey: process.env.ALLOWLY_API_KEY!,
  agentTokenSupplier: getAuth0AgentToken,
});

await allowly.check({
  authorizationId: "auth_...",
  actions: ["order.submit"],
  clientTimestamp: new Date(),
});

Identity-enabled checks always fail closed, including token supplier failures and identity_verification_unavailable responses.

Execute HTTP with provider credentials kept locally

Enable a provider in Settings → Executables and grant the exact catalog operation to an action before using executeHttp. The SDK sends a request descriptor and byte commitments to Allowly for approval, then sends the original HTTP request from your runtime. Allowly never receives provider credentials or sends the provider request. Allowly receives the method, origin, path, query, content type, byte counts, header names and hashes, body hash, and customer-reported policy input. Header values and body bytes stay local. Put provider credentials in local headers; do not put secrets in the URL, query, or policy input.

const result = await allowly.executeHttp(
  "https://harvest.greenhouse.io/v3/candidates?per_page=1&private=false",
  {
    operationId: "greenhouse-candidates-list-page-1", // persist and reuse this ID
    authorizationId: "auth_...",
    enabledExecutableId: "exe_...",
    catalogOperationId: "greenhouse.candidates.list",
    action: "greenhouse.candidates.list",
    method: "GET",
    headers: {
      authorization: `Bearer ${process.env.GREENHOUSE_ACCESS_TOKEN}`,
    },
    policyInput: {
      resource: "greenhouse:candidates",
      context: { pageSize: 1, includePrivate: false }, // customer-reported
    },
    journalDirectory: "/var/lib/my-agent/allowly-executions",
  },
);

if (result.state === "not_allowed") return;
// Use result.providerResponse locally when present (status and Uint8Array body).
if (result.outcomePending || result.state === "unknown") {
  // Retry later when Allowly is reachable. This never sends the provider request again.
  await allowly.resumeHttpExecution({
    operationId: "greenhouse-candidates-list-page-1",
    journalDirectory: "/var/lib/my-agent/allowly-executions",
  });
}

This uses Greenhouse's documented Harvest v3 List candidates endpoint with an OAuth Bearer access token. The GET has no request body, limits the page to one non-private candidate, and needs the harvest:candidates:list scope with a Site Admin authorizing user.

The private journal stores request commitments, the approval, and a pending outcome upload. It does not store the provider credential or request body. Once dispatch has been attempted, resume only uploads the same stored outcome or reconciles the same operation ID. Redirects are not followed, DNS must resolve only to public addresses, and the chosen address is pinned for the TLS connection.

If outcome upload fails after dispatch, the helper returns the local result with outcomePending: true instead of throwing. response is null until an Allowly outcome reply is confirmed; this does not prove the server stored nothing. providerResponse contains the observed local HTTP status and body bytes, or null if no response was observed. Response bytes are not uploaded or stored in the journal, so resumed calls return providerResponse: null. Keep them in your own private storage if needed. Resume retries the saved report with the same idempotency key; it does not repeat the provider action. Approval, dispatch-claim, and local journal errors still fail closed. This flag is separate from a decision receipt waiting to be signed.

The decision receipt can still be pending when the HTTP response returns. Finish and verify that evidence later; this does not contact the provider:

import {
  completeCustomerExecutionEvidence,
  fetchKeysDoc,
  loadKeysFromJson,
} from "@allowly/sdk";

const keys = loadKeysFromJson(await fetchKeysDoc(configuredWorkspaceId));
const completeEvidence = await completeCustomerExecutionEvidence(
  allowly,
  result.evidencePackage,
  keys,
  {
    expectedWorkspaceId: configuredWorkspaceId,
    trustedKeyFingerprints: configuredKeyFingerprints,
  },
);
await saveEvidence(completeEvidence);

receipt evidence records the customer runtime's reported HTTP outcome. It does not verify business completion. If the policy upgrades the request to witnessed, executeHttp fails closed unless native witness options were provided. The current native profile is limited to HTTPS on port 443, HTTP/1.1 over TLS 1.2, a 2 KiB request transcript, a 16 KiB UTF-8 response, and no redirect follow. Use witnessed mode only when the catalog and deployed witness service report it available.

Run allowly setup witness from @allowly-ai/cli for the same workspace before using witnessed execution. The default path downloads a verified precompiled Rust helper. Use allowly setup witness --build-from-source to download reviewed Allowly adapter source and build it with pinned official TLSNotary libraries. That path needs Rust 1.95.0, Cargo, Git, Bash, and a native C build toolchain. Both paths verify release checksums and remain blocked until reviewed witness-v0.1.0 assets are published and their manifest digest is pinned in the CLI. Offline setup still accepts --archive FILE --sha256 HEX or a reviewed --helper FILE. The interactive command pins the public witness key after you compare its fingerprint with the authenticated workspace page. Then request witnessed mode and give the SDK a new evidence directory for each operation:

await allowly.executeHttp("https://api.vendor.example/v1/items", {
  operationId: "items-read-1",
  authorizationId: "auth_...",
  enabledExecutableId: "exe_...",
  catalogOperationId: "vendor.items.list",
  action: "vendor.items.list",
  evidenceMode: "witnessed",
  journalDirectory: "/var/lib/my-agent/allowly-executions",
  witness: { evidenceDirectory: "/var/lib/my-agent/allowly-evidence/items-read-1" },
});

The SDK selects the installed configuration by the approved workspace ID and checks the pinned key against the witness session before it starts the helper. For a local witness with a private CA, allowly setup witness --witness-ca-cert also pins that CA. The SDK checks its fingerprint before dispatch and passes it to the helper for the witness socket only. Provider HTTPS trust is unchanged. To use a separately provisioned helper and public key, provide both witness.nativeBinaryPath and witness.trustedNotaryKeyPath with the witness.workspaceId you expect.

The helper and Allowly-hosted Witness Bridge source live together in allowly_mcp/witness. The helper wraps unchanged TLSNotary libraries pinned to v0.1.0-alpha.15 / 47aee45b53e06648c1b2ad3689b367b8c923fdec; it is not a separate TLSNotary MCP package. @allowly/mcp supports both evidence modes. Customer setup installs only the helper, not the hosted witnessing socket.

Create an authorization

Create one authorization for the subject and store its ID in your application:

const authorization = await allowly.authorizations.create({
  userId: "subject_abc123",
  policyId: "research_agent",
  expiresAt: "2026-12-31T00:00:00.000Z",
});

await saveAuthorizationId(authorization.authorizationId);

Use opaque internal subject IDs. Avoid putting raw names, emails, documents, or other sensitive data into receipt fields unless that data is intentionally part of the audit record.

Send JSON through a private SEAL webhook

Copy the private URL from the dashboard's SEAL page. The URL is the only credential this client sends; it does not use an ordinary API key.

import { SealWebhookClient } from "@allowly/sdk";

const webhook = new SealWebhookClient(process.env.ALLOWLY_SEAL_WEBHOOK_URL!);
let delivery = await webhook.send(rawJson, {
  idempotencyKey: eventId,
  type: "invoice",
  reference: "INV-1042",
  statement: "Approved for payment",
});
while (delivery.status === "received" || delivery.status === "signing") {
  await new Promise((resolve) => setTimeout(resolve, 1_000));
  delivery = await webhook.getDelivery(delivery.attemptId);
}
if (delivery.status !== "sealed") {
  throw new Error(delivery.errorCode ?? "SEAL delivery failed");
}
await saveEvidence(delivery.receipt, await webhook.getKeys());

The webhook processes your JSON to create a fingerprint; Allowly stores the fingerprint and signed receipt. Keep the original record in your workflow. Receipt details are sent in the three explicit Allowly-Seal-* headers. Their values must use printable ASCII, may contain interior spaces, and must not have leading or trailing whitespace. The client rejects invalid values instead of changing them. Direct API metadata still supports its existing Unicode values. When a signed receipt is present, the client returns its signed metadata and rejects a conflicting top-level delivery projection. Treat the full URL like a password and keep it out of logs, tickets, and source control. Regenerating or disabling it stops the old URL. Delivery associations and status remain available for 7 days; preserve signed receipts and keys under your own retention policy. With no idempotencyKey, retrying after a lost response can create another seal.

Seal a JSON record with local hashing

seal hashes strict raw JSON in your process, sends only its digest to Allowly, and waits for the full signed receipt. Generate and persist requestId in your workflow so a retry recovers the same seal:

import { randomUUID } from "node:crypto";

const requestId = randomUUID();
const sealed = await allowly.seal(rawJson, {
  requestId,
  metadata: { source: "invoice-workflow" },
});
await saveBesideRecord(sealed.receipt);

Use sealValue(parsedJson, ...) only when the original JSON text is no longer available. A parsed value cannot reveal duplicate object names or the original number spelling, so seal is the safer input boundary.

Verify later with the authenticated workspaceId response and keys fetched from Allowly through an authenticated or previously trusted source:

import { loadKeysFromJson, verifySealJson } from "@allowly/sdk";

const result = await verifySealJson(rawJson, sealed.receipt, loadKeysFromJson(keysDoc), {
  expectedWorkspaceId: sealed.workspaceId,
  trustedKeyFingerprints: configuredKeyFingerprints,
});
if (!result.signatureVerified || !result.recordMatches) {
  throw new Error(result.failureReason ?? "SEAL verification failed");
}

Verify a signed receipt

import { fetchKeysDoc, loadKeysFromJson, verifyReceipt } from "@allowly/sdk";

const receipt = await allowly.receipts.fetchSigned(receiptId);
const workspaceId = process.env.ALLOWLY_WORKSPACE_ID!;
const keys = loadKeysFromJson(await fetchKeysDoc(workspaceId));

await verifyReceipt(receipt, keys, { expectedWorkspaceId: workspaceId });

Take the expected workspace ID from trusted application configuration, not from the receipt being verified.

Documentation