# An agent's life on the chain: init to exodus (gitmark workflow for LLM agents)

> Source: <https://gist.github.com/melvincarvalho/69bc7991687d4182cb4de0090b5d16d9>
> Published: 2026-10-18 15:56:41+00:00

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.

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.

```
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 mark` on 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.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.

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/](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](https://github.com/solidpayorg/gitmark) (SKILL.md, BURN.md), fund-agent
[https://github.com/blocktrails/fund-agent](https://github.com/blocktrails/fund-agent), create-agent [https://init-agent.com/SKILL.md](https://init-agent.com/SKILL.md), the console
[https://play-grounds.github.io/gitmark/](https://play-grounds.github.io/gitmark/), git-mark.com.
