You're in a room with three signers, a whiteboard, and a treasury policy that seemed perfectly reasonable five minutes ago: any two of the three can spend after a six-month timelock, or all three can spend immediately. Clean on the whiteboard. Then someone has to turn it into actual Bitcoin Script, and the room goes quiet.

That's the problem Miniscript was built to solve.

What Miniscript Actually Does

Bitcoin Script is a stack-based language, deliberately limited, not Turing-complete. It works, but writing it by hand for anything more complex than a single signature is treacherous. Subtle bugs don't announce themselves. A script can look valid, broadcast fine, and then fail to spend when you need it most because a malleability edge case wasn't handled, or because the witness weight was miscalculated and the transaction exceeds standardness limits.

Miniscript sits one layer above raw Script. You write a policy in a human-readable language, the compiler translates it into a Miniscript expression, and that expression maps deterministically to Bitcoin Script. Deterministically is the word that matters here. Given the same policy, you always get the same script, with no judgment calls and no off-by-one errors in locktime encoding.

The policy language reads almost like English. The treasury scenario above becomes:

``` or(and(thresh(2, pk(Alice), pk(Bob), pk(Carol)), after(26280)), thresh(3, pk(Alice), pk(Bob), pk(Carol))) ```

`after(26280)` is a block-height timelock, roughly six months at ten-minute average blocks. `thresh(2, ...)` means two-of-three. The `or` at the top means either branch satisfies the condition.

The compiler takes that, reasons about it, and produces Script. Not just any Script: the minimal Script, the one that minimizes expected witness size weighted by the probability of each spending path. That matters for fees. If the emergency three-of-three path is used one percent of the time, the compiler can afford to make it slightly heavier in order to shrink the common two-of-three path. This is not a minor optimization; over a custody system processing thousands of transactions, it compounds.

The Compilation Pipeline, Step by Step

Start with the policy. The compiler first assigns satisfaction probabilities to each branch. You can specify these explicitly or let the compiler assume equal likelihood. Then it enumerates valid Miniscript expressions for the policy, a finite set because Miniscript's grammar is deliberately constrained.

Each Miniscript node carries a type system with four correctness properties, labeled B, V, K, and W in the specification. These properties propagate bottom-up through the expression tree. A node that produces a top-of-stack boolean is type B. One that verifies and leaves the stack clean is type V. The compiler checks that types compose correctly at every level, which rules out entire classes of script bugs before a single byte hits the chain. Think of it as a proof-checker that runs before deployment, not after something breaks.

Once a valid, well-typed Miniscript tree is confirmed, it translates to Script fragment by fragment. Each Miniscript fragment has exactly one Script translation. No ambiguity. The resulting Script is then checked against three further criteria: it must be sound (only the intended conditions can satisfy it), complete (all intended conditions can satisfy it), and non-malleable (no third party can modify a valid witness into a different valid witness, which would create transaction-ID instability).

Here is a concrete scenario worth sitting with. Alice and Bob are co-founders. They buy the same hardware wallet, configure a 2-of-2 multisig in raw Script, and ship to production. Two years later Bob loses his device. Their raw Script has no recovery path baked in, because adding one to hand-written Script mid-project is effectively a full rewrite. Alice, who used a Miniscript-aware wallet (Liana is a real production example), had compiled a policy with a third recovery key and a one-year timelock from the start. Her wallet can display the policy, verify it, and sign for the recovery path without touching any Script internals. Bob's wallet cannot. Same hardware, very different outcome.

What the Type System Catches That You Won't

The non-malleability check is the part people underestimate. Bitcoin's consensus rules permit certain script constructions where an attacker can flip a byte in the witness and produce a different transaction ID without invalidating the transaction. For Lightning channels and other protocols that pre-sign child transactions, that is catastrophic, not merely inconvenient. Miniscript's type system rejects any expression that permits this at compile time, before the script is ever broadcast.

Resource limits work the same way. Bitcoin Script has a 201-opcode limit per script and a 520-byte limit on pushed data elements. Miniscript tracks opcode and stack usage through the tree and refuses to compile anything that would exceed these bounds. A hand-rolled script might pass local tests and fail on mainnet when a specific spending path hits the limit. That failure mode, silent during testing and catastrophic in production, is exactly the kind of thing a formal type system exists to prevent. The discipline to build in that constraint upfront is, in this analyst's view, what separates serious custody infrastructure from optimistic engineering.

One honest caveat: Miniscript does not cover the full range of Bitcoin Script. Certain exotic constructions, including covenants built with `OP_CHECKTEMPLATEVERIFY`, sit outside the Miniscript fragment set. It is a well-defined, verifiable subset of Script, not a superset of it. That constraint is the feature, not a gap in coverage. The verification guarantees are only possible because the fragment set is bounded.

So when you're evaluating a wallet or custody setup that claims to support complex spending conditions, ask the right question. Not whether it supports multisig. Whether you can read the policy, verify the compiled script matches it, and prove the non-malleability properties hold. Miniscript makes that question answerable. Everything else is trust.