Keyring Docs

Quickstart

Connect your app to Keyring with the TypeScript SDK in minutes.

What you need

  • A Keyring workspace with at least one role and one action defined in the console.
  • Your Keyring app origin as baseUrl — https://usekeyring.dev for the hosted app, or your self-hosted origin.
  • Two API keys from Settings → API keys: a secret key for the server, a publishable key for the browser.
  • Your own auth provider (Clerk / Supabase / WorkOS / …) — Keyring does not log your users in, it authorizes them.

Install

bun add @usekeyring/sdk
npm i @usekeyring/sdk
pnpm add @usekeyring/sdk

Server: grant + check

import { Keyring } from "@usekeyring/sdk";

const keyring = new Keyring({
  apiKey: process.env.KEYRING_SECRET_KEY!, // kr_sk_live_…
  baseUrl: process.env.KEYRING_URL!, // https://usekeyring.dev (or your self-hosted origin)
});

// Give the user a role (your IDs, your display names)
await keyring.grantRole({
  role: "viewer",
  subject: user.id,
  displayName: user.email,
});

// Temporary access — auto-expires, no revoke needed
await keyring.grantRole({
  role: "repo-creator",
  subject: user.id,
  ttlSeconds: 300,
});

// Enforce
const { allowed } = await keyring.check(user.id, "invoices.refund");
if (!allowed) throw new Error("Forbidden");

The secret key needs the check scope to check, grants.write to grant/revoke, and subject_tokens.write to mint browser tokens. To bootstrap roles/actions from code instead of the dashboard, also select roles.write and actions.write — then createPermission() each action first, createRole({ permissions }), and finally grantRole().

Browser: UX-only check

After your own login, mint a subject token on your backend and hand it to the browser (prefer an httpOnly cookie):

// server — after your login
const { token, expiresAt } = await keyring.createSubjectToken({
  subject: user.id,
  ttlSeconds: 3600,
});
// browser — show/hide UI only
import { Keyring } from "@usekeyring/sdk";

const keyring = new Keyring({
  apiKey: process.env.NEXT_PUBLIC_KEYRING_PUBLISHABLE_KEY!, // kr_pk_live_…
  baseUrl: process.env.NEXT_PUBLIC_KEYRING_URL!,
  subjectToken: () => readCookie("keyring_subject"),
});

const { allowed } = await keyring.check("invoices.refund");

Or pass the token per call: keyring.check("invoices.refund", { subjectToken }). The subject comes from the JWT — the client cannot forge another subject id.

Enforce on the server

Browser checks never grant access. Re-check with the secret key before any read/write.

Next steps