jaque runs a command when an object changes state, so an operator can react automatically: restart a daemon on the first soft failure, tell an orchestrator once a host goes hard down. Handlers run regardless of acknowledgements, downtimes and flapping, and they are told the object's state, state type and attempt number as macros. This is the same mechanism Nagios calls event handlers, and jaque expresses it as config objects and a role of its own that runs them.

1. What fires

A handler fires on a StateChanged fact, never on every check result, following this table:

From To Trigger
anything soft problem SOFT, on every attempt
soft, or a different status hard problem HARD
problem, soft or hard OK RECOVERY
same status and type (flapping or attempt-only change) none

This matches Nagios's own rule for when event_handler runs, with one addition: jaque also fires on a hard-to-hard status change. Handlers run in downtime, the way Nagios runs them, because a downtime silences notifications and leaves the state changes handlers react to untouched.

2. The handlers and event_handlers blocks

handlers: {
	"restart-httpd": {
		type:    "command"
		command: "restart-service"     // key into commands
		args:    ["httpd"]
		timeout: "30s"                 // Nagios event_handler_timeout
	}
	"page-orchestrator": {
		type:   "wasm"
		module: "/opt/jaque/handlers/page.wasm"
		args:   ["--cluster", "eu1"]
	}
	"notify-ops": {
		type: "legacy"
		path: "/usr/local/bin/notify-ops"
		args: ["$HOSTNAME$", "$SERVICESTATE$"]
	}
}

event_handlers: {
	enabled: true                       // enable_event_handlers
	host:    "page-orchestrator"        // global_host_event_handler
	service: ""                         // global_service_event_handler
}

hosts: {
	web01: {
		handler:         "restart-httpd"   // event_handler
		handler_enabled: true              // event_handler_enabled
		services: {
			http: {handler: "restart-httpd"}
		}
	}
}

A handler is one of the three command shapes a check already has, legacy, command or wasm, plus timeout (Nagios's event_handler_timeout, 30s by default). Unlike a command check, whose macros are all known when config loads, a handler's line or arguments may name $HOSTSTATE$ and friends: those are dispatch-time macros, so config keeps the template unexpanded and validates only that the handler name and, for a command handler, the command name resolve. A host or service names its own handler with handler; event_handlers names the globals, Nagios's global_host_event_handler and global_service_event_handler.

3. Order: global first, own second

When an object has both a global handler for its kind and its own handler, both run on the same trigger, the global one first and the object's own second: Nagios's order. event_handlers.enabled: false switches handlers off everywhere; handler_enabled: false on a host or service switches them off for that object alone, overriding whichever handlers would otherwise apply to it.

4. Macros available at fire time

A handler template sees the same dispatch-time macros a notification command does: $HOSTSTATE$/$SERVICESTATE$ (UP/DOWN for a host, the status name for a service), $HOSTSTATETYPE$/$SERVICESTATETYPE$ (SOFT/HARD), $HOSTATTEMPT$/$SERVICEATTEMPT$, $HOSTOUTPUT$/$SERVICEOUTPUT$ and $HOSTPERFDATA$/$SERVICEPERFDATA$ (the last completed check's result), $LASTHOSTSTATECHANGE$/$LASTSERVICESTATECHANGE$, and the five date macros ($TIMET$, $DATE$, $TIME$, $SHORTDATETIME$, $LONGDATETIME$). A template whose expansion fails, naming a macro the object cannot provide, runs nothing: the failure is recorded as a run with status UNKNOWN and the error as the output, so the audit shows the handler was due and why it did not run.

5. What is recorded

Every run, executed or failed to expand, is a handler_executed event on the log carrying the handler name, the trigger and the result (status, output, latency); see Event log schema. It is never folded into projected state: replay and every projection ignore it, and it exists for the audit trail. The same run is logged as one line with the object, handler, trigger, status and latency, and counted by jaque_eventhandler_runs_total{outcome}; a shard left blocked or an event fenced by a stale membership epoch is counted by jaque_eventhandler_blocked_total and jaque_eventhandler_fenced_total. See Metrics.

6. Where it runs

Handlers run under their own role, -target eventhandler, embedded in -target all and -target engine by default and moved out with -eventhandler-execution external. See Topologies section 1.7 for its flags and how it composes with the other roles, and Security for the trust a handler command runs with.

7. Importing from Nagios

event_handler on a host or service becomes a handlers entry keyed by the directive's value verbatim, rendered as a command handler with the !-split arguments, the same split check_command gets, and handler: "<value>" on the object; two objects naming the same value share the entry. event_handler_enabled 0 becomes handler_enabled: false. From the main config, enable_event_handlers, global_host_event_handler and global_service_event_handler fill the event_handlers block, and event_handler_timeout fills each rendered handler's timeout. A handler naming a command the tree does not define is reported unsupported, the way an undefined check command is. event_handler_period stays unsupported: it is Icinga 1's, and Nagios has no such directive. See Importer section 4.