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

@lucairn/sdk

v1.1.1

Published

Lucairn — privacy-preserving AI gateway client for TypeScript

Readme

@lucairn/sdk

Status

The Lucairn client ships construction-time apiKey validation, baseUrl normalization with scheme guards, per-call timeout composition, the client.messages() proxy endpoint, client.getCertificate() and client.getCertificateSummary() fetch helpers, client.listAuditEvents() audit-export helper, client.verifyCertificate() for witness-signature verification, and five typed error classes. See CHANGELOG for the rebrand from @dsaveil/theveil.

Node-first. Browser use requires gateway CORS configuration (not covered by this release).

Install

npm install @lucairn/sdk

Construct a client

import { Lucairn } from '@lucairn/sdk';

const client = new Lucairn({ apiKey: process.env.LUCAIRN_API_KEY! });

The default baseUrl is https://gateway.lucairn.eu (the hosted Lucairn gateway). Enterprise self-host deployments must pass baseUrl explicitly.

Send a request through the privacy-preserving proxy

const response = await client.messages({
  prompt_template: 'Hello {name}',
  context: { name: 'Example Person' },
  model: 'claude-sonnet-4-6',
  max_tokens: 1024,
});

Privacy receipts: free vs Pro tier paths

Every messages() call generates a privacy receipt witnessed by the gateway. Two surfaces exist for that receipt, and which one your code should consume depends on your tier:

  • getCertificateSummary(requestId) — returns a human-readable HTML summary (DPO-friendly). Available on every tier including Developer (free).
  • getCertificate(requestId) + verifyCertificate(cert, keys) — fetches the raw JSON certificate and verifies the witness's Ed25519 signature over its canonical signed subset. Pro tier and above.

If a Developer-tier key calls getCertificate(), the gateway returns HTTP 403 with {"error":"tier_insufficient","hint":"Contact sales to upgrade."}, surfaced by the SDK as LucairnHttpError with status === 403.

Developer tier (free) — render the HTML summary

import { Lucairn, LucairnHttpError } from '@lucairn/sdk';

const client = new Lucairn({ apiKey: process.env.LUCAIRN_API_KEY! });

const response = await client.messages({
  prompt_template: 'Hello {name}',
  context: { name: 'Example Person' },
  model: 'claude-sonnet-4-6',
  max_tokens: 1024,
});

// `response.request_id` is populated on every tier (Developer / Pro / Enterprise).
// Pro/Enterprise responses additionally expose `response.veil.summary_url` if you
// want the summary URL directly without an extra fetch.
const requestId = response.request_id;

let summaryHtml: string;
try {
  summaryHtml = await client.getCertificateSummary(requestId);
} catch (err) {
  if (err instanceof LucairnHttpError && err.status === 202) {
    // Pending; the body is the gateway's "pending" HTML view.
    return;
  }
  throw err;
}
// Display summaryHtml in a sandboxed iframe or save for the DPO.

Pro tier and above — fetch + verify the JSON certificate

On Pro and Enterprise tier responses the gateway adds a veil block with summary_url and certificate_url. Pro and Enterprise keys can also fetch the raw certificate and verify the witness Ed25519 signature locally for a programmatic audit trail.

client.getCertificate(requestId) returns the raw VeilCertificate. Pair it with client.verifyCertificate(cert, keys) to prove the witness's Ed25519 signature over the certificate's canonical JSON signed subset. The two calls are deliberately separate: the SDK never fetches or bakes in witness keys, and the caller supplies the witness identity (expected witnessKeyId label and raw 32-byte witnessPublicKey) out of band.

External RFC 3161 timestamp verification and Sigstore Rekor transparency- log verification are not performed by this release. They land in a follow-up release pending gateway work. Until then, anchorStatus and overallVerdict are surfaced as pass-through metadata for observability, not independently verified.

Quota behaviour for certificate reads is a gateway-side concern; see the gateway documentation for current tier limits.

