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

@empyre/relay-sdk

v1.0.0

Published

Relay OAuth and identity for AI agents: Continue with Relay, PKCE, scoped tokens, and Agent ID + Agent Secret authentication.

Readme

@empyre/relay-sdk

OAuth & identity for AI agents — the official SDK for Relay by Empyre.

  • Relay Identity — mint an agent (any name, e.g. claude@relay) and get its Agent ID + Agent Secret in one step. That pair is the agent's entire credential: exchange it for short-lived tokens at any app that supports Relay.
  • Relay Platform — add Continue with Relay (OAuth 2.1 + PKCE) so AI agents authenticate into your app through a secure consent flow.

Zero runtime dependencies. Works in Node 18+, Deno, Bun, browsers, and edge runtimes.

0.4 security contract: authorizeUrl() requires high-entropy state and an S256 PKCE challenge. Callbacks must consume the stored state once and pass the matching verifier to exchangeCode().

Install

npm i @empyre/relay-sdk

The package name is @empyre/relay-sdk (scoped). npm i empyre@relay-sdk will fail with a 404 — that syntax asks npm for a package called empyre at a version tag called relay-sdk.

Quick start — give an agent an identity

Create an agent at relay.empyre.dev/identity/dashboard (any name, renameable any time). You get its Agent ID and Agent Secret right there — the secret is shown once (rotate any time). Pick a permission mode at creation:

  • ask_critical — default; normal actions run, critical ones email you for one-tap approval.
  • ask_everything — every token request waits for your email approval.
  • full_access — the agent acts without asking; everything is still audited and revocable.

These snippets are JavaScript, not shell. Save them to a file (e.g. agent.mjs) and run node agent.mjs — pasting them straight into a terminal gives zsh: parse error.

# terminal
export RELAY_AGENT_ID="paste-agent-id"
export RELAY_AGENT_SECRET="ras_paste-agent-secret"
npm i @empyre/relay-sdk
// agent.mjs — run with: node agent.mjs
import { RelayClient } from "@empyre/relay-sdk";

// Sign in by the app's DOMAIN — Relay resolves it to that app's client_id for
// you, so you never need the opaque relay_client_… id.
const relay = new RelayClient({ audience: "deelflow.dev" });

const result = await relay.authenticateAgent(
  process.env.RELAY_AGENT_ID,
  process.env.RELAY_AGENT_SECRET,
);
if ("status" in result && result.status === "pending_approval") {
  // The owner gets a one-tap approval email. Retry after approval.
  console.log(result.message);
} else {
  console.log(result.access_token); // Bearer token the app verifies via Relay
}

Which app am I signing in to? Pass its domain as audience (e.g. "deelflow.dev") and Relay looks up the client_id. If you already have the app's relay_client_… id, you can pass it as clientId instead — both work. To fetch the id yourself: await relay.resolveClient("deelflow.dev"), or GET https://api.empyre.dev/relay/oauth/clients/resolve?domain=deelflow.dev.

Quick start — Continue with Relay (PKCE)

# terminal — both credentials, server-side only
export RELAY_CLIENT_ID="relay_client_..."
export RELAY_CLIENT_SECRET="relay_secret_..."
// server-side JavaScript (e.g. relay.mjs) — not shell
import { RelayClient, createOAuthState, createPkcePair } from "@empyre/relay-sdk";

const relay = new RelayClient({
  clientId: process.env.RELAY_CLIENT_ID,
  clientSecret: process.env.RELAY_CLIENT_SECRET,
});

// 1. Send the user/agent to Relay to approve Relay sign-in/profile scopes.
const { verifier, challenge } = await createPkcePair();
const state = createOAuthState();
const url = relay.authorizeUrl({
  redirectUri: "https://yourapp.com/callback",
  scopes: ["openid", "profile"],
  state,
  codeChallenge: challenge,
});
session.relayOAuth = { state, verifier }; // server-side, bound to this browser
// → Redirect the browser to `url`.

// 2. On your callback, exchange the code for tokens.
const transaction = session.relayOAuth;
delete session.relayOAuth; // consume before validating so the callback cannot replay
if (!transaction || callbackState !== transaction.state) throw new Error("OAuth state mismatch");
const tokens = await relay.exchangeCode(code, transaction.verifier, "https://yourapp.com/callback");

// 3. Verify a token on each request.
const info = await relay.verifyToken(tokens.access_token);
if (!info.active) throw new Error("token revoked");

Account linking — "Connect Relay" for existing users

The same OAuth flow powers two buttons:

  • Continue with Relay — signup/login: no session yet; create or fetch the user keyed by the introspected sub.
  • Connect Relay — account linking: your user is already logged in (e.g. via Google). From your settings page, send them through authorizeUrl() with a state bound to their session; on the callback exchange the code, introspect the token, and attach info.sub to the existing account. Never create a second user during a link, and reject the link if that sub is already attached to a different account.
const tokens = await relay.exchangeCode(code, verifier, redirectUri);
const info = await relay.verifyToken(tokens.access_token);
await db.users.update(currentUser.id, { relay_identity_id: info.sub });

CLI

export RELAY_CLIENT_ID="relay_client_..."
export RELAY_TOKEN="paste-token-here"

npx @empyre/relay-sdk authorize-url --redirect https://yourapp.com/callback --scopes openid,profile
npx @empyre/relay-sdk verify "$RELAY_TOKEN"
npx @empyre/relay-sdk revoke "$RELAY_TOKEN"
npx @empyre/relay-sdk agent-login "$RELAY_AGENT_ID" "$RELAY_AGENT_SECRET"

Configure via env: RELAY_CLIENT_ID, RELAY_CLIENT_SECRET, RELAY_BASE_URL (default https://api.empyre.dev), and RELAY_AUTHORIZE_URL (default https://relay.empyre.dev/consent). For a local simulation, set the API and consent endpoints independently:

export RELAY_BASE_URL=http://127.0.0.1:8000
export RELAY_AUTHORIZE_URL=http://relay.localhost:4173/consent

API

RelayClient

| Method | Description | | --- | --- | | authorizeUrl(params) | Build the consent URL. Requires high-entropy state and an S256 codeChallenge. | | exchangeCode(code, verifier, redirectUri) | Authorization code → tokens. | | refresh(refreshToken) | Rotate an access token. | | authenticateAgent(id, secret, scopes) | Relay Identity client-credentials grant. Returns tokens or a typed pending_approval result. Target the app via clientId or audience (its domain). | | resolveClient(domain) | Resolve an app's domain → its public client_id. | | verifyToken(token) | Introspect (RFC 7662). | | revokeToken(token) | Revoke (RFC 7009). | | createOAuthState() | Generate a high-entropy, URL-safe OAuth state value. | | createPkcePair() | Generate a PKCE verifier + S256 challenge. |

The token endpoints target POST /relay/oauth/{token,introspect,revoke}. See the Relay docs for the current OAuth surface.

MIT © Empyre