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

Published

Connect apps and agents to a self-custodial Dexter Wallet with passkey identity, governed authority, hosted signing, and verifiable sessions.

Readme

One Dexter identity

Each signed-in person has one active Dexter identity and one canonical Dexter Wallet. A full connection returns the Wallet and account session from the same passkey ceremony. Connect asks the Dexter API to verify that exact pair before the app receives account-owned data or actions.

The browser may remember earlier Wallet passkeys for recovery. That roster stays inside Connect. The product action is Use another Dexter Wallet, which runs a fresh passkey ceremony and replaces the active identity only after verification.

Agent authority always requires a separate owner grant. Creating or connecting a Wallet grants no agent spending authority.

Interrupted wallet setup

createWallet() retains the original passkey when setup is interrupted. Calling it again resumes that wallet, including after a page reload, and may ask the owner to confirm the saved passkey. The pending wallet becomes active after initialization succeeds. Its original name and authored allowance stay with it.

If enrollment never reached the server and its challenge expires, a fresh passkey check can confirm that the saved credential has no server record. Connect then clears that pending setup and returns enrollment_not_saved. The next explicit Create action starts a new wallet. Network errors, generic 404 responses, and server failures retain the pending setup for another retry.

On another device, recoverWallet() can finish an enrolled passkey's missing wallet initialization after verifying that passkey. Recovery starts with zero agent spending allowance. Full sign-in also restores a missing wallet record while preserving the credential's existing account binding.

Install

npm install @dexterai/connect @dexterai/vault react

@solana/web3.js is optional and supports signing paths. @worldcoin/idkit is optional and supports the World ID entry point.

Entry points

| Import | Purpose | |---|---| | @dexterai/connect | Passkey ceremonies, hosted signing, lifecycle operations, and agent authority | | @dexterai/connect/react | The canonical React control, hooks, and Dexter UI primitives | | @dexterai/connect/server | Local verification of Dexter account sessions | | @dexterai/connect/worldid | Optional World ID proof-of-personhood control |

Account panel

DexterAccountControl provides Dexter's account menu: balance, Send and Receive, activity, connected apps, account switching, and connection settings. It uses the same useDexterConnection instance as the app. The panel includes its brand marks, styles, and motion; it inherits the host font and respects reduced motion.

import { DexterAccountControl, type UseDexterConnection } from '@dexterai/connect/react';

export function AccountEntry({ connection }: { connection: UseDexterConnection }) {
  return <DexterAccountControl connection={connection} />;
}

The panel reads the selected account's portfolio while open. Partial pricing is labeled Priced assets. A failed read displays Balance unavailable; switching accounts clears the previous balance immediately. Wallet holdings are bound to receiveAddress, the asset address. walletAddress identifies the Swig control account and must not be used to match a portfolio.

The account name opens its wallet. A compact control beneath it copies the selected receive address and confirms success after the clipboard accepts it. Both remain available when an app supplies its own renderSummary.

Destinations open Dexter by default and carry the selected account. Dexter asks for confirmation if a different account is active there. connection.connect({ userHandle }) targets that account's saved credential when available and checks the returned account before changing sessions. A device without that saved credential uses its passkey picker. Set navigation to use local app routes, or set an entry to false to hide it. items adds app-specific destinations with requires: 'account' or requires: 'wallet'; only verified access exposes them. The destination itself must enforce its own authorization.

<DexterAccountControl
  connection={connection}
  navigation={{
    money: { href: '/money' },
    activity: { href: '/activity' },
    profile: false,
  }}
  items={[{ id: 'seller', label: 'Your servers', href: '/servers', requires: 'account' }]}
/>

open and onOpenChange optionally coordinate the panel with site navigation. LinkComponent accepts an app router's anchor adapter. profile supplies a name and email, shown only after account access is verified. For an existing portfolio reader, renderSummary can render DexterAccountSummary with its bound result, avoid a second read, and keep the shared presentation. --dx-account-font sets the panel font; styleNonce supports a site's inline-style policy.

Device removal belongs in account management and is absent from this menu.

Canonical React integration

useDexterConnection is the controlled integration for a product header, account menu, or connection screen. The host owns its account-session storage. Connect owns the Wallet ceremony, pair verification, and display permissions.

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