import {
  Lucairn,
  LucairnCertificateError,
  LucairnHttpError,
} from '@lucairn/sdk';

const client = new Lucairn({ apiKey: process.env.LUCAIRN_API_KEY! });

let cert;
try {
  cert = await client.getCertificate(requestId); // 200 on Pro/Enterprise; 403 on Developer (free)
} catch (err) {
  if (err instanceof LucairnHttpError && err.status === 202) {
    // Certificate not yet assembled — retry after the indicated delay.
    const body = err.body as { retry_after_seconds?: number };
    const retryAfter = body.retry_after_seconds ?? 30;
    console.log(`pending; retry in ${retryAfter}s`);
    return;
  }
  if (err instanceof LucairnHttpError && err.status === 403) {
    // Developer (free) tier — use getCertificateSummary() instead.
    console.log('certificate JSON requires Pro tier or above');
    return;
  }
  throw err;
}

try {
  const result = await client.verifyCertificate(cert, {
    witnessKeyId: 'witness_v1',
    witnessPublicKey: process.env.VEIL_WITNESS_PUBLIC_KEY_BASE64!,
  });
  // result.witnessAssertedIssuedAtIso preserves full nanosecond precision;
  // result.witnessAssertedIssuedAt is the millisecond-truncated Date form.
  console.log('verified', result.certificateId, result.witnessAssertedIssuedAtIso);
} catch (err) {
  if (err instanceof LucairnCertificateError) {
    switch (err.reason) {
      case 'malformed':
      case 'unsupported_protocol_version':
      case 'witness_mismatch':
      case 'witness_signature_missing':
      case 'invalid_signature':
        console.error(`verify failed (${err.reason}):`, err.message);
        break;
    }
    return;
  }
  throw err;
}

New helpers (1.0)

getCertificateSummary(requestId, options?): Promise<string>

Returns a DPO-friendly HTML summary of a Veil Certificate. Available on every tier including Developer (free). The endpoint returns text/html; the helper returns the raw HTML string. When the certificate is not yet assembled, the gateway responds 202 Accepted with a pending-summary HTML body, surfaced as LucairnHttpError({ status: 202, body: '<html>...</html>' }).

let summaryHtml: string;
try {
  summaryHtml = await client.getCertificateSummary(requestId);
} catch (err) {
  if (err instanceof LucairnHttpError && err.status === 202) {
    // Pending; the body is the gateway's "pending" HTML view.
    return;
  }
  throw err;
}

getClientId(cert): string | null

Reads the optional client_id (org_id metadata) from a Veil Certificate. Returns the value, or null when missing or null on the wire.

import { getClientId } from '@lucairn/sdk';

const orgId = getClientId(cert);

client_id is unsigned metadata for client-side correlation. For tamper-evident proof of the issuing org, walk the bridge claim's canonical_payload (which IS in the witness signable map).

listAuditEvents(opts?): Promise<AuditExportResponse>

Lists the calling customer's recent audit events. Tier-gated server-side (403 tier_insufficient if the customer's tier doesn't include audit export). days defaults to 30 and is capped at 90 by the gateway.

const result = await client.listAuditEvents({ days: 7, eventType: 'request_recorded' });
console.log(`${result.total_events} events from ${result.source}`);
for (const ev of result.events) {
  console.log(ev.timestamp, ev.event_type, ev.request_id);
}

Migrating from @dsaveil/theveil

The pre-1.0 package name @dsaveil/theveil and class name TheVeil are re-exported as legacy aliases for one minor-version cycle so existing imports keep compiling:

// Both of these import the same constructor.
import { TheVeil } from '@lucairn/sdk';   // legacy alias
import { Lucairn } from '@lucairn/sdk';   // new name

The legacy aliases (TheVeil, TheVeilError, TheVeilConfigError, TheVeilHttpError, TheVeilTimeoutError, TheVeilCertificateError, TheVeilConfig) will be removed in the next minor bump. Migrate before upgrading past 1.x.0.

Back to root

See the monorepo README for the full SDK index.