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-rpcandevm-mempoolsource module configs. - Spec: a chain-tagged decode artifact, including the
evmpayload shape. - Sink: a delivery destination and its engine-owned policies,
including the
webhook,script, andlogmodule 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.tomlplusBLOCKWATCHER_*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):
| Method | Path | Meaning |
|---|---|---|
PUT | /networks/{id}, /specs/{id}, /sinks/{id}, /monitors/{id} | Create (no If-Match) or update (If-Match: "<version>"). |
GET | same item paths, plus the bare collection paths | Read one (with its version in ETag) or list all. |
DELETE | same item paths | Delete; If-Match is mandatory. |
Optimistic concurrency runs on ETag / If-Match:
- A
PUTwith noIf-Matchis a create:201with the new version inETag, or409 already_existsif the id is already there. - A
PUTwithIf-Match: "<version>"is a conditional update:200on success,412 version_conflict(the body carriesactual_version) if the version has moved on,404 not_foundif the record is gone. - A
DELETEwithoutIf-Matchis428 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::startitself 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; everyenv:NAMEsecret the configs reference must be present incheck’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:
| Kind | Effect of a write |
|---|---|
| Monitor | Hot-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. |
| Network | Always restarts that network’s pipeline; it resumes from the checkpoint the drain left behind. |
| Sink | Restarts every network whose stored monitors name that sink id. |
| Spec | Restarts 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.