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