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

fund-agent

v0.0.15

Published

Initialize git config nostr.privkey with funds from a voucher

Downloads

1,132

Readme

fund-agent

Initialize git config nostr.privkey with funds from a TXO voucher.

Install

npm install -g fund-agent

Or use directly with npx:

npx fund-agent

Usage

Setting up, once

npx fund-agent init

This writes a new voucher key to ~/.gitmark/faucet.txt and prints an address. Send coins there (default chain txbt4; --chain xbt or any id from the table below for another). From then on, in any repo:

npx fund-agent

The first run after the coins arrive finds them through the chain's explorer and fills in the outpoint; every run pays 10,000 sats to a fresh agent key and writes the change back to the file. npx fund-agent status shows the address, the balance and how many agents it still funds. In a funded repo, npx fund-agent balance shows what the agent's own key holds, and after git mark the trail's current coin too.

When an agent retires, git mark exodus sends its coin back to the voucher address, where it sits as a separate UTXO. npx fund-agent consolidate merges every coin there, unconfirmed included, into one and makes that the voucher, so returned coins fund agents again. --dry-run prints the plan and the raw transaction. On the Core-signed chains (tbtc4, btc) the spend is BIP341, which is also valid on the BLAKE2b twin whenever every input exists there; consolidate checks the twin first and refuses such a spend unless you pass --force.

If someone hands you a voucher string instead, store it with npx fund-agent init "txo:…".

No coins at all? On the test chains there is a public bootstrap address, funded by whoever mines, whose key everyone knows: npx fund-agent init --bootstrap takes one coin from it, one million sats, into a fresh key of your own and writes that as your voucher file. The key is sha256 of the text fund-agent bootstrap, so it is no secret and anyone can empty the address; on a chain worth nothing that is the point. txbt4 address: tb1pqz7yl29ke4hdsj6t2lpae0lkln0turndjckss5fx39d0fd8m0vmse7f7g7. Refused on mainnet chains. If the voucher file already holds a key that is waiting with nothing at its address, the coin goes into that key; --force is only for replacing a funded one. init --dry-run writes nothing on any path. init and status say when a newer fund-agent is on npm, because npx fund-agent can run a cached old copy; FUND_AGENT_NO_UPDATE_CHECK=1 skips that lookup. FUND_AGENT_FAUCET=<path> moves the file.

Swapping between chains

A swap pool holds the same key, and so the same address, on two chains. Send coins to that address on one chain and the pool pays the same amount, less its fee, to the sending address on the other. One to one, no price. npx fund-agent swap 50000 --to tbtc4 --pool <address> sends from this repo's agent key on its own chain (gitmark.network, or --chain) and tells you the coin will arrive on tbtc4 at the same address, since one key spells one address on both chains. `git config swap.pool

To run a pool: npx swap-pool --key-file ~/.gitmark/swap-pool.txt --chains txbt4,tbtc4 --ui 3480 polls both sides, pays what arrives, and serves a page with a Swap tab (which shows the command to run) and a Pool tab (reserves, ledger, refusals) plus /pool.json. Defaults: fee 1,000 sats, cap 1,000,000 per swap, 10 swaps per key per day; --reserve addr,addr names top-up addresses that are never paid back; --once and --dry-run for a look; --on-change '<command>' runs a command after any pass that paid or refused something, the ledger's publish script say, and the next pass waits for it.

A swap is announced as well as sent. After the broadcast, fund-agent swap publishes a kind 30619 event to the relays in git config nostr.relays (or the defaults): d = the deposit outpoint <chain>:<txid>:<vout>, p = the pool's key, chain, to, amount, optionally tx with the raw deposit. The pool subscribes with #p = its own key (--relays, or --no-nostr), wakes when a request arrives, and pays on that pass instead of the next poll; it still pays only for a coin it finds on the chain, and the return address is still the deposit's first input, never the event's key. It answers from its own key with the same d: paid = payout txid, or refused = the reason. A request that reaches no relay costs nothing: the poll finds the deposit anyway. Talking to relays uses Node's own WebSocket, so it needs Node 22 or newer; on an older Node the swap command says so and the pool runs as if --no-nostr were given. The pool never pays from a coin that also exists on the other chain, and a deposit that is itself a replay is paid once, so the two sides can never be made to pay twice.

CLI

# Initialize from default faucet (~/.gitmark/faucet.txt)
npx fund-agent

# Set up the voucher file: new key and an address to send coins to
npx fund-agent init
npx fund-agent init --chain xbt

# Store a voucher string in the voucher file
npx fund-agent init "txo:txbt4:abc123:0?amount=5000000&key=deadbeef..."

# Address, balance, agents left
npx fund-agent status

# What this repo's agent key holds (chain from git config gitmark.network, or --chain)
npx fund-agent balance

# Start a faucet of your own from the public bootstrap address (test chains; --dry-run to look first)
npx fund-agent init --bootstrap

# Merge the returned coins at the voucher address into one (--dry-run to look first)
npx fund-agent consolidate

# Send 50,000 sats to a swap pool; 49,000 arrives at the same address on tbtc4
npx fund-agent swap 50000 --to tbtc4 --pool <address>

# Initialize from voucher file
npx fund-agent /path/to/voucher.txt

# Initialize from voucher URI
npx fund-agent "txo:tbtc4:abc123:0?amount=5000000&key=deadbeef..."

# A key already in git config (from npm init agent) is funded as is;
# --force replaces it with a new one
npx fund-agent

# Generate key only (no funding)
npx fund-agent --no-fund

# Use global git config
npx fund-agent --global

# Dry run (show plan without broadcasting)
npx fund-agent --dry-run

The CLI is a thin layer over the library: resolveVoucher, resolveKey, planFunding and fund are the steps, and fundAgent runs them in one call.

