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

@crediolabs/agent-web-wallets

v0.1.0

Published

Browser-resident agent wallet for Stellar dApps. A Stellar Wallets Kit module backed by a non-extractable WebCrypto key.

Readme

@crediolabs/agent-web-wallets

A Stellar Wallets Kit module that lets an AI agent hold a browser-resident key and drive a dApp with ordinary UI interaction. No extension, no popup, no human approval.

Security

Removing the popup removes the last human check. What replaces it is smaller than people will assume, so it gets written down here in plain words.

The key is stored in IndexedDB as a non-extractable WebCrypto CryptoKey. It survives reloads and tab closes, and its raw bytes can never be read back by JavaScript - exportKey rejects.

What that buys: script running on the origin (XSS, a malicious dependency, a compromised CDN) cannot steal the key. Compromise is bounded to the session rather than permanent and portable.

What it does not buy: it is not a policy boundary. Any script on the origin can ask the key to sign while the page is open. isAvailable() returning false only hides the module from the connect dialog - the module id is public and any caller may select it. Do not treat the hidden module as a security control.

Treat the balance of the key you put here as the blast radius. Keep it small, and call clearAgentKey() when the run ends.

Caveats you should know before using this

Encoding: signedMessage and signedAuthEntry are base64

SEP-43 contradicts itself - the TypeScript interface for signMessage and signAuthEntry declares the return as a hex string, while the prose says base64. We chose base64 for internal consistency (signAuthEntryXdr already receives its input as base64). If a counterparty expects the SEP-43 hex shape, decode with a base64-to-hex conversion at the edge before comparing.

signAuthEntry is proven equivalent to the reference signer, not verified on-chain

signAuthEntry has never been run against a live Soroban host. It is proven byte-identical to the signature produced by stellar-sdk's own contract.basicNodeSigner(...).signAuthEntry(...) on the exact wire format AssembledTransaction sends - the outer xdr.HashIdPreimage union encoded as base64. The two halves are not the same:

  • the proof: same input, same output, byte for byte, against the same reference implementation that ships in @stellar/stellar-sdk.
  • the limit: no Soroban host has ever accepted the resulting signature. Treat the on-chain path as unproven until exercised end to end.

Key zeroing is partial: we zero our copies

