Skip to content

Epic: Deliver event-driven Morpho vault governance monitoring #437

Open
2 of 5 issues completed
Open

Description

@spalen0

Goal

Deliver Morpho Vault V1/V2 governance alerts within a few seconds of block inclusion. Use the continuous Envio subscriber proven by the timelock pilot, while retaining the existing daily state-based Morpho jobs as a low-cost completeness and semantic audit.

Initial latency target: at most five seconds p95 from observed block inclusion to durable event acceptance. Record block inclusion, Envio detection, durable acceptance, alert enqueue, and Telegram delivery separately. The WebSocket path is near-real-time transport; HTTP reconciliation and daily state snapshots remain the recovery and correctness layers.

Architecture

Blockchain governance event
  -> Envio indexes event
  -> continuous worker in yearn/monitoring
  -> durable event inbox and deterministic Morpho lifecycle rules
  -> independent alert delivery job
  -> existing Morpho Telegram routing

Envio HTTP reconciliation
  -> the same durable inbox

Existing daily Morpho state monitors
  -> parity/divergence audit
  • Envio owns event indexing and provenance; monitoring owns scope, lifecycle rules, alert state, recovery, grouping, and delivery.
  • The monitoring worker opens outbound GraphQL WebSocket subscription(s). No alert policy or Telegram delivery belongs in Envio handlers.
  • Keep bounded, paginated HTTP reconciliation while connected. Persist independent per-chain progress and deduplicate event occurrences and alert requests.
  • Reuse one supervised process where practical. Share chain-head reads and an HTTP session so the fallback does not multiply TCP/TLS traffic.
  • Keep Telegram outside ingestion. A slow or failed send must not block event intake, and delivery state must survive restarts.
  • Observe events at first inclusion. Record changed, removed, and restored rows after reorgs; define correction behavior explicitly.
  • Agree startup, historical replay, and recovered-during-outage policies before enabling alerts. A historical baseline must not emit old governance alerts, while genuinely new events recovered after downtime must still be considered.
  • Preserve current per-vault grouping and severities. Recovered lifecycle batches must be evaluated in order so a Submit followed by Accept/Revoke during an outage is not reported as still pending.

Morpho scope

Use the explicit vault allowlists in protocols/morpho/config.py, currently covering Vault V1/V2 deployments on Mainnet, Base, HyperEVM, Robinhood Chain, and Katana.

Vault V1

Preserve the current protocols/morpho/governance.py coverage:

  • pending supply-cap changes
  • pending market removals
  • pending timelock changes
  • pending guardian changes
  • the corresponding submit, revoke, and accept/set lifecycle events needed to reconstruct current state

Vault V2

Preserve the current protocols/morpho/governance_v2.py coverage:

  • pending timelocked operations: Submit, Accept, and Revoke
  • owner and curator changes
  • sentinel and allocator membership changes
  • adapter additions and removals

Out of scope for this epic:

  • Morpho market-risk, allocation, liquidity, and bad-debt monitoring
  • MorphoMarketV1AdapterV2 internal timelocks, which the current governance monitor explicitly does not cover
  • automated onchain execution in response to governance events

The daily V1/V2 governance jobs remain enabled during the rollout and report divergence between indexed event-derived state and the authoritative state snapshots. They should not be removed merely because live delivery is enabled.

Pilot and hosting decision

The timelock implementation landed in PR #442. It provides the reusable graphql-transport-ws client, durable SQLite inbox, per-chain checkpoints, HTTP reconciliation, provenance, reorg tracking, health reporting, and systemd supervision.

A read-only probe on 2026-10-24 against the configured Envio deployment:

  • negotiated graphql-transport-ws
  • answered Envio pings before and after connection_ack
  • received a five-row TimelockEvent subscription snapshot with no GraphQL errors
  • received HTTP 200 and the same five rows from the equivalent HTTP query
  • matched WebSocket and HTTP event IDs exactly
  • completed the WebSocket handshake and first snapshot in 0.343 seconds

This proves connectivity and snapshot consistency, not block-to-delivery latency or long-running recovery behavior.

Envio fully supports hosted WebSockets only on Dedicated; other plans expose them with reduced support and recommend staying below ten concurrent connections. The production decision must use the timelock pilot's live coverage/latency report and keep HTTP reconciliation regardless of hosting choice.

