Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## v0.5.0 - 2026-06-19

### Added

- Added `policy-pack --input DIR --format json|markdown [--output FILE]` for local custody, audit, and technical policy evidence review.
- Added schema version `0.5` `PolicyPackReport` output with artifacts detected, evidence document summaries, per-artifact summaries, consolidated findings, warnings, missing evidence, review questions, and limitations.
- Added Markdown/YAML/JSON policy note evidence summaries and optional metadata evidence summaries.
- Added policy-pack fixtures and CLI/unit regression tests, including missing policy notes coverage.
- Added a public sample report at `docs/policy-pack-sample.md`.

### Security

- `policy-pack` performs local file analysis only. It does not sign, create wallets, handle keys, broadcast transactions, or make network calls.

## v0.4.0 - 2026-06-19

### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "btc-risk-lab"
version = "0.4.0"
version = "0.5.0"
edition = "2021"
authors = ["Jose Robles"]
description = "A Rust CLI for explainable Bitcoin transaction, PSBT, and script risk reports."
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ Bitcoin and Web3 systems often fail at the edges: incomplete transaction context
- 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

Expand All @@ -36,6 +37,7 @@ This MVP is intentionally small, but it is structured like a real due diligence
- 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

Expand Down Expand Up @@ -132,6 +134,29 @@ btc-risk-lab review-pack --input ./review-pack --format json --output review-pac

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:

```bash
btc-risk-lab policy-pack --input tests/fixtures/policy-packs/multisig-timelock --format markdown
```

Write a policy pack report to a file:

```bash
btc-risk-lab policy-pack --input ./policy-pack --format markdown --output docs/policy-pack-sample.md
```

`policy-pack` reuses `review-pack` and the existing descriptor, PSBT, transaction, and script analyzers. It adds policy evidence handling for:

- `policy.md`
- `policy.yaml` or `policy.yml`
- `policy.json`
- `notes.md`
- `metadata.json`
- `metadata.yaml` or `metadata.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`](docs/policy-pack-sample.md).

Generate an optional executive summary from an existing JSON report:

