Chaingraph is a self-hosted, watch-only Bitcoin workbench for people who want to understand their wallets and follow activity on chain.
It runs against your Bitcoin Core node and Electrum server, and everything you save stays encrypted in your browser.
Chaingraph is not a custodial wallet or a public service. It never touches private keys, cannot sign or spend, and does not identify people.
-
Explore transactions. Look up a transaction, address or output in a chronologically ordered 3D or flat graph. Filter the view, follow funding and spending links, compact repeated outputs between the same visible transaction pair, and inspect bounded address history.
-
Review wallets. Import one or more watch-only wallets from an account-level extended public key. Chaingraph derives addresses in the browser, scans their activity through your node, and keeps factual UTXO, transaction, address, source and destination lists alongside a linked review queue. Successful address UTXO checks are saved inside the encrypted workspace with their dates and coverage; refresh them explicitly after reopening when you need a new observation.
-
Annotate activity. Add labels, notes, bookmarks and tags to transactions, outputs and addresses. Undo and redo edits without rolling back accepted chain refreshes in the current session, or exchange labels through plaintext BIP329 files.
-
Run analysis. Use seven local tools to find patterns in outputs, inputs, address reuse, value flow, scripts and wallet overlap. Findings link to evidence and remain hypotheses, not proof of ownership.
-
Scan connections. Search loaded graph data for funding and spending paths, shared ancestors and descendants, and loops. Results are bounded observations you can add to the graph.
-
Start from examples. Open nine bundled mainnet and testnet4 workspaces with public transaction snapshots and starter annotations. They require no downloads and appear only for configured networks.
- A Bitcoin Core node and an Electrum server (Fulcrum is what Chaingraph is tested with) for mainnet, testnet4, or both. Each network uses its own pair.
- Node.js 24 or newer for a native install, or Docker with Compose v2.
- A modern browser with WebGL. Encrypted workspaces need a secure context, so
use
localhostor HTTPS.
npm ci
cp -n .env.example .env.testnet4 # or .env.mainnet, or both
chmod 600 .env.testnet4
# Edit the file with your Bitcoin Core RPC and Electrum connection details.
npm run devOpen http://127.0.0.1:3001. The backend reads .env.mainnet and
.env.testnet4 from its working directory (or CHAINGRAPH_NETWORK_CONFIG_DIR)
and needs at least one of them. The file name selects the network. RPC accepts a
user/password pair or a cookie file; see .env.example.
For a production build served from one origin:
npm run build
npm start # http://127.0.0.1:3000, or SERVER_PORTmkdir -p config
cp -n .env.example config/.env.testnet4
chmod 750 config && chmod 640 config/.env.testnet4
sudo chgrp 1000 config config/.env.testnet4
# Edit config/.env.testnet4; container loopback is the container itself.
docker compose --env-file /dev/null up --build -dOpen http://127.0.0.1:3000. The container runs as an unprivileged user with a read-only filesystem, and Compose publishes the port on loopback only. HTTPS, private certificate authorities, cookie authentication and upgrades are covered in the deployment guide.
Each public container release has a signed source tag and a signed mapping that names its exact immutable image digest. Do not install from a mutable image tag.
If you have already imported and checked this key, skip to step 2. Otherwise, download the Chaingraph public key and inspect it:
curl -fsSLo chaingraph-release-key.asc https://github.com/remcoros.gpg
gpg --show-keys --keyid-format long --with-fingerprint chaingraph-release-key.ascIt must show this primary fingerprint:
pub ed25519/2F5B10B929CAC959 2024-10-28 [SC]
Key fingerprint = 9D1B D304 339B 2D31 CFA5 637A 2F5B 10B9 29CA C959
uid Remco Ros (github.com/remcoros) <remcoros@live.nl>
Compare that fingerprint with a copy you trust. Only if it matches, import the key:
gpg --import chaingraph-release-key.ascVerify both the source tag and the release mapping:
TAG=v0.1.0
git fetch --force origin "refs/tags/$TAG:refs/tags/$TAG"
git verify-tag "$TAG"
ASSET="chaingraph-$TAG.release.json"
BASE_URL="https://github.com/remcoros/chaingraph/releases/download/$TAG"
curl -fsSLO "$BASE_URL/$ASSET" "$BASE_URL/$ASSET.asc"
gpg --verify "$ASSET.asc" "$ASSET"Both checks should include output like this. The date and timezone will differ:
gpg: Signature made ...
gpg: using EDDSA key 9D1BD304339B2D31CFA5637A2F5B10B929CAC959
gpg: Good signature from "Remco Ros (github.com/remcoros) <remcoros@live.nl>" [unknown]
Primary key fingerprint: 9D1B D304 339B 2D31 CFA5 637A 2F5B 10B9 29CA C959
GnuPG may warn that the key is not certified with a trusted signature. That
means you have not marked it trusted locally; still require the fingerprint
above. Confirm that the mapping's source commit equals
git rev-parse "$TAG^{commit}", then deploy image.name@image.indexDigest from
the mapping as CHAINGRAPH_IMAGE. The deployment guide
covers that path. Advanced users can
rebuild and compare a native OCI descriptor.
The user guide walks through workspaces, wallets, the graph, annotations, analysis, connection scans and backups. A short guided tour is built into the app and can be restarted from Help.
Create a workspace with a public name and a password. Everything else in it (description, wallets, loaded transactions, annotations, analysis results and your view) is encrypted before it reaches browser storage. Workspaces save automatically and can be exported as encrypted files for backup or transfer.
- The backend is a read-only proxy to your Bitcoin Core RPC and Electrum server. It keeps no database, wallet, index, or cache of anything you look up. The genesis hash of each configured network is held in memory to identify it. The proxy and your upstream services do see the script hashes and transaction IDs you look up.
- Address derivation, scanning, analysis and encryption happen in the browser. Extended public keys are never sent to the backend as a wallet import.
- Saved workspaces use AES-256-GCM with a PBKDF2-SHA256 derived key. Passwords live only in the unlocked browser tab. There is no recovery: keep encrypted exports and remember your password.
- BIP329 label exports are plaintext and may contain extended public keys.
- The backend has no login and binds to loopback by default. It is meant for one trusted user on a trusted machine or behind your own authenticated HTTPS proxy. Public or shared hosting is not supported.
- Wallet import supports single-key account keys at depth 3 with legacy, nested SegWit, native SegWit and Taproot scripts. Descriptors, multisig, private keys, signing and spending are out of scope.
- Loaded data is a snapshot. Scans, history and expansion are bounded, and a partial result never proves that nothing else exists. A missing spend means unknown, not unspent. When bounded spending history reaches its configured limit, Chaingraph keeps any verified spenders already loaded and reports the incomplete evidence.
- Heuristics are hypotheses. Common-input ownership can be wrong, CoinJoin detection is incomplete, and no tool identifies a person or proves a wallet owns anything.
- Workspaces are stored per browser origin and subject to browser quotas. Export backups before large investigations.
The browser source separates React UI and wiring (src/App) from workspace
capabilities (src/Core/Workspace). Each concept owns its data types and validation;
the Workspace root composes the canonical document. Persistence owns encrypted
formats, migration and storage.
The source map below identifies each concept and its tests.
- User guide
- Self-hosted deployment
- Container release reproduction
- Container release process
- Architecture
- Source map
- Contributing and security policy
- References and attribution, plus notes on encryption and storage, analysis heuristics and example workspaces
LICENSE.md. Third-party dependency notices are in THIRD_PARTY_NOTICES.md.


