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, git mark (gitmark ≥ 0.0.82). Chain used here: txbt4,
the BLAKE2b testnet4. Nothing below is mainnet.
- Never print, log, paste or commit the private key. It lives in
git config nostr.privkeyand nowhere else. - One command at a time. Read its output before the next one.
- A mark is a real Bitcoin spend and cannot be undone. Announcing is public and signed. Exodus sends the whole balance. Each of these needs the operator's word, once, before the first time it happens in a repo.
- Never retry a spend blindly. If a mark or exodus errors after "broadcast", check the chain before anything else.
- Keep it light: one explorer call per check, no polling loops faster than a minute.
node --version # 20 or newer
npm view gitmark version # 0.0.82 or newer; else: npm install -g gitmark@latest
ls ~/.gitmark/faucet.txt # the voucher file; if missing, see "No voucher" below
Check: all three print something. The voucher file is the operator's, mode 600, holding a key that spends coins.
Never read it aloud; npx fund-agent status is the only way to look at it.
mkdir my-agent && cd my-agent && git init -q
npm init agent
Check: git config --local nostr.privkey | wc -c prints 65. agent.did.json exists. Nothing else to do with
the key.
npx fund-agent
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.
Check: the output ends with === Agent funded successfully! === and a txid. Then:
npx fund-agent 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. 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.
Check: git config --local gitmark.txo prints a txo:txbt4:… line, and blocktrails.json exists with
"states": [].
git add -A && git commit -qm "what changed"
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.
Check: 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.
Rules of thumb:
- Mark commits worth proving, not every commit. 10,000 sats is about thirty marks.
git markon a commit that is already the last mark is refused. That is correct; commit first.- An unknown word (
git mark anchor,git mark commit) prints usage and exits 1. It never marks by accident. - Commit
blocktrails.jsonafter marking; it is the public trail. Or setgit config gitmark.dirty falseand let the trail live in git notes, withgit mark updatewhen a file is wanted.
Repeat 4 for as long as the agent works.
Once, when the repo has a remote worth pointing at:
git remote add origin <url> # and any mirrors: git remote add forge <url>
git push -u origin HEAD
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 and every remote as a clone tag. Relays come from
git config nostr.relays (comma or space separated), or the defaults.
Check: the output ends Announced: kind 30617 … (n/m relays) with n ≥ 1. The repo then appears at
https://play-grounds.github.io/gitmark/ within a minute, agent shown as did:nostr:<hex>.
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.
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 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>
Check: 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. The agent's addresses hold no UTXOs.
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.
| Symptom | Meaning | Do |
|---|---|---|
fund-agent : voucher has no coins |
the operator's voucher is empty | stop, report the printed address |
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 |
The agent cannot make coins. If ~/.gitmark/faucet.txt is missing, npx fund-agent init creates a key and prints
an address; the operator sends testnet coins there (a faucet, a wallet), and npx fund-agent status shows them.
Then step 2.
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.
References: gitmark https://github.com/solidpayorg/gitmark (SKILL.md, BURN.md), fund-agent https://github.com/blocktrails/fund-agent, create-agent https://init-agent.com/SKILL.md, the console https://play-grounds.github.io/gitmark/, git-mark.com.