function DexterControl({ loading, session }: Props) {
  const dexter = useDexterConnection({
    intent: 'wallet',
    accountSession: loading
      ? { status: 'loading' }
      : session
        ? {
            status: 'authenticated',
            accessToken: session.access_token,
          }
        : { status: 'signed_out' },
    installAccountSession: async (next) => {
      await auth.setSession({
        access_token: next.accessToken,
        refresh_token: next.refreshToken,
      });
    },
    clearAccountSession: async () => {
      await auth.signOut();
    },
  });

  if (dexter.model.stage === 'checking') return <Spinner />;
  if (dexter.model.stage === 'connect') {
    return <button onClick={() => void dexter.connect()}>Connect Dexter</button>;
  }
  if (dexter.model.stage === 'repair') {
    return <button onClick={() => void dexter.connect()}>Reconnect Dexter</button>;
  }
  if (dexter.model.stage === 'offline' || dexter.model.stage === 'error') {
    return <button onClick={() => void dexter.retry()}>Try again</button>;
  }

  return <WalletMenu dexter={dexter} />;
}

The host callbacks are part of the identity transition:

  • installAccountSession receives the session from the verified ceremony. The callback should resolve only after the host store publishes that exact access token.
  • clearAccountSession removes that host session during Disconnect Dexter.
  • accountSession maps the host store back into loading, signed_out, or authenticated with an access token.

If account installation fails, Connect restores the previous active Wallet. The new identity stays hidden until the host session is installed.

Integration intents

Choose one intent for each control.

| Intent | Capabilities requested | Ready state | |---|---|---| | identity | identity | Exact Wallet/account pair is bound | | wallet | identity, wallet.read, wallet.use | Owner Wallet is present; account content still requires bound | | agent | identity, wallet.read, agent.authority | Exact pair is bound; authority is granted in a separate owner flow |

wallet.use means the owner can use their own Wallet in the current app. agent.authority means the app may offer a governed grant for an agent. The two permissions are separate.

Stage and permission model

Render from dexter.model, rather than inferring connection from a cached Wallet or account token.

| Stage | Product response | |---|---| | connect | Offer the configured Dexter connection action | | checking | Keep private content closed while the exact pair is verified | | ready | Render only the fields enabled in model.permissions | | repair | Offer the fresh passkey reconnect path | | offline | Keep private Wallet and account content closed; offer retry | | error | Show a concise error and offer retry |

The permission fields are:

| Field | Meaning | |---|---| | walletIdentityVisible | The active Wallet identity may be shown | | walletDataVisible | Wallet balances and related data may be shown | | ownerWalletUseEnabled | The owner may use this Wallet in the current app | | accountContentVisible | The API verified bound; account-owned content may be shown | | agentAuthorityVisible | A bound agent integration may present agent-authority controls |

Only bound unlocks account profile, roles, credit, servers, or other account-owned content. A conflict enters the repair stage with private account access closed.

Lifecycle actions

The canonical control presents three identity actions:

| Action | Effect | |---|---| | Use another Dexter Wallet | Runs a fresh ceremony and keeps the replacement hidden until the API verifies the pair and the host installs the matching account session | | Disconnect Dexter | Clears the host account session and active Wallet; the passkey remains on the device for a later sign-in | | Remove from this device | Requires explicit confirmation, removes the local Wallet record, and asks the browser to remove its passkey |

Connect keeps the browser Wallet roster, raw switching methods, and partial Wallet/account disconnects inside its recovery plumbing.

Framework-neutral transition

Apps without React can use the same full transition directly:

import {
  connectDexterIdentity,
  disconnectDexterIdentity,
} from '@dexterai/connect';

const result = await connectDexterIdentity({
  transport: 'auto',
  installAccountSession: (session) => auth.setSession(session),
});

await disconnectDexterIdentity({
  clearAccountSession: () => auth.signOut(),
});

connectDexterIdentity keeps the passkey result provisional, verifies its exact Wallet/account relation, then publishes the Wallet and asks the host to install the matching account session.

Hosted ceremonies

