Skip to content

Commit f1f7197

Browse files
[codex] implement policy-pack v0.5 (#2)
* implement policy-pack v0.5 * address policy-pack review feedback
1 parent 46e429f commit f1f7197

19 files changed

Lines changed: 940 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
# Changelog
22

3+
## v0.5.0 - 2026-06-19
4+
5+
### Added
6+
7+
- Added `policy-pack --input DIR --format json|markdown [--output FILE]` for local custody, audit, and technical policy evidence review.
8+
- 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.
9+
- Added Markdown/YAML/JSON policy note evidence summaries and optional metadata evidence summaries.
10+
- Added policy-pack fixtures and CLI/unit regression tests, including missing policy notes coverage.
11+
- Added a public sample report at `docs/policy-pack-sample.md`.
12+
13+
### Security
14+
15+
- `policy-pack` performs local file analysis only. It does not sign, create wallets, handle keys, broadcast transactions, or make network calls.
16+
317
## v0.4.0 - 2026-06-19
418

519
### Added

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "btc-risk-lab"
3-
version = "0.4.0"
3+
version = "0.5.0"
44
edition = "2021"
55
authors = ["Jose Robles"]
66
description = "A Rust CLI for explainable Bitcoin transaction, PSBT, and script risk reports."

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Bitcoin and Web3 systems often fail at the edges: incomplete transaction context
2222
- human-readable warning explanations
2323
- JSON output suitable for downstream automation
2424
- consolidated review packs for descriptor, PSBT, transaction, script, policy, and notes directories
25+
- policy-pack reports for custody, audit, and technical review evidence
2526
- optional AI summary layer that never replaces the technical report
2627
- clear security boundaries around private keys and funds
2728

@@ -36,6 +37,7 @@ This MVP is intentionally small, but it is structured like a real due diligence
3637
- structured reporting with `serde`
3738
- Markdown and JSON output
3839
- review-pack reports with cross-artifact checks
40+
- policy-pack reports with policy notes and metadata evidence
3941
- CI with `fmt`, `clippy`, and tests
4042
- a security posture that avoids custody, signing, seed phrases, and private key handling
4143

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

133135
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.
134136

137+
Analyze a policy pack directory for custody or audit review:
138+
139+
```bash
140+
btc-risk-lab policy-pack --input tests/fixtures/policy-packs/multisig-timelock --format markdown
141+
```
142+
143+
Write a policy pack report to a file:
144+
145+
```bash
146+
btc-risk-lab policy-pack --input ./policy-pack --format markdown --output docs/policy-pack-sample.md
147+
```
148+
149+
`policy-pack` reuses `review-pack` and the existing descriptor, PSBT, transaction, and script analyzers. It adds policy evidence handling for:
150+
151+
- `policy.md`
152+
- `policy.yaml` or `policy.yml`
153+
- `policy.json`
154+
- `notes.md`
155+
- `metadata.json`
156+
- `metadata.yaml` or `metadata.yml`
157+
158+
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).
159+
135160
Generate an optional executive summary from an existing JSON report:
136161

