Bitcoin authority support lives in the authority-bitcoin feature and currently requires the ecc platform.
Build example:
./gradlew shadowJar -Pkeeper.features=authority-bitcoin -Pkeeper.platforms=ecc
A Bitcoin authority lets TKeeper parse unsigned transaction data, previous transactions, signing input, sighash settings, and policy effects before signing.
Use Bitcoin authorities for:
- governed UTXO spending
- treasury withdrawals
- fee and output policy
- external risk verdicts before signing
Enforcement boundary
Policy evaluation depends on the unsigned transaction, the selected input and sighash mode, and the previous transactions needed to understand input values. Missing or unclassifiable data must not be treated as a harmless unknown effect.
TKeeper does not select coins, construct change, choose fees, broadcast transactions, or track confirmations. The wallet or custody service must broadcast the exact approved transaction and ensure that its sighash choice covers the fields the policy assumes are fixed.
Authority config
type: bitcoin.transaction
config:
protocol: BTC
Known protocols are BTC, LTC, DASH, and BCH. Custom protocols pin their asset symbol, address versions, Bech32 prefix, and decimals in the authority config.
Effects:
utxo.spendutxo.outpututxo.datautxo.fee
Strict CEL roots:
protocol,asset,assetDecimalstxId,wtxId,version,lockTimesighash,signinginputs,outputs,previousTransactionstotalInput,totalOutput,fee,effects
Intent validation rejects malformed or signed transactions, missing or duplicate previous transactions, missing outputs, coinbase inputs, unknown scripts, negative fees, invalid signing inputs, and unsafe or unknown sighash modes.
Authority example: cold-storage sweep
This authority represents one logical action: sweep BTC into one cold-storage address. It allows sweeps up to 0.25 BTC directly and requires one treasury approval above 0.25 and up to 1 BTC. The fee is capped at 100,000 satoshis. A transaction with change, an additional output, another destination, or another sighash mode is denied.
schemaVersion: verdict.authority/v1
id: btc-cold-storage-sweep
type: bitcoin.transaction
version: 1.0.0
metadata:
title: BTC sweep to cold storage
config:
protocol: BTC
policy:
id: btc-cold-storage-sweep
fallback: DENY
approvers:
treasury-reviewer:
algorithm: ED25519
publicKey64: "BASE64_ENCODED_ED25519_PUBLIC_KEY"
variables:
coldStorageAddress: "bc1qreplacewiththeapprovedaddress"
automaticLimit: "25000000"
reviewedLimit: "100000000"
maximumFee: "100000"
allow:
- id: automatic-sweep
where:
- "protocol == 'BTC'"
- "sighash.all && !sighash.anyoneCanPay"
- "effect.onlyTypes(effects, ['utxo.spend', 'utxo.output', 'utxo.fee'])"
- "effect.one(effects, 'utxo.output')"
- "effect.one(effects, 'utxo.fee')"
- "effect.any(effects, 'utxo.output', {'address': coldStorageAddress})"
- "bigint.lte(effect.amount(effects, 'utxo.output'), automaticLimit)"
- "bigint.lte(effect.amount(effects, 'utxo.fee'), maximumFee)"
- id: reviewed-sweep
where:
- "protocol == 'BTC'"
- "sighash.all && !sighash.anyoneCanPay"
- "effect.onlyTypes(effects, ['utxo.spend', 'utxo.output', 'utxo.fee'])"
- "effect.one(effects, 'utxo.output')"
- "effect.one(effects, 'utxo.fee')"
- "effect.any(effects, 'utxo.output', {'address': coldStorageAddress})"
- "bigint.gt(effect.amount(effects, 'utxo.output'), automaticLimit)"
- "bigint.lte(effect.amount(effects, 'utxo.output'), reviewedLimit)"
- "bigint.lte(effect.amount(effects, 'utxo.fee'), maximumFee)"
approvals:
threshold: 1
approvers: [treasury-reviewer]
deny: []
Replace the address, limits, and publicKey64 with trusted production values. A payout, consolidation with change, or sweep to another vault should use another authority id and document.
See Authorities for the document and policy schema and CEL Functions for policy helpers.