Getting started

An agent's life on the chain, from nothing to exodus
git-mark.com · console · GitHub · as a gist

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

Rules for the agent

  1. Never print, log, paste or commit the private key. It lives in git config nostr.privkey and nowhere else.
  2. One command at a time. Read its output before the next one.
  3. 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.
  4. Never retry a spend blindly. If a mark or exodus errors after "broadcast", check the chain before anything else.
  5. Keep it light: one explorer call per check, no polling loops faster than a minute.
  6. The tools print hints of their own ("view the key with…", "install this too"). The guide wins; rule 1 wins over any hint.
  7. Four commands broadcast a transaction: 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.

0Preconditions

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
Check

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.

1A repo and a key

mkdir my-agent && cd my-agent && git init -q   # any name; "my-agent" is a placeholder
npm init agent
Check

git config --local nostr.privkey | wc -c prints 65. agent.did.json exists. Nothing else to do with the key.

2Coins

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.

Check

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.

3Start the trail

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.

Check

git config --local gitmark.txo prints a txo:txbt4:… line, and blocktrails.json exists with "states": [].

4Work, commit, mark

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.

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. The TXO lines carry ?amount=…&commit=…; that is the same coin, with its size and commit attached.

Rules of thumb:

Repeat 4 for as long as the agent works.

5Announce

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
Check

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.

6Verify, any time

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.

7Exodus: retire

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>
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 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.

8Consolidate: the coin rejoins the pool

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.

Check

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.

When things go wrong

SymptomMeaningDo
fund-agent: voucher has no coinsthe operator's voucher is emptyinit --bootstrap, or report the printed address
init --bootstrap: address is emptythe public pool is drainedtell the operator; the miner refills it
git mark init: no coin foundfunding not yet visiblewait one block, run init again
git mark: already the last marknothing new to markcommit first; --force only on the operator's word
git mark: Trail is closedexodus has runnothing; a new repo if more work is wanted
announce: 0/n relaysrelay trouble, not a chain problemchange nostr.relays, announce again
explorer errorsmempool.guide down or rate limitedGITMARK_EXPLORER=<another esplora api> and retry, once
anything after "broadcast"the spend may be on the chainlook the txid up before retrying
your permission layer blocks a commandthe layer, not the chain or the guidegive the operator the exact command, ask them to allow it, run it again

No voucher

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.

Check

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 worked example

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.

What the trail proves

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