Keypair.fromSecret(secret) retains the raw seed inside the Keypair object for the lifetime of the call, with no public wipe API. The secret string the caller passed in is an immutable JavaScript string we cannot erase. What setAgentKey zeroes is the buffers it allocated itself (the raw seed buffer and the PKCS#8 encoding it derives from it). The honest claim is "we zero our copies", not "the secret leaves no trace in the heap". Do not load this key from a context that captures the secret in long-lived closures, error logs, or dev tools.

isAvailable() hides, it does not prevent

isAvailable() returns false until setAgentKey has run, which keeps the module out of a human user's Stellar Wallets Kit connect dialog. That is a UX affordance for agent-driven flows. It is not a security control: AGENT_WALLET_ID is "agent-web-wallet", it is public in this README and in src/stellar/module.ts, and any script on the origin can select it directly with kit.setWallet("agent-web-wallet").

Storage: IndexedDB or nothing

IndexedDB is the only browser store that can hold a non-extractable CryptoKey (it is structured-cloneable; localStorage and sessionStorage only take strings, which would put the key in plaintext where any script on the origin can read it). When IndexedDB is unavailable, setAgentKey throws StorageUnavailableError:

  • Firefox private windows expose no IndexedDB by design.
  • Partitioned third-party iframes (Safari ITP, cross-site iframes) have no IndexedDB access.
  • Chrome with third-party cookies blocked can throw a SecurityError from indexedDB.open() rather than undefined. Both leave the module unusable; callers must catch and surface the error.

Network passphrase is pinned at module construction

new AgentWalletModule({ networkPassphrase: Networks.PUBLIC }) fixes the passphrase for the lifetime of that instance. A conflicting per-call passphrase is refused with NetworkMismatchError (transaction envelope signing) or detected against the embedded networkId and refused with NetworkMismatchError (Soroban auth entry signing, where the preimage carries its own network identity).

Why this matters: a transaction envelope carries no network identity of its own - the passphrase is supplied alongside it. A signature produced under a wrong-but-valid passphrase is byte-valid as an ed25519 signature but is bound to a network the recipient will not accept. With a human approval popup, the human notices the wrong network name and cancels. This wallet has no popup, so no one would notice. Refuse the request at the boundary instead.

Install

npm i @crediolabs/agent-web-wallets

Peer dependencies:

  • @stellar/stellar-sdk (>= 14)
  • @creit.tech/stellar-wallets-kit (>= 1.9.1)

Use

The agent places a key once through whatever channel it has for out-of-band calls to the page, then drives the dApp with ordinary UI interaction. No special signing channel; the dApp uses the kit exactly as it would for any other wallet.

import {
  StellarWalletsKit,
  WalletNetwork,
  allowAllModules,
} from "@creit.tech/stellar-wallets-kit";
import { AgentWalletModule } from "@crediolabs/agent-web-wallets/stellar";
import { setAgentKey, clearAgentKey } from "@crediolabs/agent-web-wallets";

const kit = new StellarWalletsKit({
  network: WalletNetwork.PUBLIC,
  modules: [
    ...allowAllModules(),
    new AgentWalletModule({ networkPassphrase: WalletNetwork.PUBLIC }),
  ],
});

// The agent does this once, through its automation channel:
//   await setAgentKey("S...");
// then drives the app with normal clicks; the kit now resolves every
// signing request without a popup.

// At the end of the run, remove the key:
//   await clearAgentKey();

The key persists across reloads and tab closes until clearAgentKey() runs. That is the deliberate consequence of choosing IndexedDB over sessionStorage - see the Security section above.

API surface

From @crediolabs/agent-web-wallets (the package root):

| export | purpose | | --- | --- | | setAgentKey(secret) | import a Stellar secret seed, store a non-extractable CryptoKey, return the derived address | | clearAgentKey() | remove the stored key from IndexedDB | | getAgentAddress() | read the address stored alongside the key (or null) | | hasAgentKey() | whether a key is stored in this browser; what the module's isAvailable() delegates to | | signPayload(bytes) | sign arbitrary bytes with the session key (raw ed25519 R||S) | | NoAgentKeyError | no key in this browser | | Ed25519UnsupportedError | WebCrypto Ed25519 missing on this browser | | StorageUnavailableError | no IndexedDB in this browsing context | | NetworkMismatchError | per-call passphrase disagrees with the pinned one (or disagrees with the Soroban preimage's networkId) | | SignerMismatchError | per-call address does not match the agent key in this browser | | InvalidAuthEntryPreimageError | signAuthEntryXdr was given a non-Soroban HashIdPreimage variant alongside an expected network passphrase |

From @crediolabs/agent-web-wallets/stellar:

| export | purpose | | --- | --- | | AgentWalletModule | the SWK ModuleInterface implementation | | AGENT_WALLET_ID | the string "agent-web-wallet", the SWK product id | | signTransactionXdr(envelopeXdr, networkPassphrase) | sign a base64 transaction envelope | | signAuthEntryXdr(preimageBase64, expectedNetworkPassphrase?) | sign a Soroban auth entry preimage | | signMessageBytes(message) | sign a UTF-8 message |

signPayload and the three sign*Xdr helpers are the building blocks for non-SWK consumers - the module wires them up to the SEP-43 surface.

Browser requirements

  • WebCrypto Ed25519. crypto.subtle.importKey("pkcs8", ..., "Ed25519", ...) must succeed. Chrome 137+, Firefox 132+, Safari 17+ all support it. Older targets fail with Ed25519UnsupportedError.
  • IndexedDB. Required to persist the key across reload. See the Storage caveat above for the contexts in which it can silently be missing.
  • No Buffer. The shipped runtime code uses Web APIs only. Node Buffer is not a browser global and no polyfill ships with this package.

Scope: v1 is Stellar only

This package ships one adapter: a custom Stellar Wallets Kit module. There is no EVM support. The equivalent for EVM is a custom wagmi connector, which is a separate surface (EIP-1193, chain switching, no auth-entry concept). The core session APIs are chain-agnostic, but no EVM code ships in v1.

The signing surface intentionally covers the three SEP-43 entry points SWK exposes - signTransaction, signAuthEntry, signMessage - and nothing else. There is no policy engine, no hosted signer, and no browser automation: the agent brings its own driver. The key is local to the browser; a remote signer would make a public npm package unusable by anyone who installs it.