Workspace Index › Dev Notes › An invariant is a stop, not an alarm — how to price a false positive
#53PoC
An invariant is a stop, not an alarm — how to price a false positive
In the Liquid case every individual transaction was valid and only the total was wrong. One line checking holdings == issued every block would have turned a week-long drain into a single block. The usual objection is false positives — 'a wrong stop is also an incident' — and it dissolves the moment you put both costs side by side.
Not a build — a 30-minute exercise. Write three candidate invariants for a system you run, and next to each write one line: 'what does a violation STOP?' Discard any whose honest answer is 'writes a log' — an invariant with no attached action is decoration, and building it is worse than nothing because it trains you to ignore it. Limit the S-tier to one hard conservation check (holdings == issued). Then decide where the watcher runs: it must live on infrastructure separate from the sign/execute path, or it dies in the same incident it was meant to catch. Confirm the Liquid specifics against Blockstream's notes before citing.
Why
The word 'monitoring' hides a decision. An alarm notifies and hopes someone reacts in time; an invariant stops the system before the bad state can be acted on. Liquid is the case for the second: individual transactions were all valid, so nothing an alarm watches would have fired — only the aggregate was wrong, and only a conservation check that halts on violation would have caught it. A week of drain becomes one block.
The reflex objection is false positives: what if the stop is wrong? Answer it by pricing both sides on the same table. A wrong stop costs one angry hour — support tickets, a manual override. A missed violation costs the reserve. That asymmetry is so lopsided that once it is written down the debate ends, and it is the specific move this card teaches: do not argue false positives in the abstract, put the two numbers next to each other.
Two corollaries follow. First, an invariant that only logs is decoration — if there is no action to attach to a violation, do not build the check; a dashboard nobody halts on is theater. Second, the watcher must run on infrastructure separate from the sign/execute path, because a watcher inside the same deployment pipeline dies in the same incident. The check, the action, and the isolation are one design, not three.
How it works
The false-positive argument, priced
Outcome
Cost
Reversible?
Wrong stop (false positive)
one angry hour — tickets, manual override
yes
Missed violation (false negative)
the reserve
no
The asymmetry is the whole argument. You are not choosing between 'errors' and 'no errors'; you are choosing which error you can afford, and one of them is unrecoverable.
The check that would have caught Liquid
Every transaction valid, the total wrong — so per-transaction validation passes and only a conservation invariant fails. holdings == issued, evaluated every block, halts on mismatch. Related: liquid-issuance-not-authorization (why validity is not backing) and foundry-invariant-reachability (an invariant is only as good as the actions that reach it).
Two rules that keep it honest
No action, no check. An invariant that only writes a log is decoration — attach a stop or do not build it.
Isolate the watcher. It must run off the sign/execute path; a watcher in the same pipeline dies with the system. Tenderly (tenderly-lower-the-cost-to-start-watching) is one fast way to stand the first one up off-path.
The one-line takeaway
Monitoring asks someone to react; an invariant reacts for you. Price the wrong-stop against the missed-violation and the fear of false positives evaporates.
리퀴드 건에서 개별 트랜잭션은 전부 유효했고 총량만 틀렸습니다. 매 블록 보유 = 발행을 세는 한 줄이면 일주일짜리 유출이 한 블록이 됐습니다. 흔한 반론은 오탐입니다 — '잘못 멈추면 그것도 사고 아닌가' — 그런데 양쪽 비용을 나란히 놓는 순간 사라집니다.
구현이 아니라 30분 연습입니다. 운영하는 시스템에 불변식 후보 세 줄을 적고, 각각 옆에 한 줄: '위반 시 무엇을 멈추는가?' 정직한 답이 '로그를 남긴다'인 것은 버리세요 — 붙일 행동이 없는 불변식은 장식이고, 만드는 게 안 만드는 것보다 나쁩니다(무시하는 습관을 들이니까). S 등급은 단단한 보존 검사 하나(보유 = 발행)로 제한하세요. 그다음 감시자가 어디서 도는지 정하세요: 서명·실행 경로와 다른 인프라에 있어야 합니다 — 아니면 잡으려던 바로 그 사고에서 같이 죽습니다. 리퀴드 세부는 블록스트림 공지로 확인하세요.
왜
'모니터링'이라는 말이 결정을 숨깁니다. 알람은 알리고 누군가 제때 반응하길 바랍니다; 불변식은 나쁜 상태가 행사되기 전에 시스템을 멈춥니다. 리퀴드는 후자의 사례입니다: 개별 트랜잭션은 전부 유효해서 알람이 보는 것은 아무것도 안 울렸을 것이고 — 총량만 틀렸으며, 위반 시 멈추는 보존 검사만이 잡았을 것입니다. 일주일 유출이 한 블록이 됩니다.
반사적 반론은 오탐입니다: 멈춤이 틀리면? 양쪽을 같은 표에 값 매겨 답하세요. 잘못된 멈춤은 화난 한 시간 — 지원 티켓, 수동 오버라이드. 놓친 위반은 준비금 전부. 이 비대칭이 너무 일방적이라, 적어 놓는 순간 논쟁이 끝납니다. 이 카드가 가르치는 구체적 수는 이것입니다: 오탐을 추상적으로 다투지 말고 두 숫자를 나란히 놓아라.
따름정리 둘. 첫째, 로그만 남기는 불변식은 장식입니다 — 위반에 붙일 행동이 없으면 검사를 만들지 마세요; 아무도 멈추지 않는 대시보드는 연극입니다. 둘째, 감시자는 서명·실행 경로와 다른 인프라에서 돌아야 합니다 — 같은 배포 파이프라인 안의 감시자는 같은 사고에서 죽으니까요. 검사·행동·격리는 셋이 아니라 하나의 설계입니다.
동작 방식
오탐 논증, 값을 매기면
결과
비용
되돌릴 수 있나
잘못된 멈춤(오탐)
화난 한 시간 — 티켓, 수동 오버라이드
예
놓친 위반(미탐)
준비금
아니오
비대칭이 곧 논증 전부입니다. '오류냐 무오류냐'를 고르는 게 아니라, 감당할 수 있는 오류가 어느 쪽인지 고르는 것이고, 하나는 회복 불가입니다.
리퀴드를 잡았을 검사
모든 트랜잭션 유효, 총량 틀림 — 그래서 트랜잭션별 검증은 통과하고 보존 불변식만 실패합니다. 매 블록 평가되는 보유 = 발행이 불일치 시 멈춥니다. 관련: liquid-issuance-not-authorization(유효성이 뒷받침이 아닌 이유), foundry-invariant-reachability(불변식은 닿는 행동만큼만 좋다).
정직하게 유지하는 두 규칙
행동 없으면 검사도 없다. 로그만 남기는 불변식은 장식 — 멈춤을 붙이거나 만들지 마라.
감시자를 격리하라. 서명·실행 경로 밖에서 돌아야 합니다; 같은 파이프라인의 감시자는 시스템과 함께 죽습니다. Tenderly(tenderly-lower-the-cost-to-start-watching)가 첫 감시자를 경로 밖에 빠르게 세우는 한 방법입니다.
한 줄 요약
모니터링은 누군가에게 반응하라 하고, 불변식은 대신 반응합니다. 잘못된 멈춤을 놓친 위반에 값 매기면 오탐 공포가 증발합니다.