137162
```bash
@@ -198,6 +223,7 @@ Current analysis includes:
198223
- descriptor max satisfaction weight where available
199224
- threshold and multisig policy hints
200225
- review-pack cross-artifact checks for descriptor/PSBT policy signals and PSBT/transaction input-output counts
226+
- policy-pack findings for policy notes, optional metadata, missing evidence, and custodian/auditor review questions
201227
- script complexity score
202228
- report schema versioning
203229
- missing-data dependencies
@@ -215,6 +241,7 @@ Current analysis includes:
215241
- request seed phrases
216242
- broadcast transactions
217243
- make network calls from `review-pack`
244+
- make network calls from `policy-pack`
218245
- promise consensus-level validation
219246
- send secrets to an LLM
220247

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

235262
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.
236263

264+
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.
265+
237266
## Technical Due Diligence Connection
238267

239268
The same approach used here applies to technical due diligence for Bitcoin, Web3, fintech, and AI systems:

ROADMAP.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- [x] script analysis from hex or a small ASM subset
99
- [x] descriptor parsing and policy hints with `miniscript`
1010
- [x] review-pack reports for descriptor, PSBT, transaction, script, policy, and notes directories
11+
- [x] policy-pack reports for custody/audit policy evidence directories
1112
- [x] report schema versioning
1213
- [x] JSON and Markdown reports
1314
- [x] risk warnings with human explanations
@@ -31,8 +32,8 @@
3132

3233
## Due Diligence Use Cases
3334

34-
- richer batch analysis for transaction review packs
35-
- policy review for multisig and timelock setups
35+
- richer batch analysis for transaction and policy packs
36+
- deeper policy review for multisig and timelock setups
3637
- PSBT readiness checklist
3738
- executive PDF or Markdown due diligence reports
3839
- CI mode for teams that want checks on generated Bitcoin artifacts
@@ -46,4 +47,5 @@
4647
- custody
4748
- broadcasting
4849
- review-pack network calls
50+
- policy-pack network calls
4951
- consensus-level validation claims

docs/policy-pack-sample.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# BTC Risk Lab Policy Pack
2+
3+
- Schema: `0.5`
4+
- Pack type: `policy_pack`
5+
- Consolidated risk: `medium`
6+
7+
## Artifacts Detected
8+
9+
| Artifact | File |
10+
|---|---|
11+
| `descriptor` | `descriptor.txt` |
12+
| `psbt` | `psbt.base64` |
13+
| `transaction` | `tx.json` |
14+
| `script` | `script.txt` |
15+
| `policy_notes` | `policy.md` |
16+
| `policy_notes` | `policy.yaml` |
17+
| `metadata` | `metadata.json` |
18+
19+
## Evidence Documents
20+
21+
| Artifact | File | Format | Summary |
22+
|---|---|---|---|
23+
| `policy_notes` | `policy.md` | `markdown` | bytes: 264; lines: 6; headings: 1 |
24+
| `policy_notes` | `policy.yaml` | `yaml` | bytes: 118; lines: 5; key_like_lines: 5 |
25+
| `metadata` | `metadata.json` | `json` | bytes: 166; lines: 7; json_type: object; top_level_keys: 5; keys: environment, owner, prepared_by, purpose, review_date |
26+
27+
## Findings
28+
29+
- **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.
30+
- **Descriptor and PSBT timelock signals match** (`low`, `descriptor-psbt-timelock-match`): Both descriptor and PSBT expose `false` for this signal.
31+
- **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.
32+
- **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.
33+
- **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.
34+
- **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.
35+
- **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.
36+
- **Fee evidence is incomplete** (`medium`, `fee-evidence-missing`): Fee review depends on UTXO or prevout evidence. Without it, economic review remains partial.
37+
- **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.
38+
- **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.
39+
- **Transaction fee estimate available** (`low`, `transaction-fee-estimated`): Transaction includes an estimated fee of 900 sats from available analyzer evidence.
40+
41+
## Warnings
42+
43+
- **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.
44+
- **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.
45+
- **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.
46+
- **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.
47+
- **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.
48+
- **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.
49+
- **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.
50+
- **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.
51+
- **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.
52+
53+
## Missing Evidence
54+
55+
- psbt: witness_utxo or non_witness_utxo for every PSBT input
56+
- 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.
57+
58+
## Per-Artifact Summary
59+
60+
### `descriptor` (Analyzed)
61+
62+
- Risk: `low`
63+
- descriptor_type: `wsh_sortedmulti`
64+
- script_type: `p2wsh`
65+
- sanity_check: `true`
66+
- max_satisfaction_weight_wu: `253`
67+
68+
### `psbt` (Analyzed)
69+
70+
- Risk: `medium`
71+
- inputs: `1`
72+
- outputs: `2`
73+
- inputs_with_witness_utxo: `0`
74+
- inputs_with_non_witness_utxo: `0`
75+
- Missing data:
76+
- witness_utxo or non_witness_utxo for every PSBT input
77+
78+
### `transaction` (Analyzed)
79+
80+
- Risk: `medium`
81+
- inputs: `1`
82+
- outputs: `2`
83+
- output_value_sats: `1100`
84+
- estimated_fee_sats: `900`
85+
86+
### `script` (Analyzed)
87+
88+
- Risk: `medium`
89+
- script_type: `unknown`
90+
- byte_len: `1`
91+
- opcode_count: `1`
92+
- complexity: `low`
93+
94+
## Review Questions
95+
96+
- Have reviewers independently confirmed that input/output counts, destinations, amounts, and fees match the intended transaction?
97+
- Which missing data must be collected before treating this review pack as complete?
98+
- Do descriptor.txt, PSBT data, transaction data, and policy notes describe the same intended signing and spending policy?
99+
- Does the written policy identify each signer role, quorum, custody model, recovery path, and approval authority?
100+
- Do descriptor, PSBT, and transaction artifacts match the documented policy intent without relying on this tool to prove formal equivalence?
101+
- Have custodians or auditors independently confirmed destinations, amounts, fee assumptions, and change handling?
102+
- Are absolute or relative timelocks documented with operational consequences and emergency procedures?
103+
- Does metadata identify owner, prepared_by, review_date, environment, and whether this pack is public-demo or production evidence?
104+
- Which missing evidence must be collected before approving, signing, or relying on this policy pack?
105+
106+
## Limitations
107+
108+
- This is an explainability and reporting tool, not a consensus-level Bitcoin validator.
109+
- The policy-pack command performs local file analysis only and does not make network calls.
110+
- The tool does not create wallets, sign transactions, custody funds, request seed phrases, handle private keys, or broadcast transactions.
111+
- Cross-artifact findings compare available signals and counts only; formal descriptor, PSBT, transaction, or wallet equivalence is not proven.
112+
- Fee and weight findings are analyzer evidence, not Bitcoin Core mempool acceptance or fee-rate validation.
113+
- Markdown and YAML policy evidence is summarized structurally; the tool does not semantically validate natural-language policy commitments.

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod analyzer;
2+
pub mod policy_pack;
23
pub mod report;
34
pub mod review_pack;
45

src/main.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ use anyhow::{bail, Result};
44
use clap::{Parser, Subcommand, ValueEnum};
55

66
use btc_risk_lab::analyzer;
7-
use btc_risk_lab::report::{render_report, render_review_pack_report, OutputFormat};
7+
use btc_risk_lab::report::{
8+
render_policy_pack_report, render_report, render_review_pack_report, OutputFormat,
9+
};
810

911
#[derive(Debug, Parser)]
1012
#[command(author, version, about)]
@@ -89,6 +91,18 @@ enum Commands {
8991
output: Option<PathBuf>,
9092
},
9193

94+
/// Analyze a local policy pack with Bitcoin artifacts, policy notes, and optional metadata.
95+
PolicyPack {
96+
#[arg(long, value_name = "DIR")]
97+
input: PathBuf,
98+
99+
#[arg(long, value_enum, default_value_t = CliFormat::Markdown)]
100+
format: CliFormat,
101+
102+
#[arg(long, value_name = "FILE")]
103+
output: Option<PathBuf>,
104+
},
105+
92106
/// Generate an optional executive summary from an existing technical JSON report.
93107
Summarize {
94108
#[arg(long)]
@@ -156,6 +170,14 @@ fn main() -> Result<()> {
156170
let report = btc_risk_lab::review_pack::analyze_review_pack(&input)?;
157171
write_or_print(render_review_pack_report(&report, format.into())?, output)?;
158172
}
173+
Commands::PolicyPack {
174+
input,
175+
format,
176+
output,
177+
} => {
178+
let report = btc_risk_lab::policy_pack::analyze_policy_pack(&input)?;
179+
write_or_print(render_policy_pack_report(&report, format.into())?, output)?;
180+
}
159181
Commands::Summarize { input, provider } => summarize(input, provider)?,
160182
}
161183

0 commit comments

Comments
 (0)