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

Resource reference

Everything blockwatcher watches and everywhere it delivers to is one of four resource kinds, each its own Rust struct in crates/blockwatcher-types/src/resource.rs. This section is the field-level reference for an operator writing one: every key, its type, its default, and the exact refusal a bad value gets. What the kinds mean and how they relate lives on Resources; this section owns the tables.

One page per kind:

  • Network: a feed to watch, including the evm-rpc and evm-mempool source module configs.
  • Spec: a chain-tagged decode artifact, including the evm payload shape.
  • Sink: a delivery destination and its engine-owned policies, including the webhook, script, and log module configs.
  • Monitor: the rule tying the other three together, optionally gated.

The two configuration planes

blockwatcher splits configuration by lifetime, not by topic:

  • Instance configuration (blockwatcher.toml plus BLOCKWATCHER_* environment overrides): the process’s own plumbing, read once at boot and immutable for the run. See the Configuration reference.
  • Resource configuration (this section): networks, specs, sinks, and monitors, stored in the storage backend, mutated through the HTTP API or loaded once from a seed directory, and never read from blockwatcher.toml.

Write routes

Every kind answers on the identical CRUD shape (crates/blockwatcher-api/src/routes/resources.rs):

MethodPathMeaning
PUT/networks/{id}, /specs/{id}, /sinks/{id}, /monitors/{id}Create (no If-Match) or update (If-Match: "<version>").
GETsame item paths, plus the bare collection pathsRead one (with its version in ETag) or list all.
DELETEsame item pathsDelete; If-Match is mandatory.

Optimistic concurrency runs on ETag / If-Match:

  • A PUT with no If-Match is a create: 201 with the new version in ETag, or 409 already_exists if the id is already there.
  • A PUT with If-Match: "<version>" is a conditional update: 200 on success, 412 version_conflict (the body carries actual_version) if the version has moved on, 404 not_found if the record is gone.
  • A DELETE without If-Match is 428 precondition_required.

The full route contract, including error bodies and the If-Match format, is on the HTTP API reference.

Seed directories

The binary’s --seed <dir> flag loads resources once, at first boot only (crates/blockwatcher/src/seed.rs):

  • The directory holds exactly one subdirectory per kind: networks/, specs/, sinks/, monitors/, one JSON file per resource.
  • The bundle runs through the same validation Engine::start itself runs, then writes with create-only semantics.
  • Once storage holds any resource of any kind, seeding is refused as a no-op on every later boot: after the first boot, resources are managed exclusively through the API.
  • blockwatcher check <dir> validates a seed directory offline, against the modules compiled into the binary, constructing every one of them; every env:NAME secret the configs reference must be present in check’s own environment.

What a write does to a running pipeline

Hot-reload granularity differs by kind, per crates/blockwatcher-core/src/control/writes.rs and, for the delete noted below, crates/blockwatcher-core/src/control/deletes.rs:

KindEffect of a write
MonitorHot-swap: the running pipeline receives a recompiled monitor set with no restart. Two exceptions restart that one network: a write that changes gate, whose previous envelope’s holds are dropped with the pipeline stopped and the pipeline then brought back (a delete of a gated monitor likewise), and a monitor that names a sink the pipeline never spawned a worker for.
NetworkAlways restarts that network’s pipeline; it resumes from the checkpoint the drain left behind.
SinkRestarts every network whose stored monitors name that sink id.
SpecRestarts every network on the spec’s chain, and on a chain reassignment its prior chain too, because every spec sharing a chain compiles together.

The mechanics, including what a failed fan-out restart does, are on Resources § Lifecycle.

Validation and deletes

Every write validates before it reaches storage, and every validation rejection surfaces on the wire as 422 Unprocessable Entity; the per-kind checks are on Resources § Write-time validation. The refusal messages quoted throughout this section are those checks’ actual output.

A DELETE of a network, spec, or sink refuses outright while any stored monitor still references the id, per refuse_if_referenced (crates/blockwatcher-core/src/control/deletes.rs): delete the monitors first.