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

@keychains/server-sdk

v0.0.2

Published

Server SDK for Keychains.dev (BETA) — register trusted apps, create permissions, mint tokens, and make proxy calls via JWKS authentication

Readme

@keychains/server-sdk

BETA — This SDK is under active development. APIs may change.

Server SDK for Keychains.dev — give your server-side app secure access to user credentials (OAuth tokens, API keys) through the Keychains proxy.

Setup

npm install @keychains/server-sdk
npx @keychains/server-sdk register myapp.com

The register script will:

  1. Generate an RSA keypair and write jwks.json to the right location for your project
  2. Walk you through DNS verification
  3. Add KEYCHAINS_* env vars to your .env

Quickstart

import { KeychainsApp } from '@keychains/server-sdk';

const keychains = KeychainsApp.fromEnv();

Option 1: Approval at request time (recommended)

Everything is transparent until the proxy request — identical to how keychains curl works. A wildcard permission is created automatically, and the auth URL only surfaces when the user actually needs to connect a service.

// One-liner — fetch through the proxy:
try {
  const res = await keychains.forUser(userId).withTask('Project Albatros').fetch('https://api.github.com/user');
  const data = await res.json();
} catch (err) {
  if (err instanceof KeychainsAppError && err.approvalUrl) {
    // User hasn't connected GitHub yet — show them this URL
    console.log(`Please authorize: ${err.approvalUrl}`);
  }
}

Or step by step:

// Get a token (creates a wildcard permission if needed)
const { token } = await keychains.forUser(userId).withTask('Project Albatros').getToken();

// Use the token for one or more proxy requests
const res = await keychains.fetch('https://api.github.com/user', { permissionToken: token });

When to use: Quick prototyping, AI agents, cases where you don't know upfront which services the user will need.

Option 2: Pre-approving requests

Create a scoped permission with specific scopes. The user approves upfront, then all subsequent requests go through without interruption.

const scopes = ['gmail.com::oauth2::read', 'linear.app::oauth2::read'];
const permission = await keychains.forUser(userId).withTask('Project Albatros').getPermission(scopes);

// Show approval URL to the user
console.log('Please approve at: ' + permission.approvalUrl());

// Check if approved (poll or after redirect)
if (await permission.isApproved()) {
  const res = await permission.fetch('https://gmail.googleapis.com/gmail/v1/users/me/messages');
  // or: const { token } = await permission.getToken();
}

When to use: Apps with explicit user consent UIs, when you know exactly which services are needed.

API Reference

KeychainsApp

// From environment variables (recommended)
const keychains = KeychainsApp.fromEnv();

// Manual configuration
const keychains = new KeychainsApp({
  domain: 'myapp.com',
  privateKey: process.env.KEYCHAINS_PRIVATE_KEY!,
  keyId: 'key-1',
  serverUrl: 'https://keychains.dev', // optional
});
keychains.setAppId(process.env.KEYCHAINS_APP_ID!);

Fluent API

| Method | Returns | Description | |--------|---------|-------------| | .forUser(userId) | UserContext | Scope operations to a user | | .forUser(id).withTask(name) | TaskContext | Scope to a user + task | | .forUser(id).withTask(name).fetch(url, opts?) | Response | Wildcard token + proxied fetch | | .forUser(id).withTask(name).getToken() | TokenResult | Wildcard token for manual fetch | | .forUser(id).withTask(name).getPermission(scopes?) | Permission | Scoped permission for pre-approval | | .forUser(id).getTokenForTask(name) | TokenResult | Shortcut for .withTask(name).getToken() | | .forUser(id).getPermissionForTask(name, scopes) | Permission | Shortcut for .withTask(name).getPermission(scopes) |

Lower-level methods

| Method | Description | |--------|-------------| | .createPermission(opts) | Create a permission request | | .listPermissions(appUserId) | List permissions for a user | | .getPermissionStatus(id, appUserId) | Check permission status | | .mintToken(permissionId, appUserId, ttl?) | Mint a short-lived token | | .fetch(url, { permissionToken }) | Proxied fetch with a token | | .register({ verify? }) | DNS registration flow | | .delegate(permissionId, appUserId, opts) | Delegate access to VMs |

Permission

| Method | Returns | Description | |--------|---------|-------------| | .approvalUrl() | string \| undefined | URL for user to approve | | .isApproved() | boolean | Poll if permission is active | | .getToken(ttl?) | TokenResult | Mint a token | | .fetch(url, opts?) | Response | Mint token + proxied fetch |

Error Handling

import { KeychainsAppError } from '@keychains/server-sdk';

try {
  const res = await keychains.forUser(userId).withTask('My Task').fetch(url);
} catch (err) {
  if (err instanceof KeychainsAppError) {
    if (err.approvalUrl) {
      // User needs to authorize — redirect or show the URL
      console.log(`Authorize: ${err.approvalUrl}`);
    } else {
      console.error(`${err.code}: ${err.message}`);
    }
  }
}

| Code | Description | |------|-------------| | authorization_required | Proxy request needs user approval (has approvalUrl) | | missing_env | Required environment variables not set | | not_registered | App ID not set — run register or call setAppId() | | unauthorized | Invalid or expired JWT | | not_found | Permission or app not found | | forbidden | Permission revoked or app revoked |

Registration Flow

1. Run the register script

npx @keychains/server-sdk register myapp.com

This generates a keypair, writes jwks.json, and walks you through DNS verification.

2. Serve the JWKS endpoint

Your domain must serve the JWKS file at:

https://myapp.com/.well-known/keychains.dev/jwks.json

For Next.js / static sites, the register script places it in public/ by default.

3. DNS verification

Add a TXT record as instructed by the script. Once verified, your app is registered and the env vars are written to .env.

Delegation

Delegate access to VMs or sub-agents:

const delegate = await keychains.delegate(permissionId, userId, {
  publicKey: vmPublicKey,
  scopes: ['github.com::oauth2::repo'],
});

// Or use bootstrap token for self-registration
const delegate = await keychains.delegate(permissionId, userId, {
  useBootstrapToken: true,
  scopes: ['github.com::oauth2::repo'],
});
// delegate.bootstrapToken → send to VM

Security Model

  • JWKS Authentication: Your app signs JWTs with a private key; Keychains verifies via your published JWKS
  • Domain Verification: DNS TXT record proves domain ownership
  • User Sovereignty: Users authenticate on keychains.dev and explicitly authorize access. Compromising your signing key does NOT grant access to user credentials.
  • Key Rotation: Add new keys to JWKS before removing old ones. 24-hour grace period on key disappearance.

Environment Variables

| Variable | Required | Description | |----------|----------|-------------| | KEYCHAINS_DOMAIN | Yes | Your registered domain | | KEYCHAINS_PRIVATE_KEY | Yes | PEM-encoded private key | | KEYCHAINS_KEY_ID | Yes | Key ID (kid) from your JWKS | | KEYCHAINS_APP_ID | No | App ID (auto-set by register script) | | KEYCHAINS_SERVER_URL | No | Override server URL (default: https://keychains.dev) |

License

Proprietary — Interagentic Inc.