Dead-Man Switches
Inverse mode turns the ritual primitive around: instead of firing when everyone acts together, the attempt fires when an assigned participant fails to check in on time. Same definitions, same attempts, same identity binding - one direction of time reversed.
Defining an inverse ritual
Set mode to inverse and give a checkin_interval_seconds; omitting the
interval fails with 400 inverse rituals require checkin_interval_seconds.
The escalation ladder lists what happens once the switch fires:
{
"name": "On-Call Dead-Man Switch",
"mode": "inverse",
"parts": [
{ "part_id": "heartbeat", "assigned_to": "docs-wave" }
],
"window_ms": 1,
"checkin_interval_seconds": 4,
"escalation": [
{ "after_missed_seconds": 0, "action": "notify", "pubsub_channel": "docs4-deadman-alerts" },
{ "after_missed_seconds": 60, "action": "fire", "webhook_ids": [] }
]
}window_ms is required by the schema but plays no role in inverse mode;
any positive value works.
The check-in loop
Arming sets the first deadline to checkin_interval_seconds from now, and
each check-in pushes it out again:
snug ritual arm --def-id nQvtRMurcqXQtyGjajZc
snug ritual checkin --attempt-id UcFpuVdCMaDLsKYPAKfF --part heartbeat{
"attempt_id": "UcFpuVdCMaDLsKYPAKfF",
"part_id": "heartbeat",
"status": "armed",
"next_deadline_ms": 1787926611219
}Check-ins are identity-bound exactly like performs: a caller who is not the
part's assigned_to gets 403 insufficient_permissions.
perform on an inverse attempt is 400 mode_mismatch
(inverse rituals use checkin, not perform), and checkin on a forward
attempt is the mirror-image 400.
When the switch fires
Miss a deadline and the background scheduler (which sweeps every few
seconds - see the CONFIG reference) marks the attempt fired and writes
the usual report:
{
"attempt_id": "UcFpuVdCMaDLsKYPAKfF",
"status": "fired",
"report": {
"performed": 0,
"required": 1,
"missing_parts": ["heartbeat"],
"spread_ms": 0,
"verdict": "0 of 1 parts performed before the window closed"
}
}Firing is exactly once - an atomic latch claims the attempt before any
side effects run. A check-in that races the firing loses cleanly: once the
attempt has fired, further check-ins are 409 not_actionable
(attempt is Fired). To resume coverage after a firing,
arm a fresh attempt.
Escalation ladders
Each escalation step has an after_missed_seconds offset relative to the
firing, an action (notify or fire), and its targets: a
pubsub_channel and/or webhook_ids. Steps at offset 0 dispatch
immediately when the switch fires; later steps are scheduled and dispatched
by the same sweeper when their offset elapses, so a grace window before a
destructive fire step is honored.
Captured live from the docs4-deadman-alerts channel at fire time:
{
"attempt_id": "TSFTzmcWLFnJEscfMPrL",
"ritual_def_id": "nQvtRMurcqXQtyGjajZc",
"event": "escalation_notify",
"after_missed_seconds": 0
}A fire-action step publishes the same shape with
"event": "escalation_fire". The action names intent - notify a human
versus trigger the failover - but both are event dispatches; acting on them
is the subscriber's job.
Rehearsal attempts (arm --rehearsal) follow the whole lifecycle,
including firing, but suppress escalation dispatch.
History
Fired attempts land in the same history and analytics as forward attempts:
snug ritual history --def-id nQvtRMurcqXQtyGjajZc --status firedThe exact request and event schemas are in the Ritual API reference.