> Section: [2. The model](https://jaque.sh/docs/concepts/architecture.md)
> Next: concepts/event-sourcing
> Index: https://jaque.sh/llms.txt


This is the part Nagios got right, and the part jaque keeps whole: the
rules that turn a stream of check results into "should a human be paged",
without paging anyone for one dropped ping or a five-minute blip. Five
mechanisms do that work -- soft and hard states, flap detection,
acknowledgements, downtimes and dependency reachability -- and every one
of them is a rule inside one pure state machine, so the same sequence of
results always produces the same decision. This page states each rule,
why it exists, and what it suppresses.

Status: downtimes are fixed-window only; flexible and triggered downtimes
are not implemented, and the config schema and the FIFO parser reject
them rather than approximate them (section 4). Everything else on this
page is shipped and property-tested. See [Status](https://jaque.sh/docs/project/status.md).

## 1. Soft and hard states

A single failed check never notifies anything. The object enters a soft
state and is re-checked at `retry_interval` rather than `check_interval`,
so confirmation arrives in seconds, not minutes. Only after `max_attempts`
consecutive failures -- Nagios's `max_check_attempts` -- does the state
become hard, and only a hard transition notifies. Recovery from a hard
problem is itself a hard transition, which is why it notifies too. A flaky
link that recovers on the second attempt wakes nobody; a real outage pages
within a bounded number of retries.


```text
att  result    state               notify
---  --------  ------------------  --------------------
 1   OK        OK        hard      -
 2   CRITICAL  CRITICAL  soft 1/3  -   retry_interval
 3   CRITICAL  CRITICAL  soft 2/3  -   retry_interval
 4   CRITICAL  CRITICAL  hard 3/3  PROBLEM  -> oncall
 5   CRITICAL  CRITICAL  hard      -   check_interval
 6   CRITICAL  CRITICAL  hard      -   renotify_interval
 7   OK        OK        hard      RECOVERY -> oncall

```

`max_attempts` lives on every check's schedule (the schema's `#Schedule`)
and defaults to 3; `retry_interval` defaults to 15s and `check_interval`
to 60s. Once hard, the object is re-checked at `check_interval` again, and
a policy's `renotify_interval` decides how often the still-open problem is
repeated (section 6).

While a problem stays hard, every further result is re-evaluated for
notification. `renotify_interval` on the escalation level bounds how
often that evaluation sends, so the effective cadence is
`max(check_interval, renotify_interval)`; a result that arrives before
the interval has elapsed is suppressed as too soon and counted, not sent.

## 2. Flap detection

An object oscillating OK, CRITICAL, OK, CRITICAL every other check is not
repeatedly broken; it is flapping, and notifying each transition is spam
nobody reads twice. jaque keeps an exponentially weighted moving average
(EWMA) of the state-change rate per object. When it crosses
`high_threshold` the object is flagged flapping and further problem and
recovery notifications are suppressed until the rate falls back below
`low_threshold`. The two thresholds are deliberately different: a single
threshold would itself flap.

Flapping start and stop are notified once each, as meta-events. They are
the one exception to "only hard states notify" and may fire from a soft
state, because flapping is a property of the check history, not of the
current state's confirmation.

```cue
check: {
	type: "tcp"
	address: "10.0.0.1:443"
	flap: {enabled: true, alpha: 0.1, high_threshold: 0.3, low_threshold: 0.15}
}
```

The result that ends a flapping episode emits `FlappingStop` and, by
default, nothing else for a problem that persists: the next result
re-notifies it. This is what Nagios does. Setting
`flap.notify_on_flap_stop: true` on the check emits the problem
notification right after `FlappingStop` on that same result. A recovery
on that result is always sent.

## 3. Acknowledgements

"I know, I am on it." An ack attaches to the current problem and silences
further notifications for it. A sticky ack survives transitions between
non-OK states -- WARNING to CRITICAL is still acked; a non-sticky ack
clears on the first state change. Either way an ack clears on recovery,
so nothing stays acked after it has fixed itself and nobody has to
remember to un-ack it.

## 4. Downtimes

A downtime is a scheduled window during which no notification fires for
the object and every event is marked as occurring in downtime, so the
history stays honest about what was expected.

Downtimes are fixed-window only. The config schema and the FIFO parser
both require `fixed=1` and reject a nonzero `trigger_id`. Nagios's
flexible downtimes, which start on the next problem inside the window
rather than at its start, and triggered downtimes are not implemented. A
`SCHEDULE_HOST_DOWNTIME` line asking for either is rejected, not silently
treated as fixed: a downtime that behaves differently from the one you
asked for is worse than an error.

## 5. Dependencies and unreachability

Hosts declare `parents`. When a parent goes hard DOWN, its dependents do
not produce forty separate "I cannot reach this either" pages: the
dependency graph recomputes reachability incrementally and moves every
child to UNREACHABLE, a state distinct from DOWN that means "unknown,
because the path there is broken". Their notifications are suppressed,
not sent and ignored. One page for the router, silence for everything
behind it, and reachability is recomputed from the graph the moment the
router recovers: a router going hard DOWN with `switch-a` and `switch-b`
behind it turns both switches UNREACHABLE in the same recomputation, and
the router coming back returns them to the state their own checks last
established.

## 6. Escalations, re-notification and periods

A notification policy is a ladder of levels. Each level names contacts, a
`threshold` (how many notifications at the previous level before the
problem escalates; 0 means never escalate past it) and a
`renotify_interval` (how often to repeat while the problem stays open). A
period restricts when a policy's levels may fire at all; outside it,
notifications are suppressed, not queued. [Escalations and
windows](../notifications/escalations-and-windows.md) has the full shape.

## 7. Where this runs

All of the above is one pure state machine. Given the current state, the
check's configuration and one result, it returns the new state plus a
list of effects as data; it takes no action itself and reads no clock,
because the result carries its own timestamp. That purity is what lets
jaque property-test thousands of random result sequences against
invariants such as "a problem notification never fires from a soft state"
and "nothing problem-class fires while flapping" without starting a
goroutine. [Event sourcing](https://jaque.sh/docs/concepts/event-sourcing.md) describes how those
effects reach the world.
