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

@moveindustries/keyless

v0.1.1

Published

Keyless account SDK for Movement blockchain — social login via ZK proofs

Readme

@moveindustries/keyless

TypeScript SDK for keyless accounts on the Movement blockchain. Users sign transactions with their Google account; a zero-knowledge proof vouches for the JWT without revealing it on-chain.

Wraps the @moveindustries/ts-sdk KeylessAccount, hides the prover HTTP call and the Poseidon nonce commitment, and exposes two login flows: browser-redirect (for SPAs) and stored-JWT (for Chrome MV3 extensions).

Install

npm install @moveindustries/keyless @moveindustries/ts-sdk

@moveindustries/ts-sdk is a peer dependency — the SDK doesn't bundle it so callers pick the version. This SDK is validated against 5.1.7, which is what package.json#devDependencies pins; any version in the >=5.1.0 range should work.

Prerequisite: a running prover

The SDK talks to the prover's POST /prove endpoint. For local development the prover + pepper-service run as docker compose up --build from the repo root — see the repo README.

For production you need a publicly reachable prover URL.

Browser redirect flow (single-page app)

import { MovementKeyless } from "@moveindustries/keyless";

const keyless = new MovementKeyless({
  proverUrl: "http://localhost:3001",
  clientId: "123456.apps.googleusercontent.com",   // your Google OAuth client_id
  redirectUri: "http://localhost:5173/callback",   // must be registered with Google
});

// On "Sign in with Google" click: generates an ephemeral key, stores it in
// sessionStorage, redirects to Google's consent screen with a Poseidon nonce
// that commits to the key.
loginButton.onclick = () => keyless.beginLogin();

// On the redirect callback page — reads id_token from the URL hash,
// requests a ZK proof from the prover, returns a signing-ready account.
if (keyless.hasSession()) {
  const account = await keyless.completeLogin();

  const movement = keyless.getMovement();
  const txn = await movement.transaction.build.simple({
    sender: account.accountAddress,
    data: {
      function: "0x1::aptos_account::transfer",
      functionArguments: [recipient, amount],
    },
  });
  const pending = await movement.signAndSubmitTransaction({ signer: account, transaction: txn });
  await movement.waitForTransaction({ transactionHash: pending.hash });
}

getMovement() returns a Movement client already configured against Movement testnet (override with the fullnodeUrl config field).

Chrome extension flow (MV3)

Extensions can't do page redirects. Use the lower-level primitives instead and drive OAuth via chrome.identity.launchWebAuthFlow:

import { MovementKeyless } from "@moveindustries/keyless";

const keyless = new MovementKeyless({
  proverUrl: "http://localhost:3001",
  clientId: GOOGLE_CLIENT_ID,
  redirectUri: chrome.identity.getRedirectURL(),   // https://<ext-id>.chromiumapp.org/
});

// 1. Build the OAuth URL and keep the ephemeral key yourself.
const { url, ephemeralKey } = keyless.buildOAuthUrl();
await chrome.storage.local.set({ epk: ephemeralKey });

// 2. Drive OAuth — Chrome returns the redirect URL with #id_token=…
const responseUrl = await chrome.identity.launchWebAuthFlow({ url, interactive: true });
const jwt = new URL(responseUrl).hash
  .substring(1)
  .split("&")
  .map((p) => p.split("="))
  .find(([k]) => k === "id_token")?.[1];

// 3. Ask the prover for a proof.
const { account, pepper, address } = await keyless.completeLoginWithJwt(jwt!, ephemeralKey);
// `pepper` + `address` are suitable for the wallet's encrypted vault so the
// user can be re-authenticated on unlock without losing their on-chain address.

Ephemeral keys expire after 1 hour; the wallet should watch the expiry and silently re-authenticate (keyless.buildOAuthUrl({ prompt: "none" })) before then.

Configuration

interface MovementKeylessConfig {
  proverUrl: string;        // POST {proverUrl}/prove
  clientId: string;         // Google OAuth client_id; becomes `aud` in the JWT
  redirectUri: string;      // registered Google redirect URI
  fullnodeUrl?: string;     // default: https://testnet.movementnetwork.xyz/v1
  uidKey?: string;          // default: "sub"
}

clientId is baked into every derived keyless address as the aud claim — changing it changes all addresses.

API surface

| Method | Use case | |---|---| | beginLogin() | SPA: redirect to Google OAuth | | completeLogin() | SPA: on the callback page, returns a KeylessAccount | | buildOAuthUrl({ prompt? }) | Build the OAuth URL without navigating (extension/native) | | completeLoginWithJwt(jwt, epk) | Complete login given a JWT obtained externally | | hasSession() | Check whether sessionStorage holds an ephemeral key | | logout() | Clear the in-memory session | | getMovement() | Preconfigured Movement client for submitting transactions |

Validation done locally

completeLogin / completeLoginWithJwt reject the JWT and refuse to attribute the session if any of these client-side checks fail (independent of the prover and chain, defence-in-depth):

  • Nonce match. payload.nonce must equal the Poseidon commitment that was placed in the OAuth URL — prevents a swapped JWT from binding to a different ephemeral key.
  • Aud match. payload.aud must equal the configured clientId — catches a JWT issued to a different OAuth client (wrong env, malicious page that triggered its own OAuth flow).
  • Exp in the future. Refuses an expired JWT before paying the prover round-trip.
  • Address derivation match. After the prover responds, the SDK re-derives the on-chain address locally from the JWT claims + the prover's pepper via KeylessPublicKey.fromJwtAndPepper(...).authKey().derivedAddress(). If the prover's returned address disagrees, the call rejects. The returned KeylessLoginResult.address is always the locally-derived value, so a buggy or compromised prover cannot mis-attribute a proof to a different account.

Development

npm install
npm test            # vitest
npm run build       # emits dist/

Tests live under src/__tests__/. keyless.test.ts exercises the full login flow against a mocked prover and is a good companion to this README.

Open issues

  • On-chain VK alignment. Movement testnet's deployed Groth16 verification key matches the circuit-v4.0.0 ceremony (deployed 2026-05-08); the prover must use that zkey. Proofs generated against any other zkey (e.g. circuit-v1.0.1, circuit-v1.2.0, circuit-v5.0.0) will be accepted by the SDK but rejected by the chain.
  • Prover hosting. No public prover instance yet — consumers need to run their own (docker compose, as above).