```bash
Expand Down Expand Up @@ -198,6 +223,7 @@ Current analysis includes:
- 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
Expand All @@ -215,6 +241,7 @@ Current analysis includes:
- 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

Expand All @@ -234,6 +261,8 @@ Risk classifications are only as complete as the artifact data provided. Missing

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.

## Technical Due Diligence Connection

The same approach used here applies to technical due diligence for Bitcoin, Web3, fintech, and AI systems:
Expand Down
6 changes: 4 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- [x] script analysis from hex or a small ASM subset
- [x] descriptor parsing and policy hints with `miniscript`
- [x] review-pack reports for descriptor, PSBT, transaction, script, policy, and notes directories
- [x] policy-pack reports for custody/audit policy evidence directories
- [x] report schema versioning
- [x] JSON and Markdown reports
- [x] risk warnings with human explanations
Expand All @@ -31,8 +32,8 @@

## Due Diligence Use Cases

- richer batch analysis for transaction review packs
- policy review for multisig and timelock setups
- richer batch analysis for transaction and policy packs
- deeper policy review for multisig and timelock setups
- PSBT readiness checklist
- executive PDF or Markdown due diligence reports
- CI mode for teams that want checks on generated Bitcoin artifacts
Expand All @@ -46,4 +47,5 @@
- custody
- broadcasting
- review-pack network calls
- policy-pack network calls
- consensus-level validation claims
113 changes: 113 additions & 0 deletions docs/policy-pack-sample.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# BTC Risk Lab Policy Pack

- Schema: `0.5`
- Pack type: `policy_pack`
- Consolidated risk: `medium`

## Artifacts Detected

| Artifact | File |
|---|---|
| `descriptor` | `descriptor.txt` |
| `psbt` | `psbt.base64` |
| `transaction` | `tx.json` |
| `script` | `script.txt` |
| `policy_notes` | `policy.md` |
| `policy_notes` | `policy.yaml` |
| `metadata` | `metadata.json` |

## Evidence Documents

| Artifact | File | Format | Summary |
|---|---|---|---|
| `policy_notes` | `policy.md` | `markdown` | bytes: 264; lines: 6; headings: 1 |
| `policy_notes` | `policy.yaml` | `yaml` | bytes: 118; lines: 5; key_like_lines: 5 |
| `metadata` | `metadata.json` | `json` | bytes: 166; lines: 7; json_type: object; top_level_keys: 5; keys: environment, owner, prepared_by, purpose, review_date |

## Findings

- **Descriptor and PSBT multisig signals differ** (`medium`, `descriptor-psbt-multisig-mismatch`): Descriptor signal is `true`, while PSBT signal is `false`. This may be valid for incomplete PSBT data, but it requires manual review.
- **Descriptor and PSBT timelock signals match** (`low`, `descriptor-psbt-timelock-match`): Both descriptor and PSBT expose `false` for this signal.
- **Descriptor threshold cannot be fully checked against PSBT** (`unknown`, `descriptor-psbt-threshold-unavailable`): The descriptor exposes threshold policy, but the current PSBT analyzer only exposes heuristic multisig/script signals, not an exact quorum or signer set. Real descriptor-to-PSBT equivalence is not verified.
- **Descriptor-to-PSBT equivalence is not proven** (`unknown`, `descriptor-psbt-equivalence-not-verified`): The review pack compares available policy signals only. It does not derive addresses, reconstruct wallet origin data, or prove that the PSBT spends from the provided descriptor.
- **Transaction and PSBT counts match** (`low`, `tx-psbt-counts-match`): Both artifacts report 1 input(s) and 2 output(s). This is a count-level check only, not transaction equivalence.
- **Transaction-to-PSBT equivalence is not proven** (`unknown`, `tx-psbt-equivalence-not-verified`): The review pack compares input/output counts only. It does not prove that tx.json is the finalized or extracted transaction for psbt.base64.
- **Threshold policy signal detected** (`low`, `threshold-policy-signal`): At least one artifact exposes threshold or multisig policy. Review signer count, quorum, key custody, backup paths, and emergency access.
- **Fee evidence is incomplete** (`medium`, `fee-evidence-missing`): Fee review depends on UTXO or prevout evidence. Without it, economic review remains partial.
- **Timelock signal detected** (`medium`, `timelock-signal`): At least one artifact exposes absolute or relative timelock policy. Confirm block height, median-time, sequence, and recovery semantics in policy notes.
- **Descriptor satisfaction weight is available** (`low`, `descriptor-weight-available`): The descriptor analyzer reports a max satisfaction weight of 253 WU. This is useful review evidence, not a transaction-level fee-rate proof.
- **Transaction fee estimate available** (`low`, `transaction-fee-estimated`): Transaction includes an estimated fee of 900 sats from available analyzer evidence.

## Warnings

- **Descriptor: Threshold or multisig policy detected** (`low`, `descriptor:threshold-policy`): The descriptor includes threshold-like signing policy. Review signer count, quorum, backup paths, and key origin documentation.
- **PSBT: Cannot estimate fee** (`medium`, `psbt:missing-utxo-data`): The PSBT does not include enough UTXO data for every input. Fee and economic risk analysis remain partial.
- **PSBT: Dust-like output detected** (`medium`, `psbt:dust-output`): At least one non-zero output is below the heuristic dust threshold for its script type.
- **Transaction: Dust-like output detected** (`medium`, `transaction:dust-output`): At least one non-zero output is below the heuristic dust threshold for its script type. This can indicate uneconomic outputs, spam-like construction, or operational mistakes.
- **Script: Timelock signal detected** (`medium`, `script:timelock-signal`): The script includes absolute or relative timelock opcodes. Confirm block height, median time, and sequence semantics before operational use.
- **Descriptor and PSBT multisig signals differ** (`medium`, `descriptor-psbt-multisig-mismatch`): Descriptor signal is `true`, while PSBT signal is `false`. This may be valid for incomplete PSBT data, but it requires manual review.
- **Descriptor threshold cannot be fully checked against PSBT** (`unknown`, `descriptor-psbt-threshold-unavailable`): The descriptor exposes threshold policy, but the current PSBT analyzer only exposes heuristic multisig/script signals, not an exact quorum or signer set. Real descriptor-to-PSBT equivalence is not verified.
- **Descriptor-to-PSBT equivalence is not proven** (`unknown`, `descriptor-psbt-equivalence-not-verified`): The review pack compares available policy signals only. It does not derive addresses, reconstruct wallet origin data, or prove that the PSBT spends from the provided descriptor.
- **Transaction-to-PSBT equivalence is not proven** (`unknown`, `tx-psbt-equivalence-not-verified`): The review pack compares input/output counts only. It does not prove that tx.json is the finalized or extracted transaction for psbt.base64.

## Missing Evidence

- psbt: witness_utxo or non_witness_utxo for every PSBT input
- The descriptor exposes threshold policy, but the current PSBT analyzer only exposes heuristic multisig/script signals, not an exact quorum or signer set. Real descriptor-to-PSBT equivalence is not verified.

## Per-Artifact Summary

### `descriptor` (Analyzed)

- Risk: `low`
- descriptor_type: `wsh_sortedmulti`
- script_type: `p2wsh`
- sanity_check: `true`
- max_satisfaction_weight_wu: `253`

### `psbt` (Analyzed)

- Risk: `medium`
- inputs: `1`
- outputs: `2`
- inputs_with_witness_utxo: `0`
- inputs_with_non_witness_utxo: `0`
- Missing data:
- witness_utxo or non_witness_utxo for every PSBT input

### `transaction` (Analyzed)

- Risk: `medium`
- inputs: `1`
- outputs: `2`
- output_value_sats: `1100`
- estimated_fee_sats: `900`

### `script` (Analyzed)

- Risk: `medium`
- script_type: `unknown`
- byte_len: `1`
- opcode_count: `1`
- complexity: `low`

## Review Questions

- Have reviewers independently confirmed that input/output counts, destinations, amounts, and fees match the intended transaction?
- Which missing data must be collected before treating this review pack as complete?
- Do descriptor.txt, PSBT data, transaction data, and policy notes describe the same intended signing and spending policy?
- Does the written policy identify each signer role, quorum, custody model, recovery path, and approval authority?
- Do descriptor, PSBT, and transaction artifacts match the documented policy intent without relying on this tool to prove formal equivalence?
- Have custodians or auditors independently confirmed destinations, amounts, fee assumptions, and change handling?
- Are absolute or relative timelocks documented with operational consequences and emergency procedures?
- Does metadata identify owner, prepared_by, review_date, environment, and whether this pack is public-demo or production evidence?
- Which missing evidence must be collected before approving, signing, or relying on this policy pack?

## Limitations

- This is an explainability and reporting tool, not a consensus-level Bitcoin validator.
- The policy-pack command performs local file analysis only and does not make network calls.
- The tool does not create wallets, sign transactions, custody funds, request seed phrases, handle private keys, or broadcast transactions.
- Cross-artifact findings compare available signals and counts only; formal descriptor, PSBT, transaction, or wallet equivalence is not proven.
- Fee and weight findings are analyzer evidence, not Bitcoin Core mempool acceptance or fee-rate validation.
- Markdown and YAML policy evidence is summarized structurally; the tool does not semantically validate natural-language policy commitments.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub mod analyzer;
pub mod policy_pack;
pub mod report;
pub mod review_pack;

Expand Down
24 changes: 23 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ use anyhow::{bail, Result};
use clap::{Parser, Subcommand, ValueEnum};

use btc_risk_lab::analyzer;
use btc_risk_lab::report::{render_report, render_review_pack_report, OutputFormat};
use btc_risk_lab::report::{
render_policy_pack_report, render_report, render_review_pack_report, OutputFormat,
};

#[derive(Debug, Parser)]
#[command(author, version, about)]
Expand Down Expand Up @@ -89,6 +91,18 @@ enum Commands {
output: Option<PathBuf>,
},

/// Analyze a local policy pack with Bitcoin artifacts, policy notes, and optional metadata.
PolicyPack {
#[arg(long, value_name = "DIR")]
input: PathBuf,

#[arg(long, value_enum, default_value_t = CliFormat::Markdown)]
format: CliFormat,

#[arg(long, value_name = "FILE")]
output: Option<PathBuf>,
},

/// Generate an optional executive summary from an existing technical JSON report.
Summarize {
#[arg(long)]
Expand Down Expand Up @@ -156,6 +170,14 @@ fn main() -> Result<()> {
let report = btc_risk_lab::review_pack::analyze_review_pack(&input)?;
write_or_print(render_review_pack_report(&report, format.into())?, output)?;
}
Commands::PolicyPack {
input,
format,
output,
} => {
let report = btc_risk_lab::policy_pack::analyze_policy_pack(&input)?;
write_or_print(render_policy_pack_report(&report, format.into())?, output)?;
}
Commands::Summarize { input, provider } => summarize(input, provider)?,
}

Expand Down
Loading
Loading