coco-cashu-plugin-npc
v3.0.0
Published
NPubCash plugin for @cashu/coco-core - bridges NPubCash server with coco wallet
Maintainers
Readme
coco-cashu-plugin-npc
coco-cashu-plugin-npc integrates one or more NPubCash accounts with
@cashu/coco-core. It syncs paid quotes from NPC servers, imports them as
coco mint quotes, and redeems them through the host's mint operation service.
- Polls NPC for paid quotes since a per-account persisted timestamp
- Optionally listens for realtime websocket updates per account
- Groups quotes by
mintUrlbefore importing and redeeming them through coco - Exposes an account-aware
npcextension API for account management, username management, quote inspection, and manual sync
Installation
bun add coco-cashu-plugin-npcInstall the required peer dependencies in the host app as well:
bun add @cashu/coco-core@^2.0.0 typescriptThis package uses npubcash-sdk internally for NPC API access and JWT auth.
Upgrading from v2? See Migrating To v3.
Quick Start
import { NPCPlugin, MemorySinceStore } from "coco-cashu-plugin-npc";
const plugin = new NPCPlugin({
defaultBaseUrl: "https://npubx.cash",
syncIntervalMs: 25_000,
useWebsocket: true,
});
// manager.use(plugin)
const account = await manager.ext.npc.addAccount({
id: "wallet-main",
signer,
sinceStore: new MemorySinceStore(0),
});
await account.sync();The host calls onInit() during plugin registration and onReady() when
services are ready. The plugin registers manager.ext.npc immediately, even
with zero accounts. Accounts can then sync from interval timers, websocket
notifications, or manual account-level sync() calls.
Configuration
NPCPlugin accepts plugin-level defaults:
interface NPCPluginOptions {
defaultBaseUrl?: string;
accountStore?: NPCAccountStore;
sinceStoreFactory?: NPCSinceStoreFactory;
syncIntervalMs?: number;
useWebsocket?: boolean;
logger?: Logger;
}defaultBaseUrl: NPC server URL used when an account does not provide oneaccountStore: optional host-owned store for account metadatasinceStoreFactory: optional factory for per-accountSinceStoreinstancessyncIntervalMs: default polling interval in milliseconds; omit to disable interval syncinguseWebsocket: default websocket setting for new account runtimeslogger: optional logger used by the plugin and derived child loggers
NPCPluginOptions no longer accepts a signer or a single sinceStore. Signers
and explicit stores are supplied per account.
Extension API
When the plugin is initialized it registers the root npc extension on the
host. The root extension manages accounts:
const npc = manager.ext.npc;
const account = await npc.addAccount({
id: "wallet-main",
signer,
baseUrl: "https://npubx.cash",
syncIntervalMs: 60_000,
useWebsocket: true,
});
await npc.syncAll();
const sameAccount = npc.getAccount("wallet-main");
await sameAccount?.getInfo();Available root methods:
addAccount(options): create or return an account runtimeremoveAccount(accountId): stop and remove an account runtimegetAccount(accountId): return one account API if registeredlistAccounts(): list registered accounts, including stopped accountsgetStatus(): summarize registry and runtime statesyncAll(): run all account syncs concurrentlyshutdownAccount(accountId): shut down one account runtime without removing it
Each account exposes the operations that were global in v2:
await account.getInfo();
await account.getQuotesSince(0);
await account.sync();
const result = await account.setUsername("alice", true);
if (!result.success) {
console.log(result.pr);
}Available account methods:
getInfo(): fetch authenticated NPC account metadatasetUsername(username, attemptPayment?): set the NPC username and optionally handle the payment-required flow through cocogetQuotesSince(sinceUnix): inspect raw NPC quotes without importing them into cocosync(): manually trigger this account's quote sync pipelinestart(): start this account's timer and websocket behaviorstop(): stop this account's timer and websocket behaviorgetStatus(): inspect this account runtime state
The old root methods manager.ext.npc.getInfo(),
manager.ext.npc.setUsername(), manager.ext.npc.getQuotesSince(), and
manager.ext.npc.sync() were removed in v3. Use the account returned by
addAccount() or manager.ext.npc.getAccount(id).
Accounts And Persistence
Account ids are host-defined. The plugin does not derive ids from signers, even when a signer can expose a public key.
await manager.ext.npc.addAccount({
id: "wallet-main",
signer,
baseUrl: "https://npubx.cash",
sinceStore,
autoStart: true,
});addAccount() is idempotent when the incoming configuration matches the
registered account: account id, resolved base URL, signer reference, explicit
SinceStore reference, interval setting, websocket setting, and autoStart
setting. Reusing an id with different configuration throws; remove the account
before re-adding it.
Each account resolves its SinceStore in this order:
AddNPCAccountOptions.sinceStore- plugin
sinceStoreFactory(accountId, baseUrl) - a new in-memory
MemorySinceStore(0)
NPCAccountStore persists account metadata only. It does not persist signer
material, and the plugin does not automatically activate stored records. Hosts
must reconstruct signer material and call addAccount() for the accounts they
want active.
Sync Behavior
Each account sync cycle:
- Reads the last processed timestamp from that account's
SinceStore - Fetches paid quotes from NPC with
getQuotesSince(since) - Filters out already-processed timestamps, invalid quotes, and invalid mint URLs
- Groups valid quotes by
mintUrl - Adds each mint as trusted and forwards transformed quotes to coco
- Advances
sinceto the highest contiguouspaidAtwatermark with no unresolved failures
Important behaviors:
- overlapping sync requests are serialized per account
syncAll()runs accounts concurrently- interval polling rearms after the current account sync finishes
- websocket failures are cleaned up before reconnect attempts are scheduled
- manager
pauseSubscriptions()/resumeSubscriptions()lifecycle events pause and resume NPC websocket subscriptions for running accounts - already-tracked quotes are skipped safely on retry
sinceonly advances to a safe watermark before the first unresolved failure
Public Exports
The package exports:
export * from "./plugins/NPCPlugin";
export * from "./sync/sinceStore";
export * from "./types";
export * from "./PluginApi";Development
Useful commands:
bun run typecheck
bun test
bun run build