The WebSocket itself is not the expected resource constraint. A separate Morpho connection would keep the deployment well below the documented connection caution. At a 60-second cadence, HTTP reconciliation dominates traffic and request count; implementation should reuse the existing worker, share metadata queries, and keep persistent HTTP connections.

Implementation checklist

Create focused issues for steps 3–7 before implementation, with the indexer work deployable independently from the monitoring consumer.

Delivery order

  1. Complete the timelock production transport decision and alert migration. Not planned: timelock alerts stay scheduled (every 30 minutes); see step 2 above.
  2. Define the normalized Morpho event schema and exact V1/V2 event signatures.
  3. Add Envio coverage and backfill from explicit per-chain deployment blocks. Do not alert from historical backfill.
  4. Generalize the worker and implement Morpho lifecycle rules against fixtures and indexed history.
  5. Run WebSocket and HTTP recovery tests, then compare live output with the daily state monitors.
  6. Enable shadow delivery, validate grouping/severity/parity, and only then switch the production Morpho sender.

Epic acceptance criteria

  • Timelock and Morpho streams run in one supervised process without one stream blocking the other; only the Morpho stream delivers alerts (timelock alerts stay scheduled).
  • Every configured Morpho vault and every in-scope governance event has explicit indexed coverage.
  • WebSocket delivery meets the five-second p95 durable-acceptance target, measured from included block time; HTTP recovery and Telegram delivery latency are reported separately.
  • Disconnects, restarts, backlog recovery, duplicate snapshots, equal timestamps, late indexing, and reorgs do not silently lose events or duplicate completed alerts.
  • Submit/Accept/Revoke and submit/revoke/set sequences recover correctly across outages; already-resolved actions are not presented as pending.
  • Alert content, severity, routing, and per-vault grouping match the current V1/V2 governance monitors.
  • Historical backfill cannot send unintended alerts. New events indexed during downtime remain eligible after restart.
  • WebSocket and HTTP coverage, outages, reconciliation lag, state divergence, pending delivery, and delivery outcomes are observable.
  • The existing daily state monitors stay enabled until event-derived state demonstrates parity; any later removal requires a separate decision.
  • Relevant tests, monitoring metadata, protocol documentation, service supervision, and runbooks ship with each implementation issue.

Related work

  • PR #442: continuous timelock subscriber and reusable transport/recovery foundation.
  • Issue #439: move timelock alert delivery to the continuous worker.
  • Issue #302 and PR #307: oracle-event consumer work; reuse suitable transport components without expanding this epic into oracle policy.
  • Envio WebSocket documentation.

Activity

  1. changed the title [-]Epic: Deliver event-driven infiniFi emergency monitoring[/-] [+]Epic: Deliver event-driven Morpho vault governance monitoring[/+] on Oct 24, 2026
  2. gjwang commented on Oct 25, 2026

    @gjwang

    For the HTTP recovery tests in steps 4–5, there is a fail-closed case worth preserving when generalizing the worker. On 8cb745ff9ff35970ea21ad9a8b861c869916b22b, a missing/null event collection is treated as a complete empty range.

    I reproduced this offline with the stored envio.py, reconcile.py and real SQLite EventInbox code (original AST, external import wiring stubbed, no production HTTP adapter): an active event at block 12, checkpoint 15, overlap 5 and processed head 20. Each of {}, {"data":null}, {"data":{}} and {"data":{"TimelockEvent":null}} returned normally, marked that event removed, and advanced the checkpoint to 20. In all four cases, the expected rejection did not occur.

    The separate controls behaved as expected: an explicit TimelockEvent: [] completed the empty range, a valid event stayed active, and a nonempty GraphQL errors array raised EnvioQueryError without changing event status or checkpoint.

    The boundary is _data / fetch_range: the or {} / or [] fallbacks let a malformed response reach mark_missing and checkpoint advancement. Requiring the expected data object and event list before reconciliation would preserve the checkpoint on malformed responses while retaining valid empty-array behavior.

    This was a synthetic response test, with no network or alert sends; I have not observed these payloads from the live Envio deployment. It is a recovery-hardening finding, not evidence of a production outage.

  3. self-assigned this
    on Oct 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions