Skip to content

Instantly share code, notes, and snippets.

@RandyMcMillan
Forked from lukechilds/README.md
Created August 20, 2026 11:33
Show Gist options
  • Select an option

  • Save RandyMcMillan/eb4b6ffd9287d79d3f0fa409dbdb45ba to your computer and use it in GitHub Desktop.

Select an option

Save RandyMcMillan/eb4b6ffd9287d79d3f0fa409dbdb45ba to your computer and use it in GitHub Desktop.
Connector Vaults: Delegated Execution and Stateless Revocation

Connector Vaults

Connector Vaults: Delegated Execution and Stateless Revocation

Each chain of presigned vault transactions carries one tiny connector UTXO controlled by a controller wallet. Every transaction spends the current connector and creates the next, so it can execute only when the controller wallet signs at broadcast time. Spending the live connector elsewhere revokes the remaining chain without moving its funds or requiring presigned revocation transactions.

Properties:

  • Offline signatures and presigned transactions are halved. An n-step policy needs n offline-signer authorizations instead of 2n (n authorizations + n revocations). This is especially impactful when performing large signing ceremonies on low-powered devices like hardware wallets.
  • Revocation transactions do not need to be presigned. The controller wallet constructs them at broadcast time, with the current feerate, ordinary fee inputs and change, and normal RBF.
  • Revocation is stateless: no revocation PSBTs or one-time revocation secrets need to be stored. The capability to revoke is derivable from the controller wallet's seed and nothing else. This makes this vault construct very secure. In the event of a malicious unvaulting scenario, lost state does not prevent the ability to revoke the operation.
  • Execution of vault operations is delegated to the controller wallet. Presigned authorizations are inert on their own. Executing one requires a fresh signature from the controller wallet on its connector input, so leaking the presigned set does not leak the ability to execute vault operations.
  • Vaults can have multiple different unvaulting operations that run in parallel. Every single vault operation can be atomically revoked with a single batch revoke transaction.
  • Revoke transactions do not move principal; they are entirely disconnected. Funds stay put under the original vault script.

The construction

The standard way to make presigned vault operations cancellable (Custody Protocols Using Bitcoin Vaults, Revault, Rewind, Anzen) is to presign a matching revocation for every operation that sweeps the principal back to safety. That doubles the offline signing work, creates revocation artifacts that must be stored and survive device loss, fixes revocation fees months in advance, and moves the principal on-chain just to stop a policy. Connectors remove all of that.

Conventional presigned vault

First, let's explain how a conventional vault works. A conventional cancellable operation first moves the amount being withdrawn into a staged output. Confirmation of this trigger starts the timelock. Two presigned transactions then compete to spend the staged funds: claim after the delay, or revoke back to safety.

Conventional presigned vault flow

Revocation relies on two conflicting presigned transactions, so both claim and revoke must be signed and stored in advance.

Connector vault

In a connector vault, each chain of presigned vault transactions has one tiny connector UTXO live at a time, held in a separate controller wallet. Each presigned vault transaction must spend the current connector, so it cannot run until the controller wallet approves it by signing the connector at execution time. Each step consumes that connector and, unless it is the final step, creates the next one.

Connector vault flow

The presigned chain cannot move funds on its own. Even if it leaks, every step still needs a fresh connector signature from the controller wallet.

The controller wallet can stop the chain at any point by spending the live connector in a separate transaction. To stop it before the trigger confirms, it spends C₀. If C₀ is spent, the trigger cannot consume it and the entire chain can never be valid:

Connector vault revocation flow

If the trigger has already been executed, the controller wallet spends C₁ within the delay timeout instead:

Connector vault revocation after trigger confirmation

Once revocation confirms, the transaction that needed the spent connector, and every later transaction in that chain, can never confirm. Revocation itself does not move the vault funds. It simply consumes the connector, so the vault funds can no longer move further along the presigned chain.

Revocation leaves the funds in place and prevents them from moving further along the presigned chain. Every step must therefore keep the funds spendable under the normal vault policy.

Stateless revocation

Revocation is stateless. Restore the controller wallet key, scan for live connector UTXOs, and spend them. No revocation PSBTs, secrets or presigned transaction backups are required. After a wallet compromise, even with no saved state, the key alone is enough to immediately broadcast a single batch revocation transaction for every outstanding operation.

Revocation can optionally be outsourced to watchtowers by giving them presigned connector revocation transactions produced by the controller wallet. These watchtowers can revoke vault operations but cannot execute them.

Fees

Fees are simple for revocation too. A revocation is just a normal spend from a separate wallet, built at the current feerate with ordinary fee inputs, change and RBF. There is no presigned principal transaction to rescue with CPFP and no P2A output to manage. Presigned execution transactions still need normal P2A/CPFP fee management.

Controller wallet

This creates a clean delegation model: the policy signer decides what may happen, while execution and revocation are delegated entirely to the controller wallet.

The controller wallet can be any signer or threshold. It does not approve policies or choose where funds go. It only executes or revokes operations that were already approved.

In a connector-based Anzen design, vault policy approval is 2-of-2 between the phone and hardware wallet. The controller wallet is 1-of-2 between the phone and hardware wallet. Both devices must approve a new vault policy, but either device can execute or revoke an approved operation. Neither device can change the operation or create a new one on its own. Anyone who gets access to the presigned chain still cannot execute it without either the phone or hardware wallet.

Example: monthly withdrawals

Connector vaults become especially appealing for vault operations that require long chains of presigned transactions, such as Anzen's monthly spending limit.

Suppose a vault policy allows twelve monthly withdrawals of 0.1 BTC.

Without connectors, every live state needs a presigned claim and a presigned revoke. With connectors, each claim rolls one live connector into the next state:

Conventional and connector vault monthly withdrawal flows

As you can see, connectors cut the signing work in half, from 24 to 12 transactions. This matters when signing vault policies on low-powered hardware wallets. For example, using traditional presigned revocations, a complex Anzen vault policy currently takes about 16 seconds to sign on a Ledger Flex.

Prior art

Connectors

Vaults

  • Custody Protocols Using Bitcoin Vaults: describes presigned vaults with delayed withdrawals and immediate recovery.
  • Revault: multi-party custody using presigned unvault and cancel transactions.
  • Vaults and Covenants: James O'Beirne's survey of Bitcoin vault designs and proposal for OP_VAULT.
  • Rewind: consumer vaults with delayed withdrawals and a rescue path.
  • Anzen: policy-based cold storage built from presigned transaction chains and revocations.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment