btc-risk-lab is an explainable Bitcoin risk analysis CLI written in Rust.
It analyzes Bitcoin artifacts such as raw transactions, PSBTs, scripts, and output descriptors, then produces a technical report in JSON or Markdown. The goal is not to replace wallet software or consensus validation. The goal is to make transaction structure, missing data, policy signals, and review assumptions visible.
This repository is also a public engineering artifact by Jose Robles, Head of Engineering / AI Architect, showing hands-on work across Rust, Bitcoin/blockchain, AI-assisted reporting, security-minded product boundaries, and technical due diligence.
Bitcoin and Web3 systems often fail at the edges: incomplete transaction context, unclear signing policy, weak operational review, or hidden assumptions around scripts and PSBTs.
btc-risk-lab demonstrates how a senior engineering team can turn low-level artifacts into reviewable evidence:
- deterministic Rust analysis first
- explicit missing-data dependencies
- human-readable warning explanations
- JSON output suitable for downstream automation
- consolidated review packs for descriptor, PSBT, transaction, script, policy, and notes directories
- policy-pack reports for custody, audit, and technical review evidence
- optional AI summary layer that never replaces the technical report
- clear security boundaries around private keys and funds
This MVP is intentionally small, but it is structured like a real due diligence tool:
- Rust CLI architecture with
clap - Bitcoin transaction and PSBT parsing via
rust-bitcoin - descriptor parsing and policy hints via
miniscript - script inspection heuristics
- structured reporting with
serde - Markdown and JSON output
- review-pack reports with cross-artifact checks
- policy-pack reports with policy notes and metadata evidence
- CI with
fmt,clippy, and tests - a security posture that avoids custody, signing, seed phrases, and private key handling
From crates.io:
cargo install btc-risk-labInstall the optional public Esplora fetch command:
cargo install btc-risk-lab --features fetchFrom source:
git clone https://github.com/josediegorobles/btc-risk-lab.git
cd btc-risk-lab
cargo build --releaseRun locally during development:
cargo run -- analyze-tx --input examples/tx.json --format markdownForce offline mode for commands that should not touch the network:
btc-risk-lab --offline analyze-tx --input examples/tx.json --format markdownAnalyze a transaction JSON file:
btc-risk-lab analyze-tx --input examples/tx.json --format markdownAnalyze a raw transaction hex string directly:
btc-risk-lab analyze-tx --hex 02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff02e80300000000000016001400000000000000000000000000000000000000006400000000000000160014111111111111111111111111111111111111111100000000 --format markdownWhen no prevouts are provided, fee analysis is reported as unavailable instead of guessed.
Fetch a public transaction from mempool.space Esplora and analyze it with prevouts:
btc-risk-lab fetch-tx --txid a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d --format markdownfetch-tx is behind the non-default fetch feature and only performs read-only HTTPS GET requests to public transaction endpoints. It does not broadcast transactions, sign, custody funds, or handle keys.
Use --offline to make fetch-tx fail before any HTTP request is made:
btc-risk-lab --offline fetch-tx --txid a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48dAnalyze a base64 PSBT:
btc-risk-lab analyze-psbt --input examples/sample.psbt --format jsonAnalyze a script as hex or a small ASM subset:
btc-risk-lab analyze-script --script "OP_CHECKMULTISIG OP_CHECKSEQUENCEVERIFY" --format markdownAnalyze an output descriptor:
btc-risk-lab analyze-descriptor --descriptor "wsh(sortedmulti(2,020e0338c96a8870479f2396c373cc7696ba124e8635d41b0ea581112b67817261,0250863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b2352,03fff97bd5755eeea420453a14355235d382f6472f8568a18b2f057a1460297556))" --format markdownAnalyze a local review pack directory:
btc-risk-lab review-pack --input tests/fixtures/review-packs/complete --format markdownWrite a review pack report to a file:
btc-risk-lab review-pack --input ./review-pack --format json --output review-pack-report.jsonreview-pack looks for these optional files:
descriptor.txtpsbt.base64tx.jsonscript.txtpolicy.jsonnotes.md
It reuses the existing descriptor, PSBT, transaction, and script analyzers, then emits a schema 0.4 ReviewPackReport with detected artifacts, per-artifact summaries, consolidated risk, warnings, missing data, cross-artifact findings, review questions, and limitations.
Analyze a policy pack directory for custody or audit review:
btc-risk-lab policy-pack --input tests/fixtures/policy-packs/multisig-timelock --format markdownWrite a policy pack report to a file:
btc-risk-lab policy-pack --input ./policy-pack --format markdown --output docs/policy-pack-sample.mdpolicy-pack reuses review-pack and the existing descriptor, PSBT, transaction, and script analyzers. It adds policy evidence handling for:
policy.mdpolicy.yamlorpolicy.ymlpolicy.jsonnotes.mdmetadata.jsonmetadata.yamlormetadata.yml
The schema 0.5 PolicyPackReport includes artifacts detected, evidence document summaries, per-artifact summaries, consolidated findings, warnings, missing evidence, review questions, and clear limitations. A public sample is available at docs/policy-pack-sample.md.
Generate an optional executive summary from an existing JSON report:
cargo run -- analyze-tx --input examples/tx.json --format json > report.json
BTC_RISK_LAB_AI_API_KEY=sk-your-key cargo run --features ai -- summarize --input report.json --provider openaiThe summarize command is behind the non-default ai feature. Builds compiled without that feature return compiled without ai feature.
When enabled, the OpenAI-compatible provider calls BTC_RISK_LAB_AI_BASE_URL or https://api.openai.com/v1 by default, authenticates with BTC_RISK_LAB_AI_API_KEY, and optionally uses BTC_RISK_LAB_AI_MODEL or gpt-4o-mini by default. The HTTP client has a hard 20 second timeout.
The only input sent to the provider is the already-produced btc-risk-lab JSON report passed via --input. The command does not read or upload raw transaction hex files, PSBT files, descriptors, scripts, policy notes, wallet files, keys, seed phrases, or signing material. Output is marked AI-assisted draft and must be reviewed against the underlying technical report.
Use --offline to make the AI-backed summary command fail before any HTTP request is made.
Transactions are provided as JSON so fee analysis can explain whether the required UTXO context is present.
{
"hex": "02000000010000000000000000000000000000000000000000000000000000000000000000ffffffff00ffffffff02e80300000000000016001400000000000000000000000000000000000000006400000000000000160014111111111111111111111111111111111111111100000000",
"prevouts": [
{
"value_sats": 2000,
"script_pubkey": "00140000000000000000000000000000000000000000"
}
]
}Bitcoin transactions do not include the value of the UTXOs they spend. If prevouts are missing, btc-risk-lab reports that fee estimation is unavailable instead of inventing a number.
# BTC Risk Lab Report
- Artifact: `transaction`
- Risk: `medium`
## Summary
| Signal | Value |
|---|---:|
| inputs | 1 |
| outputs | 2 |
| output_value_sats | 1100 |
| estimated_fee_sats | 900 |
## Warnings
- **Dust-like output detected** (`Medium`, `dust-output`): At least one non-zero output is below the heuristic dust threshold for its script type.Current analysis includes:
- number of inputs
- number of outputs
- output value in sats
- output script type where detectable
- address rendering where detectable
- dust-like outputs
- fee estimation when input UTXO values are available
- multisig signals
- absolute and relative timelock signals
- descriptor type and script type
- descriptor sanity check through
miniscript - descriptor max satisfaction weight where available
- threshold and multisig policy hints
- review-pack cross-artifact checks for descriptor/PSBT policy signals and PSBT/transaction input-output counts
- policy-pack findings for policy notes, optional metadata, missing evidence, and custodian/auditor review questions
- script complexity score
- report schema versioning
- missing-data dependencies
- risk classification:
low,medium,high, orunknown - human-readable warning explanations
btc-risk-lab does not:
- create wallets
- sign transactions
- custody funds
- handle private keys
- request seed phrases
- broadcast transactions
- make network calls from
review-pack - make network calls from
policy-pack - promise consensus-level validation
- send secrets to an LLM
It is an analysis, explainability, and reporting tool.
The base analyzer runs locally and does not require network access.
AI support is optional and isolated behind the ai feature flag. When summarize is run with the OpenAI-compatible provider, it sends only the already-produced technical JSON report to the configured AI endpoint and labels the result as an AI-assisted draft. Private keys, seed phrases, wallet files, raw artifacts, and signing material are out of scope by design.
By default, installed builds have no network features enabled. The deterministic analyzer commands are local-only:
analyze-tx --input FILEanalyze-tx --hex HEXanalyze-psbt --input FILEanalyze-script --script TEXTanalyze-descriptor --descriptor TEXTreview-pack --input DIRpolicy-pack --input DIR
Commands that can touch the network:
fetch-tx --txid TXID, when compiled with--features fetch, performs read-only HTTPS GET requests to mempool.space's Esplora API athttps://mempool.space/api. The outgoing request path contains only the public transaction id. Avoid this egress by passing the transaction directly toanalyze-tx --hex HEXoranalyze-tx --input FILE.summarize --input report.json --provider openai, when compiled with--features ai, sends only the already-produced btc-risk-lab JSON report to the configured OpenAI-compatible endpoint. Avoid this egress by inspecting the JSON or Markdown report directly.
The global --offline flag turns any network-backed command into an explanatory error before the HTTP client is used.
This MVP uses heuristics. It does not perform full Bitcoin Core policy validation, mempool acceptance simulation, chain lookup, script execution, wallet state analysis, or consensus-level validation.
Risk classifications are only as complete as the artifact data provided. Missing UTXO data, omitted redeem scripts, absent witness scripts, incomplete PSBT maps, descriptors without operational wallet context, and review packs without matching descriptor/PSBT/transaction artifacts can all reduce confidence.
Review-pack cross-artifact checks are intentionally limited. The tool compares available policy signals and input/output counts, but it does not prove descriptor-to-PSBT equivalence, transaction extraction from PSBT, key origin correctness, signer-set ownership, or wallet state.
Policy-pack reports are evidence packs, not approvals. They summarize Markdown/YAML policy notes structurally, compare available analyzer signals, and surface missing evidence, but they do not semantically validate natural-language policy commitments or prove wallet ownership.
The same approach used here applies to technical due diligence for Bitcoin, Web3, fintech, and AI systems:
- make implicit risks explicit
- separate deterministic facts from interpretation
- expose missing evidence
- document operational assumptions
- build machine-readable reports that executives can still understand
- keep security and privacy boundaries visible in the product design
For clients, this repo is a compact example of how Jose Robles approaches engineering leadership: practical Rust implementation, domain-aware Bitcoin analysis, AI used as a reporting layer instead of a source of truth, and clear communication of risk.
cargo fmt --all
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-featuresMIT