GitBCH Docs
A practical reference for building on GitBCH. How to connect a wallet, mint a Repo, post a CashBit, read on-chain content, and call the server’s HTTP API.
For protocol thesis and on-chain receipts, see the whitepaper.
Quick start
Connect a wallet, create a Repo, mint a CashBit, view it on the resolver.
Prerequisites
- A CashTokens-capable Bitcoin Cash wallet. CashNect is the reference handshake; any wallet that exposes a CashNect-compatible signing surface works.
- A small amount of BCH for fees. A typical Repo + first CashBit transaction lands around 600 bytes — under $0.01 at typical BCH prices.
- Modern browser. The dashboard runs entirely client-side for the signing flows; the server is read-only and indexing-only.
Steps
- On the homepage, click CashNect Wallet in the top-right nav. Approve the connection in your wallet UI.
- Open the Dashboard. In the sidebar, click + New Repo. Enter a repo name and an optional README. The flow signs and broadcasts a single transaction containing the minting NFT, the README CashBit, the GCP-1 content chunks, and the BCMR-v2 anchor.
- When the broadcast confirms, the new Repo appears in your sidebar with its category. The Dashboard polls the server’s /api/repo/:category endpoint to display files.
- Click into the Repo, then + New CashBit to add a file. Enter the path and contents; sign the mint. The new CashBit appears under the Repo.
- Open the read URL with your CashBit’s TXID (/read.html?txid=<your-cashbit-txid>), or click the CashBit row from the Dashboard. The resolver renders the file: parent Repo, file path, content, content-hash verification, owner address.
Everything past step 1 is optional. A read-only consumer can skip the wallet entirely and use the API to fetch any Repo, CashBit, Reply, or Issue by its TXID or category.
Concepts
Short definitions. Each entry maps to a token type or a system role. For full specification depth, see whitepaper §2 and §4.
- Repo. A minting NFT (type tag 0x00) whose holder is the project owner. Its category is the namespace under which all child commits are minted.
- CashBit. A mutable child NFT (type tag 0x01) under a Repo’s category. Its commitment encodes the file path hash and content hash. A file edit is a spend that mints a new CashBit.
- Reply. A mutable NFT (type tag 0x03) representing a single message in a discussion thread. Cheap to mint, scoped to a parent reference.
- Thread. A minting NFT (type tag 0x04) representing an Issue with a state machine: Open, In Progress, Resolved, Closed. State transitions are mints under the Thread’s category.
- PR. A CashBit minted under a fork’s category with BCMR metadata flag type:'pr'. Owner merge is a re-mint into the target Repo’s category. No new tag.
- Bounty. A CashScript covenant (not an NFT) with a time-locked cancellation. Lives under an Issue.
- Identity. A transferable mutable NFT (type tag 0x02) carrying a contributor profile. Holdership is the authority, not the address. Spec’d, not yet deployed — v1 uses Path-A profiles.
- BCMR. The Bitcoin Cash Metadata Registry, v2. A JSON registry whose hash is committed on chain alongside the mint. Maps logical paths to content hashes, lineage, and metadata.
- ChunkDrip. A CashScript covenant that publishes raw GCP-1 OP_RETURN bytes paid for by anonymous QR. Used in V1 and still available for unattributed posts.
- CashNect. The wallet-handshake surface used for any flow that requires a signature: genesis, mint, edit, bounty post, Issue state change, PR merge.
- Sign-only signing. The wallet’s contract on the CashNect sign_transaction path: derive the signature, return the signed bytes via the relay, do not broadcast. Broadcast is the dApp’s responsibility.
- Server-side TX builder. An endpoint family at /api/wallet/:addr/build-* that constructs unsigned mint transactions on the server using libauth. Replaces browser-side mainnet-js for the mint flow, so chain-query failures in the browser cannot stall the user.
- Broadcast endpoint. POST /api/broadcast. Accepts a signed TX hex from the dApp and submits it through the server’s TCP Fulcrum proxy.
Token commitment reference
Every GitBCH NFT is identified by the first byte of its commitment: the type tag. The remaining bytes pack the references the token needs. Resolvers dispatch on a single byte; new tokens can be added without colliding.
Commitment layouts
0x00 · Repo
Capability: minting. Authorizes future child mints under this category indefinitely. Holder is the project owner.
0x01 · CashBit (File)
Capability: mutable. Editing the file is a spend that produces a new mutable NFT under the same category, preserving lineage. Total commitment is 80 hex chars.
0x02 · Identity (spec’d, not yet deployed)
Capability: mutable. A transferable contributor profile. The NFT carries the profile, not the address. Commitment reserves bytes for a recovery scheme reference. This token type is specified but not yet live on chain; v1 profiles use the Path-A record (see the Profiles endpoint), with a migration pointer reserved for when Identity ships.
0x03 · Reply
Capability: mutable. A single message in a discussion. The commitment carries a reference to the parent (a Repo, a Thread, or another Reply). Edits are spends that mint a new Reply under the same category.
0x04 · Thread (Issue)
Capability: minting. Each Thread is its own minting authority; state transitions (Open → In Progress → Resolved → Closed) are mints under the Thread’s category, with the new state encoded in the child commitment.
0x05 · Bounty
Not an NFT — a CashScript covenant. Source: contracts/BountyEscrow.cash. Parameterized by (projectOwnerPkh, serviceFeePkh); the P2SH-20 contract address is deterministic from those parameters. The covenant exposes five functions:
- postBounty(ownerPk, ownerSig, bountyHash) — owner sends BCH to the contract and commits the description hash via an OP_RETURN output. Re-locks the value (minus a 1,000-sat miner fee) to the same contract.
- claimBounty(ownerPk, ownerSig, contributorPk, contributorSig) — owner co-signs a contributor’s claim. Funds stay locked in the same contract.
- releaseBounty(ownerPk, ownerSig, contributorPkh) — owner approves payout. Covenant enforces output 0 = contributor P2PKH (99%), output 1 = service-fee P2PKH (1%), with the 1% floored at 546 sats. Miner fee is 1,000 sats; the contract self-funds it.
- cancelBounty(ownerPk, ownerSig) — owner reclaims funds. Time-locked: this.age >= 144 blocks (~1 day).
- disputeBounty(ownerPk, ownerSig, arbiterPk, arbiterSig, recipientPkh, recipientAmount) — a chosen arbiter co-signs with the owner to resolve a dispute. Arbiter is not hardcoded; both signatures are required, preventing unilateral arbiter theft. The 1% service fee still applies.
The release transaction spends the contract input via cashscript’s TransactionBuilder.generateWcTransactionObject({}) per CHIP-2024-09 / BCH-WalletConnect. The contract input’s sourceOutputs[i].contract hint carries { abiFunction, redeemScript, artifact } so the signing wallet can re-derive the unlock script with the owner’s real pubkey and signature. Inputs are Schnorr-signed with sighash 0x61 (SIGHASH_ALL | SIGHASH_UTXOS | FORKID).
Releasing a bounty therefore requires a wallet that supports BCH-WalletConnect contract-input signing — not just P2PKH signing. The funding transaction itself is a plain P2PKH send to the contract address and works from any BCH wallet.
Reference test vector for the release path: contract bitcoincash:prdnv2rrfahuelfptarv5nxkyym9599hzyqe4t6uth released at TXID 42639ef2d86835e186c69b854fa9e28668752cd384ba38208e26f8e030d43410 (101,572 sats funded, two-output release).
0x07 · Attestation
Capability: mutable. A signed claim by a verifier about a CashBit (build passed, test ran, dependency pinned). Pre-Layla these are signature-trust; post-Layla they become CashVM-checkable.
API reference
The GitBCH server exposes a read-mostly HTTP API for indexing and resolving on-chain content. Endpoints below are enumerated from server.mjs; the wallet-side flows that write to chain are not server endpoints — they are CashNect-signed transactions broadcast directly.
All responses are JSON. Successful reads include an Access-Control-Allow-Origin: * header for browser callers.
Repos
List all known Repos indexed by the server. Server-side filtering excludes BCMR files whose registryIdentity.description or name identifies them as non-Repo token types (Issue, Reply, Thread NFT, Reply NFT, Comment NFT, or any type 0x0[1-9a-f]) — the Repo and non-Repo BCMRs share the same on-disk layout, so the discriminator is applied at read time.
[
{ "category": "ea4da7be92ba65cf...", "name": "hello bch", "owner": "bitcoincash:q...", "fileCount": 3 },
{ "category": "f5c05e9acc2e603e...", "name": "Test-1", "owner": "bitcoincash:q...", "fileCount": 1 }
]
Full Repo manifest: BCMR registry, file list, owner, genesis TXID.
Resolve a single file by path. Returns content, current CashBit TXID, and content-hash.
List addresses that have minted any CashBit under this Repo’s category.
CashBits
Full CashBit resolver. Decodes the child mutable NFT’s 80-hex commitment, looks up the parent Repo from the on-disk BCMR by category, matches the file by sha256(path)[0..40], reports a contentHashMatches boolean. Resolves content via three sources in order: chunks-with-mint sentinel, multi-TX ChunkDrip reassembly, or inline BCMR fallback.
{
"txid": "2d0f5f80...",
"category": "f5c05e9a...",
"repoName": "Test-1",
"filePath": "src/hello.js",
"owner": "bitcoincash:q...",
"content": "console.log('hello');",
"contentSource": "chunks-with-mint",
"contentHashMatches": true
}
CashBits created by a given wallet. Walks the address’s outbound TXs, identifies P2SH ChunkDrip contracts paid by it, then collects GCP-1 OP_RETURN TXs from those contracts. Capped at 50 contracts per request.
CashBits currently held by a wallet (NFT-balance view), as opposed to those it created.
Issues (Thread NFTs)
Index a freshly-broadcast Thread NFT. The wallet supplies the genesis TXID; the server independently fetches and verifies the commitment matches type tag 0x04 before writing the sidecar entry.
Resolve a single Issue: title, body, current state, parent Repo category, history of state transitions.
Index a state-transition mint (Open → In Progress → Resolved → Closed). Body carries the new state and the transition mint TXID.
All Issues under a Repo, with their current states.
Pull Requests
Index a new PR. A PR is a CashBit (0x01) under a fork’s category with BCMR type:'pr' — this endpoint records the cross-reference to the target Repo.
PR detail: source fork, target Repo, file diff list, current state.
Index a merge: the wallet has re-minted the PR’s CashBit under the target Repo’s category, signed by the target’s minting-NFT holder. Merge TXID is supplied; the server verifies authority before writing.
Index a PR close (no merge). Closure is by Repo owner.
All PRs targeting this Repo.
Bounties
Bounty contract instances are registered server-side at post time and the registry is enriched at read time against Fulcrum to reflect live funding state.
Register a newly posted bounty. Body: { contractAddress, title, description, repoCategory, amountSats, ownerAddress, bountyHash }. Idempotent by contractAddress.
List registered bounties enriched with Fulcrum-derived UTXO state. Each entry reports awaiting funding, funded, or released; entries with a recorded releaseTxid are pinned to released regardless of UTXO state.
Record an Approve & Release event after the dApp broadcasts the unlock TX. Body: { releaseTxid, contributorAddress, releasedSats }. releasedSats is the on-chain UTXO total at the moment of release and is used by /api/highest-earner to credit the contributor with the real payout (floor((releasedSats − 1000) × 99 / 100)) rather than the staged amount.
Replies (chat)
Index a Reply NFT mint under a Repo or Thread. Body carries reply TXID and parent reference.
Reply feed for a Repo or Thread, oldest-first.
Index a freshly-minted child CashBit under a known Repo. Server independently fetches the TX and verifies parent category before writing.
BCMR registry
Read the BCMR-v2 registry JSON for a Repo. Maps logical file paths to content hashes, lineage TXIDs, and metadata.
Update an indexed BCMR. The hash anchored on chain is the source of truth; this endpoint refreshes the local fast-lookup copy.
Tx resolver
Resolve the CashTokens category for a genesis broadcast. Per CashTokens spec, the category equals vin[0].txid of the broadcast, not the broadcast TX hash itself. Used to backfill repos minted before clean recording.
{
"broadcastTxid": "29f7b5f3...",
"category": "ea4da7be92ba65cf...",
"prevoutVout": 0,
"isGenesis": true,
"commitment": "00..."
}
Universal read endpoint. Input is either a 64-hex TXID (decodes the OP_RETURN GCP-1 chunk for that TX) or a BCH address (returns its TX history). Powers read.html.
Profiles
Path-A profile lookup for an address: handle, bio, links, contributions index. Profile JSON may carry avatarCashBitTxid (64-hex) and avatarMime (whitelisted MIME); the profile renderer fetches /api/cashbit/:txid for that pointer and renders an <img> with a data: URL, falling back to an identicon. SVG payloads are sanitized (scripts, event handlers, external href / xlink:href, and javascript: URLs are stripped). Binary CashBit round-trip through /api/cashbit/:txid is currently UTF-8 lossy, so binary-format avatars (PNG/JPEG/WebP/GIF) may not render across browsers until the resolver emits base64 alongside a content type. Will be superseded by Identity NFT (0x02) lookups; the never-delete migration leaves a migratedTo pointer on the Path-A record.
Update a Path-A profile. Auth is by wallet signature over the request body. Partial updates are preserved: fields omitted in the request are not wiped, so an avatar-only PUT does not clear displayName/bio/links. avatarCashBitTxid accepts a 64-hex CashBit TXID or null; avatarMime must be in the server-side MIME whitelist.
Leaderboards
Wallets ranked by total BCH paid out to GitBCH counterparties. Combines claimed Path-A tip-intent senders and bounty-release owners. Each row carries a tips · bounties · total breakdown.
Per-recipient aggregate of inbound BCH. Tip column reads from tip-intents.json (self-tips excluded). Bounty column folds released bounties from bounties.json: the contributor is credited with floor((releasedSats − 1000) × 99 / 100), using releasedSats recorded at release time and falling back to the staged amountSats on pre-2026-05-10 records.
Senders ranked by tip volume on Path A.
ChunkDrip
Create a new ChunkDrip session: a CashScript covenant address derived for a planned multi-chunk content publish. Returns a session id and the funding QR.
Poll a ChunkDrip session: how many chunks funded, how many published, current cost basis.
Submit a chunk for the next OP_RETURN broadcast under this session’s covenant.
List all known ChunkDrip sessions on the server.
Server-side TX building & broadcast
The server builds unsigned transactions on behalf of the dApp using libauth, returns the bytes for the wallet to sign over CashNect, then broadcasts the signed bytes via its own Fulcrum proxy. The browser never queries the chain on the mint path. See whitepaper Pattern F.
Browser-side cashscript calls (used by the bounty release builder) initialize ElectrumNetworkProvider with hostname bch.loping.net. A local Fulcrum on top of BCHN is the intended sovereign replacement.
Server-side ownership check. Returns { holdsMinting, utxo } with the full token UTXO when the address holds the Repo’s minting NFT under the given category. Uses Fulcrum’s 'include_tokens' flag — the standard listunspent filters token UTXOs out without it.
Build an unsigned Reply NFT (0x03) mint TX. Body: { category, parentRef, contentB64, bcmrPayload?, chunks?, serviceFee? }. Returns { unsignedTransaction, sourceOutputs, commitment, parentUtxo, childCategory }.
Build an unsigned CashBit (0x01) mint TX for a new file under an existing Repo’s category. Body: { category, filePath, contentB64, bcmrPayload?, chunks?, serviceFee? }. Same response shape as build-mint-reply.
Build an unsigned edit-as-spend TX: spend the caller’s existing File CashBit at filePath and produce a new mutable NFT with the updated commitment. Body: { category, filePath, newContentB64, bcmrPayload?, chunks? }. 404 when no existing CashBit at this address matches the path.
Submit a signed TX to the chain via the server’s TCP Fulcrum proxy. Body: { signedTransaction: hex }. Returns { txid } on success or 502 with the underlying Fulcrum rejection message.
CashNect relay (broadcast notification)
The CashNect relay enforces a 1 MB body cap on relay POSTs. The previous 16 KB cap was sized for plain sign_message payloads and rejected BCH-WalletConnect contract-unlock requests, which carry the full CashScript artifact JSON on sourceOutputs[i].contract per CHIP-2024-09. Overflow tears the TCP connection on the relay side; a 1 MB ceiling accommodates the current covenant artifacts.
One-way notification dApp → wallet that the signed TX it returned earlier has now been broadcast and landed at this txid. Body: { requestId, txid }. Lets the wallet’s TX history render the entry without itself attempting broadcast.
Wallet polls this endpoint for queued broadcast_completed notifications. Mark-delivered semantics — the relay sweeps after a successful read.
Reading on-chain content
The read.html resolver accepts a 64-hex TXID and dispatches to one of three branches based on what the transaction carries. The branching is what makes the resolver a single surface for V1 and V2 content together.
Detection branches
- CashBit child mutable NFT. Output 0 carries a token with capability: 'mutable' and a commitment whose first byte is 0x01 and whose total length is 80 hex chars. The resolver decodes the path-hash and content-hash, fetches the parent Repo from the BCMR by category, matches the file by sha256(path)[0..40], and renders a CashBit card.
- Repo genesis minting NFT. Output 0 carries a token with capability: 'minting' and the TX has no GCP-1 OP_RETURN. The resolver renders a Repo Genesis card with category, owner, commitment, NFT badges, and a “Mint a CashBit under this Repo” CTA.
- Raw GCP-1 ChunkDrip. The TX has an OP_RETURN whose payload decodes as GCP-1 with no NFT outputs. Renders a CashBit content card from the V1 era.
Hash verification step
For CashBits, the resolver computes contentHashMatches by comparing the 19-byte content-hash carried in the NFT commitment to the first 19 bytes of sha256(content) as fetched. A green “HASH ✓” pill renders only when this matches. If chunks are unavailable (the publisher never broadcast the content TX), the card renders honestly with contentSource: 'unavailable' rather than silently substituting alternative bytes.
For full pattern depth, see whitepaper §4 — in particular Pattern B (chain-verified sidecar) and Pattern D (BCMR-v2 hash-anchored registry).
The chunks-with-mint pattern
File content rides as GCP-1 OP_RETURN outputs in the same transaction as the NFT mint. The BCMR-v2 record for the file declares chunks: ["$self"] — a sentinel meaning “the resolver should substitute this transaction’s own ID at read-time.”
This sidesteps a chicken-and-egg: the BCMR needs to reference the mint TXID, but the BCMR is anchored inside that mint. The sentinel is a forward-reference that the resolver fills in when it reads the TX.
A typical Repo + README transaction has this output shape:
One signature, one broadcast. The Repo and its first byte of content exist together or not at all. Any “mint-and-content in one TX” protocol can adopt the sentinel.
CashNect (wallet handshake)
CashNect is the wallet handshake surface used for any GitBCH flow that requires a signature. The wallet runs in its own process; the dashboard requests a signature for a fully-constructed transaction; the wallet returns the signed bytes for broadcast.
Flows that require CashNect
- Genesis (creating a new Repo)
- Minting a CashBit under a Repo you own
- Editing a CashBit (spending the prior mutable NFT)
- Posting a Bounty covenant
- Issue state changes (Open → In Progress → Resolved → Closed)
- Merging or closing a PR
- Updating a Path-A or Identity profile
Flows that do not require CashNect
- Reading any Repo, CashBit, Thread, or Reply via the API or read.html.
- Anonymous ChunkDrip QR pay (pay the covenant address; the chunk publishes itself).
- Tipping via direct on-chain transfer.
Sign-only signing
Wallets implementing CashNect for sign_transaction MUST return { signedTransaction: hex } via the relay and stop. The dApp posts that hex to POST /api/broadcast and submits it to chain. A one-way broadcast_completed notification flows back so the wallet’s history can render the entry. Wallets that also self-broadcast (legacy behavior) can return { signedTransaction, txid }; the dApp will use the wallet-provided txid in that case. Either way, the protocol carries enough to stay backward-compatible.
Glossary
Reference for canonical terms.