Programmatic

import { fundAgent, generateKeypair, parseVoucher } from 'fund-agent';

// Fund from voucher
const result = await fundAgent({
  voucher: '~/.gitmark/faucet.txt',
  global: true,
});

console.log(result.publicKey);  // Your new public key
console.log(result.txid);       // Funding transaction ID

// Generate keypair only
const { privateKey, publicKey } = generateKeypair();

// Parse a voucher URI
const voucher = parseVoucher('txo:tbtc4:abc:0?amount=1000000&key=xyz');
console.log(voucher.amount);  // 1000000

API

fundAgent(options)

Fund an agent wallet from a voucher.

Options:

  • voucher - Voucher URI or file path
  • global - Use global git config (default: false)
  • force - Overwrite existing key (default: false)

Returns:

  • privateKey - Generated private key
  • publicKey - Derived public key
  • txid - Funding transaction ID
  • amount - Funded amount in satoshis
  • funded - Whether funding was performed

generateKeypair()

Generate a new secp256k1 keypair.

parseVoucher(uri)

Parse a TXO voucher URI.

createVoucher(options)

Create a TXO voucher URI.

gitConfig(key, value, global)

Get or set git config values.

planFunding(voucher)

The amounts a parsed voucher yields: userAmount, changeAmount, fee, and the chain entry.

explorerUrl(chain, txid)

The explorer link for a transaction on that chain.

createFaucet({ chain, privateKey })

A voucher key waiting for coins: { uri, chain, privateKey, publicKey, address, funded: false }. The uri form is txo:<chain>?key=<privkey>.

parseFaucet(uri)

Parses either form. A funded voucher comes back as from parseVoucher plus publicKey, address and funded: true.

refreshVoucher({ uri, file }, { fetch })

For a waiting voucher, asks the chain's explorer for UTXOs at its address, takes the largest (confirmed first), rewrites the file with the full voucher and returns { voucher, utxos }.

balanceOf(chain, pubkey, { fetch })

Returns { chain, address, utxos, confirmed, pending, total } for a key: its coins from the chain's explorer, confirmed first.

trailTxo(), parseTxo(uri), lookupOutput(chain, { txid, vout }, { fetch })

The trail coin git mark keeps in git config gitmark.txo, the plain txo: parser it needs, and one output looked up on the explorer: { address, amount, confirmed, spent }.

bootstrap({ chain, voucherFile, dryRun, fetch, random, privateKey }), bootstrapFaucet(chain), planBootstrap(utxos), BOOTSTRAP_KEY, BOOTSTRAP_COIN

One confirmed coin at the public bootstrap address, picked at random, spent whole (less 1,000 sats) to a fresh key that becomes the voucher file. BOOTSTRAP_COIN (1,001,000 sats) is what a loader puts in each coin. Mainnet chains throw.

latestVersion({ fetch, timeoutMs }), isNewer(current, latest), canAdoptForBootstrap(voucher, utxos)

The newest version on npm or null, never throwing; an x.y.z comparison; and whether a voucher key is waiting with nothing at its address, so a bootstrap can land in it.

consolidate({ voucher, voucherFile, dryRun, fetch }), planConsolidation(chain, utxos), buildConsolidationTx({ voucher, utxos })

Every confirmed coin at the voucher address, largest first, into one output back to the voucher key, fee consolidationFee(n) = 1,000 sats plus 100 per extra input. Unconfirmed coins are merged too. Refuses with fewer than two coins or when the result would be under 10,000 sats. Unified sighash on the BLAKE2b chains, BIP341 key-path on tbtc4 and btc. The merged coin is written to the voucher file.

addressOf(chain, pubkey), lookupUtxos(chain, address), agentsFrom(sats)

Raw-key taproot address for a chain, the explorer UTXO lookup, and how many agents an amount funds.

CHAINS

The chain table: name, broadcast endpoint, explorer, and which sighash a spend needs.

Voucher Format

txo:<chain>:<txid>:<vout>?amount=<sats>&key=<privkey>

Example:

txo:tbtc4:abc123def456:0?amount=5000000&key=0123456789abcdef...

planSwaps, poolPass, buildPayment, senderOf (lib/pool.js), poolSnapshot, renderPage, serveUi (lib/pool-ui.js)

The swap pool: the planner is pure (planSwaps({ chain, incoming, poolAddress, state, limits, now }) returns payouts, refusals and the per-key daily count), poolPass does one round over both chains, and the UI module renders the page and serves it.

Files Created

  • git config nostr.privkey - Your private key
  • .well-known/txo/txo.json - Your TXO (spendable output)

Chains

| id | chain | explorer API, broadcast and links | signing | |---|---|---|---| | tbtc4 | Bitcoin testnet4 (default) | mempool.space | BIP341 key path | | tbtc3 | Bitcoin testnet3 | mempool.space | BIP341 key path | | signet | Bitcoin signet | mempool.space | BIP341 key path | | btc | Bitcoin mainnet | mempool.space | BIP341 key path | | txbt4 | Bitcoin BLAKE2b testnet4 | mempool.guide | unified sighash (0x21), replay-protected | | xbt | Bitcoin BLAKE2b mainnet | mempool.guide | unified sighash (0x21), replay-protected |

The SHA256d chains and the BLAKE2b chains live on different explorers, so the chain in the voucher decides where the transaction is posted and where the explorer link points. A voucher on a BLAKE2b chain is signed with the unified opt-in sighash from Knots (lib/unified.js, checked against the 166 Knots vectors in test/unified_sighash.json), so the spend is valid only on that chain. buildFundingTx returns the signed hex without broadcasting, if you want to inspect it first.

Related

License

MIT