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

@neuraiproject/neurai-connect-wallet

v0.0.2

Published

Neurai Connect SDK for wallets: pairing, session handling, CAIP-122 auth messages and CACAO responses.

Readme

@neuraiproject/neurai-connect-wallet

The wallet side of Neurai Connect: pair by scanning a QR code, approve logins ("Sign in with Neurai") and answer dApp requests. The SDK never holds private keys — the application signs and hands the result back.

Specification: spec/auth.md, spec/session.md.

import { NeuraiConnectWallet, IdentityRegistry } from "@neuraiproject/neurai-connect-wallet";
import { buildCacao, buildCacaoPayload, formatAuthMessage, signatureTypeForAddress } from "@neuraiproject/neurai-connect-core";
import { sign } from "@neuraiproject/neurai-message";       // in a browser: "@neuraiproject/neurai-message/browser"

const wallet = await NeuraiConnectWallet.init({
  relayUrl: "wss://relay.neurai.org/v1",
  storage,                                   // encrypted app storage
  metadata: { name: "NeuraiWallet", url: "https://neurai.org" },
});

// Login: show domain, URI and statement, then sign the canonical CAIP-122 text.
wallet.on("auth_request", async ({ id, payload, verify }) => {
  if (!(await ui.confirmLogin(payload, verify))) return wallet.rejectAuth(id);
  const address = await ui.pickAddress(payload.addressPolicy);   // identity or wallet address
  const p = buildCacaoPayload(payload, payload.chains[0], address);
  const cacao = buildCacao(p, { t: signatureTypeForAddress(address), s: sign(formatAuthMessage(p), privateKey, true) });
  await wallet.approveAuth(id, { cacaos: [cacao] });
});

// dApp session
wallet.on("session_proposal", async ({ id, namespaces }) => {
  await wallet.approveSession(id, { namespaces: { bip122: { chains, accounts, methods, events } } });
});
wallet.on("session_request", async ({ id, method, params, guard }) => {
  if (guard?.blocked) return wallet.rejectRequest(id);            // a sign-in message for another domain
  const result = await ui.approveAndSign(method, params);        // PIN or biometrics
  await wallet.respondRequest(id, result);
});

await wallet.pair(uriFromScanner);            // nc: from the QR code, or neuraiwallet://connect?uri=…

The example signs for a legacy address (N… / t…). Each address type has its own neurai-message signer, and addressKind(address) from neurai-connect-core tells them apart:

| addressKind | Addresses | Signer (neurai-message 0.11) | s.t | | --- | --- | --- | --- | | legacy | N… / t… | sign(text, privateKey, true) | neurai-secp256k1-compact | | ecdsa | nq1r… / tnq1r… | signECDSAWitnessMessage(text, privateKey, address) | neurai-secp256k1-compact | | authscript | nc1p… / tnc1p… | signPQMessage(text, secretKey, publicKey, address) | neurai-ml-dsa-44 | | pq | pq1z… / tpq1z… | signPQMessage(text, secretKey, publicKey, address) | neurai-ml-dsa-44 |

Always pass the address to the witness signers: witness v2 and v3 sign a hash bound to the address, so a signature made without it never verifies. Check the result with verifyMessage(text, address, signature) before sending it.

What the SDK enforces for you

  • Expiry. A login request whose exp, nbf or TTL has passed — or whose dates cannot be parsed — is rejected on the wire and never reaches auth_request; the reason arrives on auth_rejected. approveAuth checks the window again and throws if the user took too long, so a stale request cannot be signed.
  • Malformed payloads. A statement or a resources entry that is not a non-empty single-line string is rejected the same way, before the request is stored or shown: it could change the shape of the text the user signs. The dApp SDK refuses to publish such a payload too.
  • Cross-domain signMessage. Every signMessage is inspected. By default (signMessageGuard: "confirm") the request still reaches your handler with guard attached, but respondRequest throws unless you pass { confirmedCrossDomain: true } after a reinforced confirmation. With signMessageGuard: "block" the SDK answers 4001 itself and only emits request_blocked.

What the wallet must show

  • Login: the domain and URI in large type, the time of the request, the statement, and the sentence "By approving, the browser that shows this QR code will be connected to your account". verify.domainMatchesMetadata is false when the requester's metadata does not match the domain: warn. Never show a verification code, it does not protect against a relayed QR code.
  • signMessage: the whole message. If it looks like a sign-in message for another domain, block it or require reinforced confirmation.
  • sendTransfer: destination, amount and memo. signPsbt: decoded outputs, change and fee.
  • Every signature behind PIN or biometrics.

Per-domain identity addresses

IdentityRegistry computes the path (m/44'/coin'/101'/0/<index> derived from the canonical domain) and records the identities used, which the application backup MUST include: the sparse indexes are not rediscovered by a normal BIP44 scan. The application derives the key with @neuraiproject/neurai-key.

Sessions

sessions(), disconnect(topic), disconnectAll() ("log out everywhere"), extendSession, updateSession and emitSessionEvent back the session-management screen. Sessions last 7 days, persist across restarts and are restored on init(). The session_settled event fires whenever this wallet settles one (a proposal approved, or a one-click login), so a session list can redraw itself without tracking the approval calls.

Pairings

pairingList() is every pairing the wallet holds and pendingPairings() the subset still waiting for the site to send its proposal or its login request. Approving either marks the pairing active, so it leaves pendingPairings() while remaining in pairingList() — and init() resubscribes to every pairing in the list, active ones included. forgetPairing(topic) and forgetPairings() drop them: nobody is notified, because there is nothing settled to notify — the site simply never gets an answer. Use them to cancel a scan, and to empty a relay before moving to another one.

A session and a pairing are both topics on one relay. A wallet that changes relay while it holds either strands it: it resubscribes on the new server, where the site is not listening, and a later disconnect() publishes wc_sessionDelete where nobody reads it. Wallets that expose a relay setting SHOULD refuse the change until sessions() and pairingList() are both empty — pairingList(), not pendingPairings(), because a login that settled no session still leaves an active pairing that the next start would resubscribe elsewhere.

In a browser

@neuraiproject/neurai-key and @neuraiproject/neurai-message resolve to their Node builds by default; a web wallet must import their /browser subpaths. Use BrowserStorage from @neuraiproject/neurai-connect-core as the storage adapter, and never persist the mnemonic.