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

@dexterai/connect

v0.26.4

Published

Sign in with Dexter — passkey connector. Composes @dexterai/vault.

Readme


What this is

A <SignInWithDexter/> button runs a discoverable passkey ceremony. The user taps their face, and your app gets back a session plus their Dexter Wallet: address, live USD balance, and the rails for an agent to spend from it under on-chain limits. The user holds their own keys; only their passkey moves funds, enforced on-chain. Composes @dexterai/vault.

Four entry points cover the whole flow:

| Entry point | Runs in | What it gives you | |---|---|---| | @dexterai/connect | browser | framework-free core: passkeyLogin, createWallet, continueWithDexter, the wallet store, agent-spend controls | | @dexterai/connect/react | browser | <SignInWithDexter/>, the branded wallet kit, hooks | | @dexterai/connect/server | Node 18+, Workers, Vercel edge | verifyDexterSession: offline session verification | | @dexterai/connect/worldid | browser | <VerifyPersonhood/> World ID proof-of-personhood button |

Install

npm install @dexterai/connect @dexterai/vault react

@solana/web3.js and @worldcoin/idkit are optional peers: the first for passkey and agent-spend signing, the second only if you use the World ID button.

Quick start

import { SignInWithDexter } from '@dexterai/connect/react';

function Header() {
  return (
    <SignInWithDexter
      onSuccess={({ session, vault }) => {
        // session = auth tokens (camelCase); vault = the Dexter Wallet
        seatYourSession(session);
      }}
    />
  );
}

Signed out, it renders a Sign in with Dexter button. Connected, it becomes the wallet chip: address plus "$X.XX available."

Works on any website

The ceremony is not limited to dexter.cash. On a foreign origin, passkeyLogin opens a hosted popup on dexter.cash/connect, runs the ceremony there, and posts the result back to your page. A browser-stamped hello/ack binds the exact popup window, hosted origin, caller origin, request nonce, and requested operation; the caller-origin may be any HTTPS website and is not allowlisted. The default transport: 'auto' picks the right mode; 'popup' and 'inline' force it.

The connected passkeySigner.signOperation(rawOperation) follows the same cross-origin rule. On an unrelated website it opens the pinned Dexter consent window and sends the raw operation plus the selected vault identity only after the exact hello/ack handshake. Those bytes are structured-cloned in a sign-request; they are never put in the URL. The Dexter window displays the requesting origin and operation-specific consent before returning the on-chain-ready assertion bytes.

WebAuthn challenge and verification calls are pinned to https://api.dexter.cash. The legacy apiBase option remains source-compatible only for that exact value (an optional trailing slash is normalized); any other server fails before a popup, fetch, or authenticator call. apiBase is never placed in the hosted popup URL. The legacy connectHost option is likewise compatibility-only and may name only https://dexter.cash/connect; this pins the credential-handling window without restricting the embedding website.

import { passkeyLogin } from '@dexterai/connect';

const { session, vault } = await passkeyLogin({ transport: 'auto' });

By default, a successful sign-in immediately makes its wallet active in the browser wallet store, preserving existing behavior. A flow that must finish a separate account-level approval can instead keep the result provisional:

import { passkeyLogin, setActiveHandle } from '@dexterai/connect';

const result = await passkeyLogin({
  transport: 'auto',
  walletStore: 'provisional',
});

// Only after your approval has bound this exact wallet to the account:
if (result.vault) {
  setActiveHandle(
    result.vault.userHandle,
    result.vault.walletLabel ?? undefined,
    result.vault.credentialId,
  );
}

provisional suppresses both active-handle and wallet-roster writes on the hosted Dexter origin and the calling origin. It does not weaken passkey verification or persist a second identity. recoverWallet, createWallet, and every terminal sign-in/create branch of continueWithDexter accept the same option. A provisional creation still completes the passkey and Vault ceremony and returns the full CreateWalletResult; explicitly call setActiveHandle(result.handle, result.label ?? undefined, result.credentialId) only after the separate approval succeeds. The only supported modes are exact commit and provisional; omitted means commit.

Verify the session on your server

import { createDexterClient } from '@dexterai/connect/server';

const dexter = createDexterClient(); // parameterized on (iss, jwksUrl); defaults to Dexter's issuer

export async function handler(req: Request) {
  const auth = await dexter.authenticateRequest(req);
  if (!auth.isSignedIn) return new Response('unauthorized', { status: 401 });
  auth.sub;          // stable user id
  auth.vaultAddress; // the Dexter Wallet address, from the signed dexter claim
  auth.claims;       // full verified JWT payload
}

