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

@openzeppelin/guardian-operator-client

v0.14.6

Published

TypeScript HTTP client for Guardian operator dashboard endpoints

Downloads

168

Readme

@openzeppelin/guardian-operator-client

TypeScript HTTP client for Guardian operator dashboard endpoints.

Installation

npm install @openzeppelin/guardian-operator-client

Setup

import { GuardianOperatorHttpClient } from '@openzeppelin/guardian-operator-client';

const client = new GuardianOperatorHttpClient({
  baseUrl: 'http://localhost:3000',
  credentials: 'include',
});

Usage

Request A Challenge

const challenge = await client.challenge('0x...');
console.log(challenge.challenge.signingDigest);

Verify A Signed Challenge

The package does not talk to wallets or sign challenges. Callers provide the commitment and signature.

const verified = await client.verify({
  commitment: '0x...',
  signature: '0x...',
});

console.log(verified.operatorId);

List Accounts (Paginated)

// Default page size is 50; max is 500.
const page = await client.listAccounts({ limit: 50 });
console.log(page.items[0]?.accountId);

// Resume the next page with the cursor from the previous response.
if (page.nextCursor !== null) {
  const next = await client.listAccounts({
    limit: 50,
    cursor: page.nextCursor,
  });
  console.log(next.items.length);
}

Fetch One Account

getAccount() returns the bare DashboardAccountDetail — there is no { success, account } wrapper. Read fields directly on the response.

const detail = await client.getAccount('0x...');
console.log(detail.authorizedSignerIds);
console.log(detail.currentCommitment, detail.stateUpdatedAt);

Decoded Account Snapshot

Returns the decoded state (vault contents, pending-candidate flag) at the commitment Guardian last canonicalized for the account. Sourced from Guardian's stored state — no live Miden RPC calls.

const snapshot = await client.getAccountSnapshot('0x...');
console.log(snapshot.commitment, snapshot.updatedAt);
if (snapshot.hasPendingCandidate) {
  console.warn('vault may be stale — candidate delta in flight');
}
for (const asset of snapshot.vault.fungible) {
  console.log(asset.faucetId, asset.amount);
}

Inventory And Health Snapshot

const info = await client.getDashboardInfo();
console.log(info.totalAccountCount, info.environment);
console.log(info.deltaStatusCounts.candidate);
console.log(info.inFlightProposalCount);
if (info.serviceStatus === 'degraded') {
  console.warn('degraded aggregates:', info.degradedAggregates);
}

Per-Account Delta Feed

const page = await client.listAccountDeltas('0x...', { limit: 50 });
for (const entry of page.items) {
  console.log(entry.nonce, entry.status, entry.statusTimestamp);
}

Per-Account In-Flight Proposals

Single-key Miden accounts and EVM accounts always return an empty proposal queue.

const page = await client.listAccountProposals('0x...', { limit: 50 });
for (const entry of page.items) {
  console.log(
    entry.commitment,
    entry.signaturesCollected,
    '/',
    entry.signaturesRequired,
  );
}

Cross-Account Delta Feed

Paginated newest-first by status_timestamp DESC. The optional status filter accepts a single status or an array; the wrapper serializes the array to a comma-separated query parameter.

const page = await client.listGlobalDeltas({
  limit: 50,
  status: ['candidate', 'canonical'],
});
for (const entry of page.items) {
  console.log(entry.accountId, entry.nonce, entry.status);
}

Cross-Account In-Flight Proposal Feed

Paginated newest-first by originating_timestamp DESC. There is no status filter — every entry is in-flight by definition. EVM accounts do not appear in v1.

const page = await client.listGlobalProposals({ limit: 50 });
for (const entry of page.items) {
  console.log(
    entry.accountId,
    entry.commitment,
    entry.signaturesCollected,
    '/',
    entry.signaturesRequired,
  );
}

Logout

await client.logout();

Pagination Shape

Every paginated dashboard endpoint returns a PagedResult<T> envelope:

{
  "items": [ /* T entries */ ],
  "next_cursor": "string | null"  // null at end of list
}
  • limit defaults to 50 and is capped at 500. A bare ?limit= (present but empty) is treated as omitted; out-of-range values return 400 invalid_limit.
  • cursor is opaque, server-signed, and tied to a specific endpoint kind (e.g. an account-list cursor cannot be replayed on the deltas endpoint). Tampered or stale cursors return 400 invalid_cursor. There is no client-visible TTL.
  • A cursor may be supplied without a limit; the default of 50 applies.
  • The account list endpoint sorts by (updated_at DESC, account_id ASC). Concurrent updates to updated_at MAY cause an account to be skipped or repeated across a traversal — documented expected behavior of cursor pagination over a mutable sort key. All other paginated endpoints sort by an immutable per-account nonce (or (nonce, commitment) for proposals) and are fully stable.

Cookie Transport

The Guardian operator session is cookie-based. This package does not manage a cookie jar. Configure credentials or a custom fetch implementation appropriate for your runtime.

Error Handling

import {
  GuardianOperatorContractError,
  GuardianOperatorHttpError,
  isDashboardErrorCode,
} from '@openzeppelin/guardian-operator-client';

try {
  await client.listAccounts({ limit: 9999 });
} catch (error) {
  if (error instanceof GuardianOperatorHttpError) {
    // Stable machine-readable code lives on `error.data.code`.
    // Branch on it rather than on `error.status` or
    // `error.data.error` (the human message).
    const code = error.data?.code;
    if (code && isDashboardErrorCode(code)) {
      switch (code) {
        case 'invalid_limit':
        case 'invalid_cursor':
        case 'invalid_status_filter':
          // Caller bug — fix the request.
          break;
        case 'account_not_found':
          // Path-addressed account does not exist.
          break;
        case 'data_unavailable':
          // Metadata exists but storage cannot be read; retry later.
          break;
        case 'authentication_failed':
          // Operator session is missing, tampered, or expired —
          // re-issue the challenge / verify flow.
          break;
      }
    }
  }

  if (error instanceof GuardianOperatorContractError) {
    console.error(error.message);
  }
}

The dashboard error taxonomy (feature 005-operator-dashboard-metrics FR-028) is:

| HTTP | Body code | When | |------|-------------|------| | 401 | authentication_failed | missing / tampered / expired operator session | | 404 | account_not_found | path-addressed account does not exist | | 400 | invalid_cursor | tampered, malformed, or stale cursor | | 400 | invalid_limit | limit outside [1, 500] | | 400 | invalid_status_filter | global delta feed status filter is unknown or malformed | | 503 | data_unavailable | metadata exists but underlying records cannot be read |