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

@loopprotocol/sdk-byoaa

v0.1.0-alpha.14

Published

Bring Your Own Attested Agent — TypeScript SDK for submitting bank-attested receipts to Loop Protocol from inside an attested enclave.

Readme

@loopprotocol/sdk-byoaa

Bring Your Own Attested Agent — TypeScript SDK for submitting bank-attested receipts to Loop Protocol from inside an attested enclave.

Status: alpha candidate (0.1.0-alpha.14). On-chain primitives are deployed on devnet (loop-shopping submit_attested_receipt + BankAttestedReceipt PDA). Mainnet deploy gated on Bundle 2 (loop-protocol PR #27 + #28 + #29) + the mainnet ShoppingState bump repair.

What this is

Loop Protocol's bank-data layer is built on the BYOAA model: users (or their hosted-agent provider — Anthropic Claude with Computer Use, OpenAI Operator, self-hosted Nitro Enclaves, etc.) run an agent inside an attested enclave that:

  1. Logs into a bank (the agent has the credentials; Loop never touches them).
  2. Reads transactions.
  3. Constructs a BankReceipt and signs it with the enclave-bound session key.
  4. Submits the receipt to the on-chain submit_attested_receipt instruction.

This package provides the TypeScript primitives for steps 3 and 4. The enclave + bank scraping is the user's responsibility (see the reference implementation at loop-byoaa-reference-agent).

Spec: docs/roadmap/08-byoaa-sdk.md.

Install

npm install @loopprotocol/[email protected]
# optional peer deps for /solana and /runtime-sandbox surfaces (Node 20+ runtime):
npm install @coral-xyz/anchor @loopprotocol/sdk @solana/web3.js

@loopprotocol/sdk-byoaa default install is lightweight: production dependencies are limited to browser-safe hashing, encoding, and policy-signature primitives. Solana/on-chain helpers require the optional peer packages above; REST-only and receipt-only consumers do not need to import those surfaces.

Runtime support

0.1.0-alpha.14 publishes explicit entry points:

  • @loopprotocol/sdk-byoaa/receipt — browser-safe receipt helpers only; no Solana, Anchor, or Node crypto imports. Node 18+ compatible.
  • @loopprotocol/sdk-byoaa/rest — REST/API-key client surface. Node 18+ and browser-safe.
  • @loopprotocol/sdk-byoaa/proof — browser-safe shape-only proof helpers; no Solana, Anchor, Buffer, or Node crypto imports. Current proof verification is explicitly shape_only; it is not full cryptographic authenticity verification. Node 18+ compatible.
  • @loopprotocol/sdk-byoaa/solana — Node 20+ only. Uses optional peer packages (@coral-xyz/anchor, @loopprotocol/sdk, @solana/web3.js) for Solana/on-chain helpers: network config, PDA derivation, instruction encoding, receipt submission, and on-chain receipt verifier/account decoders.
  • @loopprotocol/sdk-byoaa/runtime-sandbox — Node 20+ hosted Runtime Sandbox signer adapter. Also uses the same optional peer packages and implements EnclaveSigner over a scoped bearer-token broker proxy; the SDK does not include any public token.
  • @loopprotocol/sdk-byoaa — browser-safe alpha aggregate root with receipt, REST, and shape-only proof helpers. Node 18+ compatible. Use /solana for chain/RPC helpers and /runtime-sandbox for hosted signer access.

For browser/frontend code, prefer the smallest subpath. Do not ship REST SDK keys to browsers.

Mainnet and localnet fail closed in config resolution:

  • network: "mainnet-beta" throws until BYOAA mainnet shopping program ids are live. Controlled dry runs must pass explicit non-placeholder program ids plus dangerouslyAllowUnreleasedMainnet: true.
  • network: "localnet" requires explicit shoppingProgramId and vaultProgramId; the SDK will not silently reuse devnet ids for local transaction construction.

Security and API keys

Runtime Sandbox signer tokens are bearer credentials too. They must be scoped per user/profile/runtime, expiry-bound, and revocable. Do not embed a shared public token in apps or docs. A logged-in app should retrieve a short-lived runtime token from the user's Loop profile/session, then inject it into RuntimeSandboxSigner via tokenProvider.

import { RuntimeSandboxSigner } from "@loopprotocol/sdk-byoaa/runtime-sandbox";

const enclaveSigner = new RuntimeSandboxSigner({
  brokerUrl: "https://runtime-api-devnet-v0.looplocal.io/api/v1/runtime-sandboxes/byoaa-devnet-nitro-v0/broker",
  tokenProvider: async () => {
    const res = await fetch("/api/me/runtime-sandbox-token", { method: "POST" });
    if (!res.ok) throw new Error("runtime token unavailable");
    return (await res.json()).token;
  },
});

The signer implements the existing EnclaveSigner interface used by AttestedReceiptSubmitter; it maps getSessionPubkey() and signInstruction(...) to the Runtime Sandbox broker wire protocol. For manual probes, the broker proxy accepts POST at the broker URL above with { "method": "getSessionPubkey" } or the documented signInstruction payload.

Plug-and-play hosted Runtime Sandbox submitter

Agents should prefer the helper below instead of manually wiring the signer, live session pubkey, and submitter. On-chain attestation requires a paid/runtime entitlement; free-tier tokens must fail closed at the broker before signing.

import { PublicKey } from "@solana/web3.js";
import { createRuntimeSandboxSubmitter } from "@loopprotocol/sdk-byoaa/runtime-sandbox";

const runtime = await createRuntimeSandboxSubmitter({
  brokerUrl: "https://runtime-api-devnet-v0.looplocal.io/api/v1/runtime-sandboxes/byoaa-devnet-nitro-v0/broker",
  vault: new PublicKey(process.env.LOOP_VAULT_PUBKEY!),
  network: "devnet",
  tokenProvider: async () => {
    const res = await fetch("/api/me/runtime-sandbox-token", { method: "POST" });
    if (!res.ok) throw new Error("runtime token unavailable");
    return (await res.json()).token;
  },
});

console.log("live Runtime Sandbox session", runtime.sessionPubkey.toBase58());
const result = await runtime.submitter.submit(receipt);

If submit() returns transaction_send_failed with insufficient funds, SessionAccountInvalid, or an entitlement/auth error, do not ask the end user to debug Solana. Refresh the runtime bundle, retrieve a paid runtime token, or ask the operator control plane to fund/register the current session.

LoopByoaaClient SDK keys are bearer credentials. Handle them like passwords:

  • keep SDK keys in server-side environment variables or a secret manager;
  • do not ship SDK keys to browser/frontend bundles;
  • do not log or serialize SDK keys, clients, headers, request objects, or errors containing request data;
  • rotate any key that appears in logs, snapshots, telemetry, or crash reports.

By default, the REST client derives the Loop API host from the key environment. If you pass a custom baseUrl, the SDK will send Authorization: Bearer <apiKey> to that host. Custom hosts are blocked unless you explicitly opt in:

import { LoopByoaaClient } from "@loopprotocol/sdk-byoaa";

const loop = new LoopByoaaClient({
  apiKey: process.env.LOOP_BYOAA_API_KEY!,
  baseUrl: "https://trusted-test-gateway.example.com",
  dangerouslyAllowCustomBaseUrl: true, // sends bearer credentials to this host
});

Only use dangerouslyAllowCustomBaseUrl for trusted test harnesses or approved federated deployments.

Quick start

Official authority loop (copy/paste; no custom harness)

Use the packaged examples/authority-loop.ts as the official adopter path. It uses only declared LoopByoaaClient methods and performs the complete authority sequence:

  1. loop.bridge.registerAgent registers with a live agent_key.
  2. loop.permissions.grant grants explicit bounded authority for that agent.
  3. loop.decisions.request requests a decision with permission_key.
  4. For a review result, loop.approvals.createLink produces the human Authority Center URL.
  5. loop.approvals.waitForDecision polls without approving or fabricating proof.
  6. loop.approvals.resume resumes only after a real human approval receipt exists.
  7. loop.audit.exportPack exports a top-level portable audit pack and validates required live fields.
npm install @loopprotocol/[email protected] [email protected]
export LOOP_BYOAA_API_KEY=<dev-sdk-key>
export LOOP_PRINCIPAL_ID=<dev-principal-id>
npx tsx node_modules/@loopprotocol/sdk-byoaa/examples/authority-loop.ts

Optional: set LOOP_AUTHORITY_CENTER_URL only when an operator supplies a different approved Authority Center origin. Expected outcomes are either an immediate allow, a printed approval URL followed by a real human approve/resume, or a clear fail-closed error (deny, missing review_request_id, timeout/denial/expiry, missing final decision ref, or invalid audit pack). This example never approves for the human, fabricates a receipt/proof, or mutates production. No copied snippets, endpoint guesses, or custom troubleshooting harness are required.

Credentials are split by runtime:

  • LOOP_BYOAA_API_KEY is the server-side KYA/BYOAA REST credential used by the authority loop and dev-gateway smoke.
  • LOOP_PRINCIPAL_ID is required by the authority-loop decision request so the gateway can bind authority to an existing dev principal.
  • LOOP_RUNTIME_TOKEN is only for the separately documented hosted Runtime Sandbox signer flow; the REST authority loop and dev-gateway read smoke do not use it.

Official smokes

From a fresh consumer directory, install the exact package plus its documented TypeScript runner, then run the shipped credential-free installed-artifact smoke. The smoke uses only public package exports and exercises both CJS and ESM REST imports, canonical dev-host derivation, and bearer-key serialization guardrails. It does not build, pack, read source, or make a network request:

npm init -y
npm install @loopprotocol/[email protected] [email protected]
npx tsx node_modules/@loopprotocol/sdk-byoaa/scripts/packed-artifact-smoke.ts

Expected result: packed CJS+ESM artifact smoke passed. A missing public REST export, key-parser mismatch, non-canonical dev host, or bearer-key serialization/reflection failure exits non-zero.

Run the credentialed dev-gateway parity smoke with a scoped dev SDK key:

LOOP_BYOAA_API_KEY=<dev-sdk-key> npx tsx node_modules/@loopprotocol/sdk-byoaa/scripts/dev-gateway-smoke.ts

The smoke derives https://dev-api.kya.looplocal.io from the dev key and calls only the declared harmless loop.agents.list({ limit: 1 }) route. It performs no registration, decision, approval, resume, audit export, or production mutation. Expected result is one JSON line with "ok":true, "environment":"dev", the route, item count, and "mutation_performed":false.

It fails closed when LOOP_BYOAA_API_KEY is missing/malformed or is a staging/production key. The official smoke refuses custom gateway hosts so a bearer key cannot be redirected away from the canonical dev gateway.

CI never receives a live key and therefore runs the explicit non-live gate only:

LOOP_BYOAA_SMOKE_MODE=skip npx tsx node_modules/@loopprotocol/sdk-byoaa/scripts/dev-gateway-smoke.ts

This is the official no-troubleshooting quickstart/smoke path: use these commands and package artifacts directly; do not build a custom harness or invent endpoints.

Maintainers working from a source checkout use the separate npm run smoke:fresh-consumer gate to build, pack, install, production-audit, import-smoke, and Vite-build the candidate tarball. That maintainer command requires source and development tooling and is not an installed-package adopter command.

Discover Loop authority metadata and register an external agent

Loop publishes WorkOS/Auth.md-compatible discovery surfaces at looplocal.io, but the SDK keeps Loop-native proof semantics: AuthJWT identity, KYA decisions, BYOAA receipts, Authority Center approval, Clearing House audit refs, and public/private receipt modes.

Discovery calls never send your SDK bearer key:

import {
  LoopByoaaClient,
  buildLoopAgentRegisterInput,
  discoverLoopAuthority,
} from "@loopprotocol/sdk-byoaa";

const authority = await discoverLoopAuthority();
console.log(authority.protectedResource.loop_authority?.receipt_modes);

const loop = new LoopByoaaClient({
  apiKey: process.env.LOOP_BYOAA_API_KEY!,
});

const agentInput = buildLoopAgentRegisterInput({
  client_id: "acme-procurement-agent",
  client_name: "Acme Procurement Agent",
  redirect_uris: ["https://acme.example/callback"],
  scope: "invoices:read payments:write",
  software_statement: "sha256:attested-build",
  jwks_uri: "https://acme.example/.well-known/jwks.json",
});

const agent = await loop.bridge.registerAgent(agentInput);

The WorkOS/Auth.md adapter is only an input mapper. It does not add a WorkOS-hosted runtime dependency and does not replace Loop's AuthJWT/KYA/BYOAA/Clearing House proof spine.

Construct + validate a receipt (no on-chain interaction)

import {
  deriveBankTxnId,
  normalizeMerchantName,
  validateReceipt,
  type BankReceipt,
} from "@loopprotocol/sdk-byoaa/receipt";

const merchantNameRaw = normalizeMerchantName(
  "  starbucks #12345  SEATTLE WA  "
); // → "STARBUCKS #12345 SEATTLE WA"

const postedAt = BigInt(Math.floor(Date.now() / 1000));

const bankTxnId = deriveBankTxnId({
  merchantNameRaw,
  amountCents: 875n,
  postedAt,
  accountLast4: 4242,
});

const receipt: BankReceipt = {
  bankTxnId,
  merchantNameRaw,
  mcc: 5814,
  amountCents: 875n,
  postedAt,
  accountLast4: 4242,
};

const validation = validateReceipt(receipt);
if (!validation.ok) {
  throw new Error(`Bad receipt: ${validation.error}`);
}

Submit from inside an attested enclave

AttestedReceiptSubmitter signs and submits one or more AttestedReceipt payloads from an enclave-bound session signer. It validates receipt bounds client-side, derives the receipt PDA, builds the submit_attested_receipt instruction, and sends it through the configured Solana connection.

AttestedReceiptVerifier is exported from @loopprotocol/sdk-byoaa/solana. It fetches recorded receipt PDAs and verifies the on-chain fields against the expected bank transaction id, merchant, amount, timestamp, and PCR/session metadata. It is a read/verify helper; it does not trust off-chain scraper output by itself.

Package surface

| Export | Purpose | |---|---| | BankReceipt | In-memory shape of one bank transaction | | deriveBankTxnId(input) | Stable 32-byte sha256 over normalized fields. Re-fetches collide on the same on-chain PDA. | | normalizeMerchantName(raw) | Canonical form: trim → collapse whitespace → uppercase → strip non-ASCII | | validateReceipt(receipt, nowSeconds?) | Front-runs the on-chain handler's bounds checks; cheap, no I/O | | MAX_RECEIPT_CENTS | $100M cap (mirrors on-chain) | | MAX_MERCHANT_NAME_RAW_LEN | 64 bytes (mirrors on-chain) | | MAX_RECEIPT_AGE_SECONDS | 365 days (mirrors on-chain) | | AttestedReceiptSubmitter | Builds/signs/sends submit_attested_receipt transactions for enclave-bound sessions | | createRuntimeSandboxSubmitter | Hosted Runtime Sandbox helper that resolves live session pubkey and returns { signer, sessionPubkey, submitter } for agent plug-and-play use | | AttestedReceiptVerifier | Fetches and verifies recorded attested receipt PDAs against expected receipt/session fields |

What this does NOT do

  • Hold bank credentials — that's the user's enclave's job.
  • Run the bank scraper — that's the user's agent code.
  • Manage the enclave attestation — see @loopprotocol/sdk EnclaveClient (spec 07a).
  • Pay receipt rent — the session signer (the enclave) pays SOL fees + ~0.0018 SOL rent per receipt.
  • Honor receipts as merchant payouts — that's spec 08a (merchant_claim_attested_receipt), shipped as a separate flow.

Trust model

The receipt's on-chain pcr0 field is copied at submit time from the registered session, which was bound to the enclave's image at registration. To submit a forged receipt, an attacker would need an enclave whose PCRs match an audited image AND would be admin-approved in EnclaveImageRegistry — same trust assumption as the entire spec 07a system. See spec 08 § "Threat model".

Proof helper semantics

verifyAttestedReceiptProofShape() is a shape/fail-closed helper, not a cryptographic authenticity verifier. For the current cose_sign1_x509 arm it checks that signature and cert_chain are byte-shaped and returns:

{
  ok: true,
  proof_type: "cose_sign1_x509",
  verification_level: "shape_only",
  cryptographic_authenticity_verified: false,
}

It does not validate the COSE signature, X.509 trust chain, enclave attestation root, audited-image binding, or bank/source authenticity. Reserved ZK proof arms fail closed with UnsupportedProofTypeError. The legacy verifyAttestedReceiptProof() export is a deprecated alias with the same shape-only semantics.

Development

npm install
npm run build
npm test
npm run typecheck

Tests are vitest, no on-chain dependency. End-to-end devnet rehearsal lives in loop-protocol/scripts/devnet-rehearsal/test-spec08-09-flow-devnet.ts.

Package B joined-proof settlement smoke

For the Package B checkpoint flow (runtime submitter + bridge action receipt + settlement commit + audit pack), run:

LOOP_BYOAA_API_KEY=<sdk-key> \
LOOP_VAULT_PUBKEY=<vault pubkey> \
LOOP_RUNTIME_BROKER_URL=<runtime broker url> \
LOOP_RUNTIME_TOKEN=<runtime scoped token> \
npm run smoke:package-b

Optional environment variables:

  • DEVNET_RPC (defaults to https://api.devnet.solana.com)
  • LOOP_BYOAA_BASE_URL (custom gateway host; sends bearer auth there)
  • LOOP_AGENT_REF
  • LOOP_PRINCIPAL_REF

The smoke script creates a live approval challenge via POST /api/v1/kya/approvals/challenges before runtime submit, requires status/result.approval_receipt in the response, derives approval_receipt_ref/authorization_ref/decision_ref from that receipt, then links those refs into action receipt + settlement commit. It also logs verification_level and an explicit human step: not yet cryptographically verified line.

License

MIT