A workflow for an LLM agent, or a person driving one. The agent gets a key, gets coins, anchors its commits to Bitcoin, announces the repo on Nostr, and when it is done hands the coin back and closes the trail. Every step is one command; every step has a check. Do the steps in order and stop at the first check that fails.
Tools: npm init agent (create-agent), npx fund-agent@latest (0.0.10 or newer; always @latest, because npx keeps an old copy around),
git mark (gitmark 0.0.82 or newer). Chain used here: txbt4, the BLAKE2b testnet4.
Nothing below is mainnet.
Rules · 0 Preconditions · 1 Key · 2 Coins · 3 Trail · 4 Mark · 5 Announce · 6 Verify · 7 Exodus · 8 Consolidate · When things go wrong · No voucher
git config nostr.privkey and nowhere else.init --bootstrap, fund-agent, git mark and
git mark exodus, plus consolidate. If you run inside a permission layer, it may block them as
real-world transactions. A block is not the end of the guide: it is the operator's to lift. Tell them the exact command
that was blocked and ask them to allow it, then run it again. In Claude Code that is approving the prompt, or an allow
rule for Bash(npx fund-agent@latest *) and Bash(git mark *), or leaving auto mode. What you
must not do is route around it: no other tool, no hand-built transaction, no copying the key elsewhere.node --version # 20 or newer git mark --version # 0.0.82 or newer; else: npm install -g gitmark@latest, or use npx gitmark in place of git mark npx fund-agent@latest --version # 0.0.10 or newer; this is what you will run, not what the registry lists export FUND_AGENT_FAUCET=~/.gitmark/faucet.txt # or the path the operator gave you; export it once, in the shell you keep ls "$FUND_AGENT_FAUCET" # the voucher file; if missing, see "No voucher" below
All four print something. The voucher file is the operator's, mode 600, holding a key that spends coins. Every
fund-agent command reads FUND_AGENT_FAUCET, so export it once rather than prefixing each command; a prefix
does not reach a default written as ${VAR:-…}. The first npx run prints an "npm warn exec …
will be installed" line first; that is normal. Never read the file aloud; npx fund-agent@latest status
is the only way to look at it. If it is missing, do No voucher now, then come back to step 1.
mkdir my-agent && cd my-agent && git init -q # any name; "my-agent" is a placeholder npm init agent
git config --local nostr.privkey | wc -c prints 65. agent.did.json exists.
Nothing else to do with the key.
npx fund-agent@latest
What happens: 10,000 sats go from the voucher to the agent's key; the change goes back to the voucher; the coin
is written to .well-known/txo/txo.json.
The output contains Transaction broadcast: <txid> and ends with === Agent funded successfully! ===. Then:
npx fund-agent@latest balance # Address: tb1p…, one coin of 10,000 sats (unconfirmed is fine)
If it says the voucher has no coins, stop and tell the operator the address it printed, or see No voucher. Do not look for coins elsewhere.
git mark init --chain txbt4
What happens: gitmark finds the coin sitting at the key's base address and adopts it. No spend yet. An unconfirmed coin is fine; the whole loop runs on unconfirmed coins, consolidate included from fund-agent 0.0.10.
git config --local gitmark.txo prints a txo:txbt4:… line, and
blocktrails.json exists with "states": [].
git add -A && git commit -qm "what changed" # in a fresh repo the first commit is the three generated files; that is fine git mark
What happens: one transaction, 300 sat fee, spending the trail coin to an address derived from this commit.
Output: Marked: <commit> → <txid>, the new balance, the new TXO.
git mark info shows Marks: 1, Last commit: is HEAD, Balance:
is 300 less than before. Status reads unconfirmed until the next block; that is normal on a testnet with few
blocks. The TXO lines carry ?amount=…&commit=…; that is the same coin, with its size and commit attached.
Rules of thumb:
git mark on a commit that is already the last mark is refused. That is correct; commit first.git mark anchor, git mark commit) prints usage and exits 1. It never marks by accident.blocktrails.json after marking; it is the public trail. Or set
git config gitmark.dirty false and let the trail live in git notes, with git mark update
when a file is wanted.Repeat 4 for as long as the agent works.
Any time after the first mark. No remote is needed:
git mark announce
What happens: a NIP-34 repository announcement (kind 30617), signed with the agent's key, carrying the latest
mark as chain, txo and commit tags. Relays come from
git config nostr.relays (comma or space separated), or the defaults. The event is replaceable, so
announcing again later simply updates it.
If the repo has a remote, or gets one later, announce again and every remote becomes a clone tag, so
readers know where to fetch from. A remote is somewhere the operator can push to; where it lives is their question,
so ask for the URL and do not create a repository on a hosting service without being told to:
git remote add origin <url> # and any mirrors: git remote add forge <url> git push -u origin HEAD git mark announce # again; now with clone tags
The output ends Announced: kind 30617 … (n/m relays) with n at least 1. That line is the check.
The repo then appears in the console within a minute, agent shown
as did:nostr:<hex>; the console is a browser page for people, not something you need to open.
If n is 0: the relays refused or did not answer. Set git config nostr.relays to relays known to work
and run git mark announce again. Nothing was spent. A missing remote is never the reason; do not wait
for one.
From here, git mark --announce marks and announces in one, or git config gitmark.announce true
makes that the default.
git mark verify # every mark against the chain git mark info # balance, marks, closed or not
Anyone with a clone can run these; they need no key.
When the agent is finished, the coin goes back to the funder and the trail is closed.
npx fund-agent@latest status # Address: tb1p… — the funder's address git mark exodus <that address> --dry
Read the dry run: Spend: is the current TXO, To: is the funder's address,
Send: is the balance less 300. Raw: is the transaction. Nothing was broadcast. Then:
git mark exodus <that address>
Output ends Trail closed: git mark will not mark again. Then git mark info shows
Closed: exodus … and Balance: 0 sats, and git config --local gitmark.txo prints
nothing and exits 1, which is git saying the key is unset, not a failure. npx fund-agent@latest balance shows
no coins at the base address and the trail line says the coin is spent. Commit blocktrails.json; it now carries an
exodus field, the public record that the trail ended.
After exodus, git mark refuses. That is the point. A new trail means a new repo and a new key.
The returned coin sits at the funder's address as a UTXO of its own, which the voucher file does not know about. In any directory, straight away; unconfirmed coins are merged too:
npx fund-agent@latest consolidate --dry-run # the coins it would merge, the fee, the raw transaction npx fund-agent@latest consolidate # one coin at the voucher address; the file now points at it
What happens: every coin at the voucher address is spent in one transaction back to the voucher key, fee 1,000 sats plus 100 per extra input, and the merged coin becomes the voucher.
The output ends Voucher file updated: and npx fund-agent@latest status shows one balance that
includes the returned sats. Refusals are all safe: "nothing to merge" means there is one coin already, "not worth
the fee" means the pile is under one agent's funding.
This closes the loop: the sats that funded the agent are back where the next agent is funded from.
| Symptom | Meaning | Do |
|---|---|---|
fund-agent: voucher has no coins | the operator's voucher is empty | init --bootstrap, or report the printed address |
init --bootstrap: address is empty | the public pool is drained | tell the operator; the miner refills it |
git mark init: no coin found | funding not yet visible | wait one block, run init again |
git mark: already the last mark | nothing new to mark | commit first; --force only on the operator's word |
git mark: Trail is closed | exodus has run | nothing; a new repo if more work is wanted |
announce: 0/n relays | relay trouble, not a chain problem | change nostr.relays, announce again |
| explorer errors | mempool.guide down or rate limited | GITMARK_EXPLORER=<another esplora api> and retry, once |
| anything after "broadcast" | the spend may be on the chain | look the txid up before retrying |
| your permission layer blocks a command | the layer, not the chain or the guide | give the operator the exact command, ask them to allow it, run it again |
The agent cannot make coins, but on txbt4 it can take one. A public bootstrap address holds coins under a key
everyone knows, sha256 of the text fund-agent bootstrap:
tb1pqz7yl29ke4hdsj6t2lpae0lkln0turndjckss5fx39d0fd8m0vmse7f7g7
npx fund-agent@latest init --bootstrap --dry-run # a coin it could take, the raw transaction npx fund-agent@latest init --bootstrap # one million sats into a fresh key of your own, now your voucher file
The pick is random on every run, so the real run may take a different coin from the dry run; the amounts are the same.
npx fund-agent@latest status shows about 1,000,000 sats, enough for ninety agents. Take one coin, not more.
If it says the address is empty, tell the operator; whoever mines refills it. Then step 1.
If init says the voucher file already holds a key: with fund-agent 0.0.9 or newer the bootstrap coin goes into
that key, nothing to do. On 0.0.8, check status shows 0 sats, then add --force; never delete
the file by hand.
Without the bootstrap (another chain, or it is empty), npx fund-agent@latest init creates a key and prints an
address; the operator sends testnet coins there, and npx fund-agent@latest status shows them.
A fresh agent ran this page on 18 October 2026 with nothing but the URL and an empty directory, and got to the end with no question for the operator. The lines that matter, txids shortened, keys left out:
$ export FUND_AGENT_FAUCET=~/agent/faucet.txt
$ npx fund-agent@latest init --bootstrap
Bootstrap: tb1pqz7yl29ke4hdsj6t2lpae0lkln0turndjckss5fx39d0fd8m0vmse7f7g7 (txbt4, public key, anyone may take a coin)
Taking: 1,001,000 sats txo:txbt4:33f5fbab…:0
Fee: 1,000 sats
Yours: 1,000,000 sats, to a new key
Transaction broadcast: 67ed4ca8…
Voucher written to /home/you/agent/faucet.txt
Balance: 1,000,000 sats at 67ed4ca8…:0
Funds: 90 agents at 10,000 sats and one smaller
$ mkdir my-agent && cd my-agent && git init -q && npm init agent
$ npx fund-agent@latest
Transaction plan:
Input: 1,000,000 sats (from voucher)
Output: 10,000 sats (to you)
Change: 989,000 sats (back to voucher)
Fee: 1,000 sats
Transaction broadcast: 8febd1d2…
=== Agent funded successfully! ===
$ git mark init --chain txbt4
Funded: 10000 sats (txid: 8febd1d2…)
Initialized gitmark trail: blocktrails.json
$ git add -A && git commit -qm "identity" && git mark
Marked: ce37c7f3 → 6bed3057…
Balance: 9700 sats
TXO: txo:txbt4:6bed3057…:0?commit=ce37c7f3…
$ git mark announce
✓ wss://nostr.oxtr.dev
✓ wss://relay.primal.net
✓ wss://relay.snort.social
Announced: kind 30617 6dab738a… (3/3 relays)
$ git mark verify
Verifying 2 mark(s)...
[0] OK — ce37c7f3 → tb1pp0ccxe8…
[1] OK — e28ff044 → tb1pw8f0tu8…
All marks verified.
$ git mark exodus tb1phkzv6jd… --dry
Spend: txo:txbt4:2c19d965…:0 (9400 sats)
To: tb1phkzv6jd…
Send: 9100 sats (fee 300)
Dry run: nothing broadcast.
$ git mark exodus tb1phkzv6jd…
Exodus: ef9042bc…
Trail closed: git mark will not mark again.
$ npx fund-agent@latest consolidate # the run waited a block; from 0.0.10 it need not
Merging 2 coins:
989,000 sats txo:txbt4:8febd1d2…:1
9,100 sats txo:txbt4:ef9042bc…:0
Total: 998,100 sats
Fee: 1,100 sats
Output: 997,000 sats back to the voucher address
Transaction broadcast: 18ce062d…
Voucher file updated: /home/you/agent/faucet.txt
Six transactions, 4,000 sats spent in fees and marks, and a faucet that still funds ninety agents.
Each mark spends the previous coin to an address derived from the commit hash, so the chain holds an ordered,
timestamped, unforgeable sequence of this agent's commits, verifiable from blocktrails.json alone.
Exodus ends the sequence in public: no coin, no further marks under this key. Consolidate returns the sats to
the pool, so a funder's balance is a running count of agents at work.
gitmark (SKILL.md, BURN.md) · fund-agent · create-agent · the console