Verification is a local ES256 signature check against a cached JWKS. The first call fetches the key set; every later call is pure local crypto with zero network (measured at ~0.6ms). The algorithm list is pinned to ES256, and issuer plus audience are always checked. verifyDexterSession(token, options) does the same for a bare token string, and jwtKey accepts a public JWK for fully networkless deployments.

Hook (full control)

import { useSignInWithDexter } from '@dexterai/connect/react';

const c = useSignInWithDexter();
await c.signIn();        // run the passkey ceremony
c.status;                // idle -> pending -> done -> error
c.vaultAddress;          // the Dexter Wallet address (base58)
c.usdcBalance;           // USD available (via Dexter's RPC), or null
c.disconnect();

| Field | What it is | |---|---| | signIn() / disconnect() | run the passkey ceremony / clear state | | status / isVaultConnected | idle->pending->done->error / connected flag | | session | auth session tokens (camelCase) | | vaultAddress / vaultPda | the Dexter Wallet address / PDA | | usdcBalance / refreshBalance() | USD available, best-effort via Dexter's RPC | | vault / credentialId / error | raw vault payload / credential id / typed error |

The wallet kit

Branded, presentational pieces that share one implementation across every Dexter surface, themed with --dx-* CSS variables: DexterButton (and DexterMark) for any action that should look like Dexter, DexterWalletChip as the header trigger, DexterWalletMenu for manage / save / start-fresh, and the useDexterWallet + useIdentity hooks to drive them.

Agent spend

The control surface for letting an agent spend from the connected wallet:

import {
  assembleAgentSpendStatus, // honest two-mode status read
  enableAgentSpend,         // the on switch
  revokeAgentSpend,         // the off switch
  createPasskeySigner,      // @dexterai/vault guest signer for x402 / tab flows
} from '@dexterai/connect';

The verbs are framework-free and take apiOrigin as a parameter; the SDK reads no environment variables.

World ID

import { VerifyPersonhood } from '@dexterai/connect/worldid';

<VerifyPersonhood onSuccess={(proof) => sendToYourVerifier(proof)} />

useVerifyPersonhood is the headless version. Requires the optional @worldcoin/idkit peer.

Wallet-only sign-in (no account session)

recoverWallet re-points a browser at an existing Dexter Wallet — the wallet IS the sign-in; nothing else is minted. Use it when your surface treats the wallet as the identity (the dexter.cash header does exactly this):

import { recoverWallet } from '@dexterai/connect';

const outcome = await recoverWallet({ preferImmediate: true }); // fire on TAP, never on mount
if (outcome.ok) {
  // outcome.vault.swigAddress, outcome.vault.walletLabel — the store + every
  // useIdentity/useDexterWallet surface is already updated.
} else if (outcome.reason === 'no_credential') {
  // this device has no wallet passkey → offer your create flow
} else if (outcome.reason === 'cancelled') {
  // the user dismissed the sheet → stay silent
}

It returns a discriminated outcome instead of throwing: user cancel is a normal result in WebAuthn, not an exception. preferImmediate uses Chrome 149+'s immediate UI mode to fast-fail instantly when the device holds no passkey (no empty account-picker sheet); everywhere else it falls back to the normal modal. Works from any website — off dexter.cash the ceremony runs in the hosted popup automatically.

React: <SignInWithDexter mode="recover" preferImmediate onRecovered={…} /> (after a successful recover the element renders null — show identity with DexterWalletChip over useIdentity), or useSignInWithDexter().recover().

Wallet lifecycle

  • createWallet mints a brand-new named passkey + vault; passkeyLogin signs an existing one in; continueWithDexter resumes a known wallet; recoverWallet restores the wallet with no account session.
  • The wallet store (getActiveHandle, listWallets, switchWallet, ejectActiveWallet, forgetWallet, subscribeWallet) is the canonical owner of the active-wallet handle. Read and write through it rather than touching localStorage.
  • The WebAuthn Signal API helpers (renamePasskey, prunePasskey, syncAcceptedPasskeys, passkeySignalSupport) keep the OS keychain in sync where the browser supports it.
  • resolveIdentity combines the wallet handle with whatever account token you pass in; ceremonyPhaseLabel gives shared copy for connecting-step UI; createAnonServerPolicy builds the anonymous server policy for the signer.

Peer dependencies

| Peer | Required | Why | |---|---|---| | react >=18 | yes | the /react and /worldid surfaces | | @dexterai/vault ^0.43 | yes | hardened signer envelope validation + agent-spend message builders | | @solana/web3.js | optional | passkey and agent-spend signing paths | | @worldcoin/idkit | optional | only for /worldid |

The /server entry has none of these peers; it depends only on jose.

License

MIT