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

Monitor

A monitor is the rule that ties the other three kinds together: an id, the network it watches, one or more selectors, an optional predicate, an optional gate, and actions naming the sinks it delivers to (Monitor, crates/blockwatcher-types/src/resource.rs). It is written through PUT /monitors/{id} on the HTTP API or as one JSON file under a seed directory’s monitors/ subdirectory. Writing a monitor usually restarts nothing: the running pipeline hot-swaps the recompiled monitor set in place. Two things restart that one network instead: changing gate, whose previous envelope’s holds are dropped with the pipeline stopped and the pipeline then brought back (deleting a gated monitor takes the same path), and naming a sink the pipeline never spawned a worker for, per Resources § Lifecycle. Dropping holds never drops an already-emitted digest: an emission the old envelope committed but had not yet delivered survives the wipe and is delivered (or dead-lettered) when the pipeline comes back, see Gates § Delivery guarantee.

Monitor
  id         MonitorId
  network    NetworkId
  selectors  RawSelector[]
  predicate  string?          // this monitor's filter, not a shared id
  gate       ModuleSel?       // this monitor's decision rule, like predicate
  actions    SinkId[]

A complete monitor resource, selecting against the erc20 spec from the Spec page (an EVM example; other families’ selector keys are on the family page):

{
  "id": "usdc-transfers",
  "network": "eth-mainnet",
  "selectors": [
    {
      "spec": "erc20",
      "events": ["Transfer"],
      "addresses": ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"]
    }
  ],
  "predicate": "args.value > 1000000",
  "actions": ["alerts"]
}

The same monitor with a threshold gate; omitting gate (the first example) remains valid passthrough:

{
  "id": "usdc-burst",
  "network": "eth-mainnet",
  "selectors": [
    {
      "spec": "erc20",
      "events": ["Transfer"],
      "addresses": ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"]
    }
  ],
  "predicate": "args.value > 1000000",
  "gate": {
    "module": "threshold",
    "config": { "count": 3, "window_ms": 3600000 }
  },
  "actions": ["alerts"]
}

Fields

Monitor rejects an unrecognized top-level field (#[serde(deny_unknown_fields)]); a selector entry’s keys are the one exception, checked later by the chain’s decoder rather than by serde, per the selector table below.

KeyTypeDefaultMeaning
idstringnone (required)The monitor’s name; every delivered match carries it.
networkstringnone (required)The network this monitor watches. The write refuses an id that does not exist. Fixed at creation: an update naming a different network is refused (422); delete and recreate the monitor to move it.
selectorsarray of objectsnone (required)What to decode and match, OR’d across entries. An empty array refuses: a monitor needs at least one selector.
predicatestringabsentAn expression over the decoded event; absent matches everything the selectors decode.
gateobjectabsent{ "module", "config" }. Absent = passthrough. See Gates.
actionsarray of stringsnone (required)The sinks to deliver each match to, one or more. The write refuses an id that does not name an existing sink.

The whole monitor (selectors, predicate, and gate) must compile against the live decoder, matcher, and gate catalog before the write is persisted, so every refusal quoted below surfaces as a 422 on the write, per Resources § Write-time validation.

Selector keys

Each selector entry carries a core-readable spec reference plus decoder-owned keys. spec is required: a missing key refuses selector requires a 'spec' reference. The spec must exist and share the network’s chain. Every other key is admitted or refused by that chain’s decoder, by name, at compile time. Which keys a family admits, the absent-vs-empty rule for kind lists, and address spelling, are on the family page.

What a selected kind actually decodes to, and which raw material reaches the selector per source, is also on the family page. Shared contracts (entries are OR’d, unknown keys fail closed) stay on Selectors.

Predicate

predicate is one expression string, compiled at write time against the schemas the monitor’s selectors produce; a field or namespace it names that no selected schema declares refuses the write, with a bounded edit-distance suggestion. The language (syntax, operators, the three-valued evaluation, and the type families) is documented on Predicates and the expression language.

Gate

gate compiles at write against the same schemas as the predicate. Refusals (all 422):

  • unknown field on the envelope or inside config (deny_unknown_fields)
  • unknown module (message lists catalog gate names)
  • window_ms 0 or > 86400000: same bound language as sink throttle.window_ms
  • threshold.count < 2 or > 10000
  • missing block.timestamp on the schema: gate requires 'block.timestamp'; this monitor's selectors do not expose it

Changing module or config drops persisted holds for that monitor; they are not migrated. If the pipeline cannot be brought back afterwards, the write is not 2xx and /status reports the network abandoned; persist may already have landed.