Satd: Bitcoin Transaction Filtering Language

epochbtc1 pts1 comments

Transaction-Filtering Policy - satd Operator Manual

Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Auto

Light

Rust

Coal

Navy

Ayu

satd Operator Manual

Transaction-Filtering Policy

satd ships an optional, total, statically-cost-bounded transaction-filtering<br>policy language . It lets an operator describe transaction shapes to withhold<br>— from relay, from block templates, or both — without ever changing what the<br>node will accept as valid.

The one sentence to internalize first:<br>Filtering cannot prevent confirmation; it can only decline to assist it.

This chapter is intended for operators. For the internal design and rationale —<br>the architecture, the quarantine data model, the invariants, the cost/fuel<br>system, and the reasoning behind the quarantine-only stance — see<br>satd-policy/DESIGN.md.

A transaction shape with real economic demand will confirm via other relay paths<br>or direct miner submission regardless of your policy. What policy gives you is a<br>local, reversible, fully-observable way to decline to help — and the<br>observability surfaces (below) let you watch filtered transactions confirm<br>anyway, block after block, in your own data. Understanding this ceiling is the<br>point: it is what keeps filtering an operator preference rather than an illusion<br>of control.

The quarantine model

There is no reject. Every verdict is quarantine (hold the transaction back<br>along a scope) or allow (an explicit exemption); a transaction that matches<br>no rule is simply acting — relayed and mineable, exactly as today.

Acting class — fully assisted: relayed to peers, served on request,<br>selected into block templates, visible on every standard surface.

Quarantine class — held in the same physical mempool, but withheld along<br>a scope :

relay — not announced, not served, not in BIP35 mempool replies, not<br>rebroadcast. (Still held, so it can be promoted later losslessly.)

template — not selected into block templates and not counted by fee<br>estimation, but still relayed ("relay neutrally, decline to mine").

both — withheld from everything (the default for a bare quarantine).

Consensus is untouched by construction: quarantine changes only relay and<br>template assistance, never validity. A block containing a quarantined<br>transaction still validates and connects normally.

Rules, first-match-wins, and infectious propagation

A policy file is a version 1 declaration followed by rules. Each rule is<br>quarantine [on ] when or allow when . Rules are evaluated top-to-bottom, first match wins , so put<br>exceptions first.

allow is first-match-wins and shields a matching transaction from all<br>later quarantine rules. It is the right tool for "this whole transaction is<br>mine/trusted" and the wrong tool for "spare this one output class" — for<br>narrow carve-outs, use a condition inside the matching expression instead<br>(see the cookbook's dust-storm rule). allow is also capped to the<br>standardness set: it can forgive standardness relay checks, never consensus.

A transaction inherits the union of its quarantined in-mempool ancestors'<br>scopes (infectious propagation ): the node will not announce or mine a<br>child whose parent it is withholding. This is automatic.

Configuration and reload

Point the node at a policy file with policyfile=/path/to/policy.txt (config<br>file or -policyfile flag); the path must be absolute . On startup a bad file<br>is fatal (fail-loud) — and so is a file that trips the Lightning-enforcement<br>danger gate (below), unless allowdangerousfilters=1.

The file is live-reloadable on SIGHUP : the contents are re-read and<br>recompiled on every signal (even if the path is unchanged), the whole mempool is<br>re-placed synchronously (promote/demote/evict), and promoted transactions are<br>re-announced on a bounded drain. A reload that fails to compile keeps the<br>last-good ruleset and logs the error — never a partial apply. Removing<br>policyfile= and reloading drops the engine entirely and promotes everything<br>back to acting. Re-placement only ever changes placement, never validity, so a<br>reload is lossless apart from ordinary budget eviction.

The quarantine class has its own byte budget, quarantinemempool=<br>(in megabytes , default 50), accounted and fee-rate-evicted separately from<br>the acting mempool so neither class can crowd the other out.

Validate a file offline before deploying it:

sat-cli policylint /path/to/policy.txt # parse, typecheck, cost report<br>sat-cli policylint --explain /path/to/policy.txt # plain-English per rule

policylint reports L2 safety in two tiers. The advisory (never blocking,<br>--no-advisories to silence) flags rules that mention time-sensitive<br>Lightning/L2 shapes — anchor outputs, witness-size caps, OP_CSV/OP_CLTV. The<br>danger gate is stricter: it evaluates each rule against synthetic Lightning<br>enforcement transactions and reports the ones that actually quarantine one. A<br>rule that would withhold relay for an enforcement shape...

transaction policy quarantine relay file filtering

Related Articles