New:Microsoft Teams Notifications Are Now Available in Socket.Learn more
Get Started

@keychains/server-sdk

Package Overview
Dependencies
Maintainers
1
Versions
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@keychains/server-sdk

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

latest
npmnpm
Version
0.0.2
Version published
Maintainers
1
Created
Source

@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:

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

Quickstart

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

const keychains = KeychainsApp.fromEnv();

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

MethodReturnsDescription
.forUser(userId)UserContextScope operations to a user
.forUser(id).withTask(name)TaskContextScope to a user + task
.forUser(id).withTask(name).fetch(url, opts?)ResponseWildcard token + proxied fetch
.forUser(id).withTask(name).getToken()TokenResultWildcard token for manual fetch
.forUser(id).withTask(name).getPermission(scopes?)PermissionScoped permission for pre-approval
.forUser(id).getTokenForTask(name)TokenResultShortcut for .withTask(name).getToken()
.forUser(id).getPermissionForTask(name, scopes)PermissionShortcut for .withTask(name).getPermission(scopes)

Lower-level methods

MethodDescription
.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

MethodReturnsDescription
.approvalUrl()string | undefinedURL for user to approve
.isApproved()booleanPoll if permission is active
.getToken(ttl?)TokenResultMint a token
.fetch(url, opts?)ResponseMint 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}`);
    }
  }
}
CodeDescription
authorization_requiredProxy request needs user approval (has approvalUrl)
missing_envRequired environment variables not set
not_registeredApp ID not set — run register or call setAppId()
unauthorizedInvalid or expired JWT
not_foundPermission or app not found
forbiddenPermission 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

VariableRequiredDescription
KEYCHAINS_DOMAINYesYour registered domain
KEYCHAINS_PRIVATE_KEYYesPEM-encoded private key
KEYCHAINS_KEY_IDYesKey ID (kid) from your JWKS
KEYCHAINS_APP_IDNoApp ID (auto-set by register script)
KEYCHAINS_SERVER_URLNoOverride server URL (default: https://keychains.dev)

License

Proprietary — Interagentic Inc.

Keywords

keychains

FAQs

Package last updated on 10 Mar 2026

Related posts