On another HTTPS origin, Connect opens the ceremony on dexter.cash/connect and returns the result to the calling app. The browser handshake binds the popup, hosted origin, caller origin, nonce, and requested operation. The hosted window also handles consent for Wallet signing.

WebAuthn challenge and verification calls use https://api.dexter.cash. The default transport: 'auto' selects inline or popup transport. Integrators can force popup or inline when their environment requires it.

The first-party page uses the guarded hosted entry instead of importing raw login or Wallet-store functions:

import { runHostedCeremony } from '@dexterai/connect/hosted';

const result = await runHostedCeremony({
  operation: 'continue',
  walletStore: 'provisional',
});

This entry runs only on https://dexter.cash. It pins the inline transport and Dexter API while preserving provisional Wallet results for the opener. The root package continues to expose the complete identity transition rather than its private login and browser-roster steps.

Repair a missing Vault record

Connect 0.31.0 includes recoverMissingVaultForAccount. Update older installations to use this recovery helper alongside the account panel.

Use this only after the API reports vault_not_found for an existing account. The account holder must sign in to that same account again before starting the repair.

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

const result = await recoverMissingVaultForAccount({
  accountAccessToken: session.access_token,
});

result.account.handle; // the X account confirmed by the API
result.vault.vaultPda;  // the Vault attached to that account

The passkey prompt runs in the pinned dexter.cash window. The account token stays in the calling app. A successful repair attaches the missing Vault to the account already signed in; it does not create an account, mint a new session, or change the browser's active Wallet.

Server verification

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

const dexter = createDexterClient();

export async function handler(req: Request) {
  const auth = await dexter.authenticateRequest(req);
  if (!auth.isSignedIn) return new Response('unauthorized', { status: 401 });
  if (!auth.userHandle) {
    return new Response('wallet identity required', { status: 403 });
  }

  auth.userHandle;   // stable Dexter Wallet principal
  auth.vaultAddress; // Wallet capability
  auth.sub;          // account subject
  auth.claims;       // verified JWT payload
}

The server entry verifies ES256 signatures against Dexter's JWKS and checks the issuer and audience. verifyDexterSession verifies a token directly.

Wallet creation and recovery

createWallet creates an owner Wallet and passkey. Agent authority remains a later, explicit owner action. recoverWallet runs a fresh Wallet ceremony for a device that already holds a Dexter passkey. Product integrations should route full account connection through useDexterConnection or connectDexterIdentity, so the account and Wallet are verified together.

Agent authority

Agent integrations build, present, and stage a separate owner grant. The grant can include x402 payment rules or other governed actions. It is bound to the selected Wallet and remains revocable.

Key helpers include buildAgentAuthorityRequest, beginAgentAuthority, stageAgentAuthority, and readAgentAuthority. The bounded x402 helpers provide the equivalent hosted setup path for x402-only grants.

World ID

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

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

useVerifyPersonhood is the headless form. This entry requires the optional @worldcoin/idkit peer.

Peer dependencies

| Peer | Required | Purpose | |---|---|---| | react >=18 | yes | React and World ID entry points | | @dexterai/vault ^0.43 || ^0.44 | yes | Wallet signer and governed authority messages | | @solana/web3.js | optional | Passkey and agent signing paths | | @worldcoin/idkit | optional | World ID controls |

The server entry depends only on jose.

License

MIT

Private wallet activity

Use the retained proof from the existing passkey ceremony for wallet activity and owner-operation status. The proof identifies the wallet; the server still verifies an exact operation assertion before any financial action. The request helper keeps proof material inside the SDK and only sends it to the canonical Dexter API.

import { fetchWithWalletIdentity, walletProofSessionStore } from '@dexterai/connect';

const activeWallet = { userHandle, vaultPda };
const wallet = walletProofSessionStore.bindingCandidate(userHandle, activeWallet);
if (wallet) {
  const response = await fetchWithWalletIdentity({
    wallet,
    activeWallet,
    url: `/api/passkey-vault-anon/activity/v4?user_handle=${userHandle}`,
  });
}

Older device records can save walletIdentityProof returned after their normal verified owner-operation claim using walletProofSessionStore.save(userHandle, walletIdentityProof). Preparation and the exact claim do not require a second passkey ceremony. A missing or expired proof does not authorize private reads.