From 3b9fa5f9babf6e0a5d036811fcd47cbbba3a93ca Mon Sep 17 00:00:00 2001 From: manishdex25 Date: Fri, 29 May 2026 12:11:49 +0530 Subject: [PATCH 1/5] docs: add Chain Events category to sidebar Introduces a new category for Chain Events (ETR Webhook) in the sidebar, including links to various how-to guides related to chain events, such as overview, quick start, configuration, and more. --- docs/how-tos/chain-events/configuration.md | 151 ++ docs/how-tos/chain-events/opentelemetry.md | 172 +++ docs/how-tos/chain-events/overview.md | 61 + docs/how-tos/chain-events/quick-start.md | 175 +++ docs/how-tos/chain-events/rate-limits.md | 152 ++ docs/how-tos/chain-events/registry-api.md | 158 ++ docs/how-tos/chain-events/scaling.md | 163 ++ docs/how-tos/chain-events/webhook-payload.md | 175 +++ sidebars.json | 14 + static/docs/chain-events/config.example.json | 117 ++ .../chain-events/grafana-fleet-health.json | 320 ++++ .../chain-events/grafana-webhook-events.json | 1344 +++++++++++++++++ static/docs/etr-listener/how-it-works.png | Bin 0 -> 118357 bytes 13 files changed, 3002 insertions(+) create mode 100644 docs/how-tos/chain-events/configuration.md create mode 100644 docs/how-tos/chain-events/opentelemetry.md create mode 100644 docs/how-tos/chain-events/overview.md create mode 100644 docs/how-tos/chain-events/quick-start.md create mode 100644 docs/how-tos/chain-events/rate-limits.md create mode 100644 docs/how-tos/chain-events/registry-api.md create mode 100644 docs/how-tos/chain-events/scaling.md create mode 100644 docs/how-tos/chain-events/webhook-payload.md create mode 100644 static/docs/chain-events/config.example.json create mode 100644 static/docs/chain-events/grafana-fleet-health.json create mode 100644 static/docs/chain-events/grafana-webhook-events.json create mode 100644 static/docs/etr-listener/how-it-works.png diff --git a/docs/how-tos/chain-events/configuration.md b/docs/how-tos/chain-events/configuration.md new file mode 100644 index 0000000..5de71ea --- /dev/null +++ b/docs/how-tos/chain-events/configuration.md @@ -0,0 +1,151 @@ +--- +id: configuration +title: Configuration Reference +sidebar_label: Configuration +sidebar_position: 3 +--- + +# Configuration Reference + +The container is configured via two sources: + +| Source | Purpose | +|---|---| +| `config.json` (volume-mounted) | Chain definitions, webhook URL, server settings | +| Environment variables (`.env` or `-e` flags) | Secrets and deployment-specific overrides | + +`${ENV_VAR}` placeholders inside `config.json` values are interpolated from the process environment at startup — use this to keep secrets out of the file. + +```json +{ + "chains": [...], + "webhook": { + "url": "https://my-system.example.com/events", + "headers": { + "Authorization": "Bearer ${WEBHOOK_API_TOKEN}" + } + } +} +``` + +--- + +## Chain Fields + +Each entry in the `chains` array configures one blockchain connection. + +| Field | Required | Default | Description | +|---|---|---|---| +| `chainKey` | **Yes** | — | Identifies the chain — see [Supported Chains](/docs/how-tos/chain-events/overview#supported-chains) | +| `rpcUrl` | **Yes** | — | WebSocket (`wss://`, `ws://`) or HTTP (`https://`, `http://`) RPC endpoint | +| `registryAddresses` | No | `[]` | EVM addresses of Token Registries to watch; can be managed at runtime via the [Registry API](./registry-api) | +| `replayFromBlock` | No | `0` | Block number where your registry was deployed — replay starts here on first run | +| `replayBatchSize` | No | `2000` | Max blocks per `eth_getLogs` batch — lower this (e.g. `500`) on free-tier RPCs with rate limits | +| `replayDelayMs` | No | `0` | Delay between replay batches in ms — add `500`–`1000` on free-tier RPCs | +| `confirmations` | No | `1` | Blocks to wait before delivery (max `12`) — increase to reduce reorg risk on faster chains | +| `pollIntervalMs` | No | chain default | Polling interval for HTTP-transport chains (`stability`, `astron`) — omit for WebSocket chains | + +:::tip WebSocket vs HTTP RPCs +WebSocket URLs (`wss://`) are used for event subscriptions on Ethereum, Polygon, and XDC — they receive new blocks in real time. Stability and Astron use HTTP polling (`https://`) because they do not support WebSocket subscriptions. +::: + +--- + +## Webhook Fields + +| Field | Required | Default | Description | +|---|---|---|---| +| `url` | **Yes** | — | Your downstream HTTP endpoint (must accept POST) | +| `timeoutMs` | No | `10000` | Per-attempt timeout in ms | +| `retryAttempts` | No | `3` | Retries on delivery failure (max `10`) | +| `retryBackoffMs` | No | `1000` | Base backoff in ms — doubles each attempt | +| `headers` | No | none | Extra headers on every delivery (e.g. `Authorization`, `X-Api-Key`) | +| `maxConcurrentDeliveries` | No | `10` | Max in-flight POSTs at the same time | +| `maxQueueSize` | No | `10000` | In-memory event buffer — events beyond this are logged and dropped | + +--- + +## Server Fields + +| Field | Required | Default | Description | +|---|---|---|---| +| `port` | No | `8080` | Port for the health check and Registry API | +| `host` | No | `0.0.0.0` | Keep `0.0.0.0` when running inside Docker | +| `workerProcesses` | No | `true` | Spawn each chain in its own OS process for fault isolation — one chain crashing does not affect others | +| `logLevel` | No | `info` | `trace` / `debug` / `info` / `warn` / `error` / `fatal` | + +--- + +## Environment Variables + +| Variable | Required | Description | +|---|---|---| +| `SIGNING_PRIVATE_KEY` | **Yes** | Raw 32-byte Ed25519 seed, base64-encoded — see [Quick Start](./quick-start#step-2--generate-a-signing-key) | +| `CONFIG_PATH` | No | Path to config file inside the container (default: `/app/config.json`) | +| `DB_HOST` | No | Database hostname — enables state persistence and distributed leasing | +| `DB_DIALECT` | No | Database type: `postgres` (default), `mysql`, `mariadb`, or `mssql` | +| `DB_PORT` | No | Database port — defaults to `5432` (postgres), `3306` (mysql/mariadb), `1433` (mssql) | +| `DB_NAME` | No | Database name (default: `trustvc_events`) | +| `DB_USER` | No | Database username (default: `postgres`) | +| `DB_PASSWORD` | No | Database password | +| `DB_POOL_MAX` | No | Connection pool max (default: `5`) | +| `DB_LEASE_TTL_MS` | No | Distributed lease TTL in ms for HA deployments (default: `30000`) | +| `OTEL_ENABLED` | No | Set `true` to enable OpenTelemetry traces and metrics | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | No | OTLP collector endpoint (default: `http://localhost:4318`) | +| `OTEL_SERVICE_NAME` | No | Service name reported in telemetry (default: `trustvc-webhook-events`) | + +--- + +## Full `config.json` Example + +```json +{ + "chains": [ + { + "chainKey": "ethereum-sepolia", + "rpcUrl": "wss://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY}", + "registryAddresses": [ + "0xe6b5ce7E3691a0927b2806CE6638b35237DFfAc4" + ], + "replayFromBlock": 10896377, + "replayBatchSize": 10000, + "replayDelayMs": 500, + "confirmations": 1 + }, + { + "chainKey": "stability", + "rpcUrl": "https://rpc.stabilityprotocol.com/zgt/${STABILITY_API_KEY}", + "registryAddresses": [ + "0xCB524ba5D1C39f86d87af20B180c01aeD4517DcB" + ], + "pollIntervalMs": 10000, + "replayFromBlock": 35000000, + "replayBatchSize": 5000, + "replayDelayMs": 5000, + "confirmations": 1 + }, + { + "chainKey": "polygon-amoy", + "rpcUrl": "wss://polygon-amoy-bor-rpc.publicnode.com", + "registryAddresses": [], + "replayFromBlock": 39173608, + "replayBatchSize": 5000 + } + ], + "webhook": { + "url": "https://your-system.example.com/trustvc/events", + "timeoutMs": 10000, + "retryAttempts": 3, + "retryBackoffMs": 1000, + "headers": { + "Authorization": "Bearer ${WEBHOOK_SECRET}" + } + }, + "server": { + "port": 8080, + "host": "0.0.0.0", + "workerProcesses": true, + "logLevel": "info" + } +} +``` diff --git a/docs/how-tos/chain-events/opentelemetry.md b/docs/how-tos/chain-events/opentelemetry.md new file mode 100644 index 0000000..079e768 --- /dev/null +++ b/docs/how-tos/chain-events/opentelemetry.md @@ -0,0 +1,172 @@ +--- +id: opentelemetry +title: OpenTelemetry +sidebar_label: OpenTelemetry +sidebar_position: 7 +--- + +# OpenTelemetry + +`trustvc-chain-events` can export traces and metrics to any [OpenTelemetry](https://opentelemetry.io/)-compatible backend. Point it at your existing OTLP endpoint using environment variables — no changes to `config.json` are required. + +When `OTEL_ENABLED` is not set, all telemetry operations are no-ops with zero overhead. + +--- + +## Configuration + +Add these to your `.env`: + +```bash +OTEL_ENABLED=true +OTEL_SERVICE_NAME=trustvc-chain-events +OTEL_EXPORTER_OTLP_ENDPOINT=https://your-otlp-endpoint +OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION=explicit_bucket_histogram +``` + +| Variable | Default | Description | +|---|---|---| +| `OTEL_ENABLED` | `false` | Set `true` to enable telemetry export | +| `OTEL_SERVICE_NAME` | `trustvc-webhook-events` | Service name shown in your observability backend | +| `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4318` | OTLP HTTP endpoint of your collector | +| `OTEL_EXPORTER_OTLP_HEADERS` | — | Auth headers required by your backend (see examples below) | +| `OTEL_INSTANCE_ID` | `-` | Custom instance identifier shown in metrics labels | +| `OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION` | — | Set to `explicit_bucket_histogram` for Prometheus-compatible histograms | + +--- + +## Backend Examples + +### Grafana Cloud + +```bash +OTEL_ENABLED=true +OTEL_SERVICE_NAME=trustvc-chain-events +OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp-gateway-prod-us-central-0.grafana.net/otlp +OTEL_EXPORTER_OTLP_HEADERS=Authorization=Basic +OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION=explicit_bucket_histogram +``` + +### Datadog + +```bash +OTEL_ENABLED=true +OTEL_SERVICE_NAME=trustvc-chain-events +OTEL_EXPORTER_OTLP_ENDPOINT=https://api.datadoghq.com/api/intake/otlp/v1/traces +OTEL_EXPORTER_OTLP_HEADERS=DD-API-KEY= +``` + +### New Relic + +```bash +OTEL_ENABLED=true +OTEL_SERVICE_NAME=trustvc-chain-events +OTEL_EXPORTER_OTLP_ENDPOINT=https://otlp.nr-data.net +OTEL_EXPORTER_OTLP_HEADERS=api-key= +OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION=explicit_bucket_histogram +``` + +### Self-hosted OTLP Collector + +```bash +OTEL_ENABLED=true +OTEL_SERVICE_NAME=trustvc-chain-events +OTEL_EXPORTER_OTLP_ENDPOINT=http://your-collector-host:4318 +``` + +--- + +## Emitted Metrics + +Metrics are exported on a 15-second interval. Prometheus-compatible backends receive them with dots replaced by underscores (e.g. `trustvc.instance.health` → `trustvc_instance_health`). + +### Instance Metrics + +| Metric | Type | Description | +|---|---|---| +| `trustvc.instance.health` | Gauge | `1` = ok/starting, `0` = degraded (at least one chain permanently failed) | +| `trustvc.instance.uptime_seconds` | Gauge | Process uptime in seconds | +| `trustvc.instance.active_chains` | Gauge | Number of chains currently running | +| `trustvc.instance.active_workers` | Gauge | Active child worker processes (`0` when `workerProcesses: false`) | +| `trustvc.instance.total_escrows` | Gauge | Total active TitleEscrow subscriptions across all chains | + +### Chain Metrics + +Labels: `chain`, `transport` + +| Metric | Type | Description | +|---|---|---| +| `trustvc.chain.connected` | Gauge | `1` = RPC connected, `0` = not connected | +| `trustvc.chain.last_seen_block` | Gauge | Latest block number observed | +| `trustvc.chain.active_escrows` | Gauge | Active TitleEscrow subscriptions on this chain | +| `trustvc.chain.reconnect_attempts` | Gauge | Cumulative RPC reconnection attempts | +| `trustvc.chain.events_received` | Counter | On-chain ETR events detected — labels: `chain`, `event_type` | +| `trustvc.chain.state_changes` | Counter | RPC provider state transitions — labels: `chain`, `from_status`, `to_status` | +| `trustvc.rpc.connects` | Counter | Successful RPC connections — labels: `chain`, `transport` | +| `trustvc.rpc.disconnects` | Counter | RPC disconnections — labels: `chain` | + +### Webhook Metrics + +| Metric | Type | Description | +|---|---|---| +| `trustvc.webhook.delivered` | Counter | Successful deliveries — label: `event_type` | +| `trustvc.webhook.failed` | Counter | Deliveries failed after all retries or dropped (queue full) — label: `event_type` | +| `trustvc.webhook.delivery_duration_ms` | Histogram | End-to-end delivery duration including retries — labels: `event_type`, `success` | +| `trustvc.webhook.queue_depth` | Gauge | Events currently waiting in the delivery queue | + +--- + +## Emitted Traces + +| Span | Description | Attributes | +|---|---|---| +| `deliver {event.type}` | Top-level span for a webhook delivery attempt | `event.id`, `event.type`, `event.source`, `webhook.url`, `delivery.attempts` | +| `webhook attempt {n}` | Child span for each individual retry | `http.attempt`, `http.url`, `http.status_code` | +| `chain.status_changed` | Emitted when the RPC provider state changes | `chain`, `transport`, `from_status`, `to_status`, `instance` | + +--- + +## Grafana Dashboards + +Two pre-built Grafana dashboards are available. Both use a Prometheus data source and can be imported directly into your Grafana instance. + +**To import either dashboard:** + +1. In Grafana, go to **Dashboards → Import** +2. Upload the downloaded JSON file +3. Select your Prometheus data source when prompted +4. Click **Import** + +--- + +### Dashboard 1 — Webhook Events + +Focused on day-to-day operational health: is my webhook delivering events? How fast? Are chains connected and tracking escrows? + +Download Webhook Events Dashboard + +

+ +| Section | Panels | +|---|---| +| **Overview** | Uptime, chains connected, active escrows, total delivered, total failed, queue depth | +| **Webhook Delivery** | Delivery rate per minute, p50/p95/p99 delivery duration, queue depth over time | +| **Chain Status** | Chain status table, active escrows per chain, latest block per chain, escrow replay duration (from traces) | +| **On-Chain Events** | Events detected by type per minute | + +--- + +### Dashboard 2 — Fleet & Chain Health + +Focused on infrastructure health across multiple instances: useful when running more than one container in a high-availability setup. Shows which instances are healthy, which chains are connected, and how state is distributed across the fleet. + +Download Fleet & Chain Health Dashboard + +

+ +| Section | Panels | +|---|---| +| **Fleet Overview** | Active instances, healthy instances, degraded instances, total active chains, total escrows, active worker processes | +| **Instance Health** | Instance status per host, instance uptime | +| **Chain Connectivity** | RPC connection status per chain, reconnect attempts, state transition rate, state transition counts | +| **Chain Activity** | Active escrows per chain, last seen block per chain | diff --git a/docs/how-tos/chain-events/overview.md b/docs/how-tos/chain-events/overview.md new file mode 100644 index 0000000..e070608 --- /dev/null +++ b/docs/how-tos/chain-events/overview.md @@ -0,0 +1,61 @@ +--- +id: overview +title: Chain Events — ETR Webhook Sidecar +sidebar_label: Overview +sidebar_position: 1 +--- + +# Chain Events — ETR Webhook Sidecar + +`trustvc-chain-events` is a self-hosted Docker container that watches your Token Registry contracts on-chain and delivers every ETR lifecycle event — mint, transfer, surrender, burn — to your system as a signed HTTP webhook within seconds of chain finality. + +## How It Works + +![TrustVC Secure Webhook Event Flow](/docs/etr-listener/how-it-works.png) + +Each event arrives as a **[CloudEvents 1.0](https://cloudevents.io/)** JSON payload with an `X-TrustVC-Signature` header you can verify independently. + +## Why Self-Hosted + +| Concern | Sidecar approach | +|---|---| +| Data sovereignty | Events never leave your network | +| Provider flexibility | Use your own Alchemy / QuickNode RPC | +| Compliance | Runs in a private VPC — no outbound except to your RPC and webhook | +| Isolation | Each deployment is fully independent | +| Availability | Decoupled from TrustVC infrastructure | + +## Supported Chains + +| `chainKey` | Network | Transport | +|---|---|---| +| `ethereum` | Ethereum Mainnet | WebSocket | +| `ethereum-sepolia` | Ethereum Sepolia | WebSocket | +| `polygon` | Polygon Mainnet | WebSocket | +| `polygon-amoy` | Polygon Amoy | WebSocket | +| `xdc` | XDC Network | WebSocket | +| `xdc-apothem` | XDC Apothem | WebSocket | +| `stability` | Stability Mainnet | HTTP polling | +| `stability-testnet` | Stability Testnet | HTTP polling | +| `astron` | Astron Mainnet | HTTP polling | +| `astron-testnet` | Astron Testnet | HTTP polling | + +Actual delivery timing depends on your `confirmations` setting in `config.json` — events are held until that many blocks have passed after the transaction. For example, with `"confirmations": 3` on Ethereum, delivery takes roughly 36 seconds (3 × 12 sec). + +## Prerequisites + +- A container runtime or managed service — Docker, AWS ECS, EC2, or any environment that can run a container image +- Access to an RPC endpoint for each chain you want to watch (WebSocket for EVM chains, HTTP for Stability/Astron) +- A database — PostgreSQL, MySQL, MariaDB, or MSSQL (optional but recommended — enables state persistence and hot restarts) +- An HTTP endpoint on your system that can receive POST requests + +:::tip No database? +The container runs without a database using in-memory state. If the container restarts it will replay missed events from the last known block. For production use, connect a database so state survives restarts. +::: + +## Next Steps + +1. [Quick Start](./quick-start) — get running in under 5 minutes +2. [Configuration Reference](./configuration) — all `config.json` and environment variable options +3. [Webhook Payload & Verification](./webhook-payload) — event schema and signature verification +4. [Registry API](./registry-api) — add and remove registries at runtime diff --git a/docs/how-tos/chain-events/quick-start.md b/docs/how-tos/chain-events/quick-start.md new file mode 100644 index 0000000..3a44b3c --- /dev/null +++ b/docs/how-tos/chain-events/quick-start.md @@ -0,0 +1,175 @@ +--- +id: quick-start +title: Quick Start +sidebar_label: Quick Start +sidebar_position: 2 +--- + +# Quick Start + +Get `trustvc-chain-events` running locally in under 5 minutes. + +--- + +## Step 1 — Pull the Image + +```bash +docker pull ghcr.io/trustvc/trustvc-chain-events:latest +``` + +--- + +## Step 2 — Generate a Signing Key + +The container signs every webhook delivery with an **Ed25519** key. You keep the private key; your receiver uses the public key to verify payloads. + +**Option A — random seed (simplest)** + +```bash +openssl rand -base64 32 +# example output: cZejchTTcxHUk8N+sbcOyVHZ3MVxzYQGYDCn+hFa4S4= +# Paste this value as SIGNING_PRIVATE_KEY in your .env +``` + +**Option B — PEM key pair (keep the public key for verification)** + +```bash +openssl genpkey -algorithm ed25519 -out private.pem +openssl pkey -in private.pem -pubout -out public.pem + +# Extract the 32-byte seed the container expects: +openssl pkey -in private.pem -outform DER | tail -c 32 | base64 +# Paste this output as SIGNING_PRIVATE_KEY in your .env +``` + +:::important +`SIGNING_PRIVATE_KEY` must be the **raw 32-byte Ed25519 seed encoded as base64** — not the PEM file itself. Use the extraction command above if you generated a PEM key. +::: + +--- + +## Step 3 — Create `config.json` + +Create a `config.json` in your working directory. At a minimum you need a chain, an RPC URL, and a webhook URL. + +Download config.json example + +

+ +```json +{ + "chains": [ + { + "chainKey": "ethereum-sepolia", + "rpcUrl": "wss://eth-sepolia.g.alchemy.com/v2/YOUR_API_KEY", + "registryAddresses": ["0xYourTokenRegistryAddress"], + "replayFromBlock": 6000000 + } + ], + "webhook": { + "url": "https://your-system.example.com/trustvc/events" + } +} +``` + +:::tip Finding your `replayFromBlock` +Set this to the block number when your Token Registry was deployed. The container replays all events from that block on first start to catch up. Using `0` works but will scan the entire chain history. +::: + +You can leave `registryAddresses` as an empty array `[]` and add them later via the [Registry API](./registry-api) — useful when you do not know addresses at deploy time. + +--- + +## Step 4 — Create `.env` + +```bash +# .env +SIGNING_PRIVATE_KEY="cZejchTTcxHUk8N+sbcOyVHZ3MVxzYQGYDCn+hFa4S4=" + +# Optional — PostgreSQL for state persistence +DB_HOST=localhost +DB_PORT=5432 +DB_NAME=trustvcevents +DB_USER=postgres +DB_PASSWORD=secret +``` + +--- + +## Step 5 — Run + +**With an env file (recommended)** + +```bash +docker run -d \ + -v $(pwd)/config.json:/app/config.json:ro \ + --env-file .env \ + -p 8080:8080 \ + --name trustvc-events \ + ghcr.io/trustvc/trustvc-chain-events:latest +``` + +**With Docker Compose** + +```yaml title="docker-compose.yml" +services: + trustvc-events: + image: ghcr.io/trustvc/trustvc-chain-events:latest + ports: + - "8080:8080" + volumes: + - ./config.json:/app/config.json:ro + env_file: + - .env + restart: unless-stopped +``` + +```bash +docker compose up -d +``` + +--- + +## Step 6 — Verify + +```bash +curl http://localhost:8080/health +``` + +```json +{"status":"ok"} +``` + +Check the logs to confirm chains are connected and escrows are loaded: + +```bash +docker logs trustvc-events +``` + +You should see output similar to: + +``` +INFO [startup]: trustvc-webhook-events starting version: "0.1.0" +INFO [startup]: Database connected +INFO [startup]: Chain worker ready chain: "ethereum-sepolia" escrows: 22 +INFO [startup]: ✓ Server ready — listening for on-chain events + chains: "ethereum-sepolia (22 escrows)" + webhook: "https://your-system.example.com/trustvc/events" +``` + +:::note Chain worker shows 0 escrows? +If you see `escrows: 0` in the "Chain worker ready" line but the final "Server ready" line shows the correct count, this is a display-only race condition in startup logging — the container is working correctly. The definitive count is always in the final summary. +::: + +--- + +## What Happens Next + +Once running, the container: + +1. **Replays history** — scans from `replayFromBlock` to the current block to catch any events you missed +2. **Subscribes to live blocks** — uses WebSocket subscriptions (or polling for HTTP-transport chains) to receive new events in real time +3. **Signs and delivers** — each event is signed with your Ed25519 key and POSTed to your webhook URL +4. **Retries on failure** — uses exponential backoff (configurable via `retryAttempts` and `retryBackoffMs`) + +See [Webhook Payload & Verification](./webhook-payload) for the full event schema and how to verify signatures on your receiver. diff --git a/docs/how-tos/chain-events/rate-limits.md b/docs/how-tos/chain-events/rate-limits.md new file mode 100644 index 0000000..d572dd9 --- /dev/null +++ b/docs/how-tos/chain-events/rate-limits.md @@ -0,0 +1,152 @@ +--- +id: rate-limits +title: Avoiding RPC Rate Limits +sidebar_label: Rate Limits +sidebar_position: 6 +--- + +# Avoiding RPC Rate Limits + +When the container first starts it replays historical events by scanning blocks in batches using `eth_getLogs`. Free-tier and public RPC endpoints enforce strict rate limits — too many requests too fast will result in `429 Too Many Requests` errors and missed events. + +The following config fields let you tune replay speed to stay within your RPC's limits. + +--- + +## Key Fields + +| Field | Default | What it controls | +|---|---|---| +| `replayBatchSize` | `2000` | Max number of blocks scanned per `eth_getLogs` call | +| `replayDelayMs` | `0` | Pause between each batch in milliseconds | +| `confirmations` | `1` | Blocks to wait after a transaction before delivering the event | +| `pollIntervalMs` | chain default | How often HTTP-polling chains (Stability, Astron) check for new blocks | + +--- + +## How Replay Works + +On startup the container scans from `replayFromBlock` to the current block in chunks of `replayBatchSize`: + +``` +Block 6,000,000 ──► [batch 1: 6,000,000 – 6,002,000] ──wait replayDelayMs──► + [batch 2: 6,002,000 – 6,004,000] ──wait replayDelayMs──► + ... + [current block] ──► switch to live subscription +``` + +Reducing `replayBatchSize` means more requests, each covering fewer blocks. Adding `replayDelayMs` spaces them out so you don't burst past the RPC's per-second limit. + +--- + +## Recommended Settings by RPC Tier + +### Public / free-tier RPCs + +Free public endpoints (e.g. `publicnode.com`, Infura free) typically allow 5–10 requests/second and cap `eth_getLogs` at 2,000 blocks per call. + +```json +{ + "chainKey": "ethereum-sepolia", + "rpcUrl": "wss://ethereum-sepolia-rpc.publicnode.com", + "replayFromBlock": 6000000, + "replayBatchSize": 500, + "replayDelayMs": 1000, + "confirmations": 1 +} +``` + +### Paid RPC (e.g. Alchemy Growth / QuickNode) + +Paid plans support much larger batch sizes and higher throughput. + +```json +{ + "chainKey": "ethereum-sepolia", + "rpcUrl": "wss://eth-sepolia.g.alchemy.com/v2/${ALCHEMY_API_KEY}", + "replayFromBlock": 6000000, + "replayBatchSize": 10000, + "replayDelayMs": 100, + "confirmations": 1 +} +``` + +### HTTP-polling chains (Stability, Astron) + +These chains use polling instead of WebSocket subscriptions. `pollIntervalMs` controls how often new blocks are checked — set it no lower than your RPC's minimum polling window. + +```json +{ + "chainKey": "stability", + "rpcUrl": "https://rpc.stabilityprotocol.com/zgt/${STABILITY_API_KEY}", + "replayFromBlock": 35000000, + "replayBatchSize": 5000, + "replayDelayMs": 5000, + "pollIntervalMs": 10000, + "confirmations": 1 +} +``` + +--- + +## Diagnosing Rate Limit Errors + +Check logs for these patterns: + +| Log message | Cause | Fix | +|---|---|---| +| `missing response` / `timeout` | Batch too large, RPC dropped the connection | Lower `replayBatchSize` | +| `rate limit exceeded` / `429` | Too many requests per second | Increase `replayDelayMs` | +| `could not decode result data` | Wrong registry address or RPC returned empty | Verify `registryAddresses` and `rpcUrl` | + +Enable debug logging to see each batch request: + +```bash +# In config.json +{ "logLevel": "debug" } +``` + +--- + +## Full Example — Multi-Chain with Conservative Rate Limits + +```json +{ + "chains": [ + { + "chainKey": "ethereum-sepolia", + "rpcUrl": "wss://ethereum-sepolia-rpc.publicnode.com", + "registryAddresses": ["0xYourRegistryAddress"], + "replayFromBlock": 6000000, + "replayBatchSize": 500, + "replayDelayMs": 1000, + "confirmations": 1 + }, + { + "chainKey": "polygon-amoy", + "rpcUrl": "wss://polygon-amoy-bor-rpc.publicnode.com", + "registryAddresses": [], + "replayFromBlock": 39000000, + "replayBatchSize": 1000, + "replayDelayMs": 500, + "confirmations": 1 + }, + { + "chainKey": "stability", + "rpcUrl": "https://rpc.stabilityprotocol.com/zgt/${STABILITY_API_KEY}", + "registryAddresses": [], + "replayFromBlock": 35000000, + "replayBatchSize": 5000, + "replayDelayMs": 5000, + "pollIntervalMs": 10000, + "confirmations": 1 + } + ], + "webhook": { + "url": "https://your-system.example.com/trustvc/events" + }, + "server": { + "logLevel": "info" + } +} +``` diff --git a/docs/how-tos/chain-events/registry-api.md b/docs/how-tos/chain-events/registry-api.md new file mode 100644 index 0000000..f282933 --- /dev/null +++ b/docs/how-tos/chain-events/registry-api.md @@ -0,0 +1,158 @@ +--- +id: registry-api +title: Registry API +sidebar_label: Registry API +sidebar_position: 5 +--- + +# Registry API + +The container exposes a REST API on port `8080` that lets you add and remove Token Registry contracts at runtime — without restarting the container or editing `config.json`. + +:::note Database required +All registry management endpoints require `DB_HOST` to be configured. They return `503 Service Unavailable` if no database is connected. Registries added via the API are persisted to the database and survive container restarts. +::: + +--- + +## Health Check + +```bash +GET /health +``` + +```bash +curl http://localhost:8080/health +``` + +```json +{"status":"ok"} +``` + +| `status` | Meaning | HTTP | +|---|---|---| +| `ok` | All chains connected and ready | `200` | +| `starting` | At least one chain still connecting | `200` | +| `degraded` | At least one chain permanently failed | `503` | + +--- + +## Add a Registry + +```bash +POST /registry +Content-Type: application/json +``` + +```bash +curl -X POST http://localhost:8080/registry \ + -H 'Content-Type: application/json' \ + -d '{ + "chainKey": "ethereum-sepolia", + "address": "0xYourTokenRegistryAddress", + "fromBlock": 6000000 + }' +``` + +### Request Body + +| Field | Required | Description | +|---|---|---| +| `chainKey` | **Yes** | Must match a `chainKey` in your running `config.json` | +| `address` | **Yes** | EVM address of the Token Registry contract | +| `fromBlock` | No | Block to replay history from (default: `0`) — set to your registry's deployment block | + +### Responses + +| HTTP | Meaning | +|---|---| +| `200` | Registry added and syncing — historical events are replaying in the background | +| `400` | Missing or invalid fields | +| `422` | Address is not a deployed TrustVC registry on that chain | +| `503` | Database not configured | + +:::tip Set `fromBlock` accurately +If you omit `fromBlock` or pass `0`, the container scans from the chain genesis — this can take a long time. Always pass the block number when your registry was deployed. +::: + +--- + +## List Registries + +```bash +GET /registries +``` + +```bash +curl http://localhost:8080/registries +``` + +```json +[ + { + "chainKey": "ethereum-sepolia", + "address": "0xe6b5ce7e3691a0927b2806ce6638b35237dffac4", + "fromBlock": 6000000, + "addedAt": "2024-01-15T10:00:00.000Z" + } +] +``` + +--- + +## Remove a Registry + +```bash +DELETE /registry/:chainKey/:address +``` + +```bash +curl -X DELETE \ + http://localhost:8080/registry/ethereum-sepolia/0xe6b5ce7e3691a0927b2806ce6638b35237dffac4 +``` + +| HTTP | Meaning | +|---|---| +| `200` | Registry removed — no further events will be delivered for this registry | +| `404` | Registry not found | +| `503` | Database not configured | + +--- + +## Workflow Example + +A common pattern is to start the container with an empty `registryAddresses: []` in `config.json`, then add registries as they are deployed. + +**Step 1 — Start the container** + +```bash +docker run -d \ + -v $(pwd)/config.json:/app/config.json:ro \ + --env-file .env \ + -p 8080:8080 \ + ghcr.io/trustvc/trustvc-chain-events:latest +``` + +**Step 2 — Deploy your Token Registry** + +Deploy your Token Registry contract and note the contract address and deployment block number. + +**Step 3 — Register it** + +```bash +curl -X POST http://localhost:8080/registry \ + -H 'Content-Type: application/json' \ + -d '{ + "chainKey": "ethereum-sepolia", + "address": "0xYourRegistryAddress", + "fromBlock": 6000000 + }' +``` + +Events start flowing immediately once the registry is added. + +**Step 4 — Confirm it is active** + +```bash +curl http://localhost:8080/registries +``` diff --git a/docs/how-tos/chain-events/scaling.md b/docs/how-tos/chain-events/scaling.md new file mode 100644 index 0000000..2e77d05 --- /dev/null +++ b/docs/how-tos/chain-events/scaling.md @@ -0,0 +1,163 @@ +--- +id: scaling +title: Scaling & High Availability +sidebar_label: Scaling & HA +sidebar_position: 8 +--- + +# Scaling & High Availability + +--- + +## Single Instance (Default) + +By default, each chain runs in its own child process (`workerProcesses: true` in config). This means one chain crashing or losing its RPC connection does not affect the others. For most deployments a single container is sufficient. + +```json +{ + "server": { + "workerProcesses": true + } +} +``` + +--- + +## Horizontal Scaling + +To run multiple instances of the container in parallel — for redundancy or higher throughput — you must connect a database. The container uses a **distributed lease** mechanism (one active worker per chain at a time) to prevent duplicate event delivery when multiple instances are running. + +```bash +# Required for horizontal scaling +DB_HOST=your-postgres-or-mysql-host +DB_LEASE_TTL_MS=30000 # how long a lease is held before another instance can take over +``` + +If the active instance crashes or loses its lease, another instance picks it up within `DB_LEASE_TTL_MS` milliseconds. + +--- + +## Docker Compose — 2 Replicas + +```yaml +services: + trustvc-events: + image: ghcr.io/trustvc/trustvc-chain-events:latest + deploy: + replicas: 2 + ports: + - "8080-8081:8080" + volumes: + - ./config.json:/app/config.json:ro + env_file: + - .env + restart: unless-stopped + + postgres: + image: postgres:16-alpine + environment: + POSTGRES_DB: trustvcevents + POSTGRES_USER: trustvc + POSTGRES_PASSWORD: secret + volumes: + - pgdata:/var/lib/postgresql/data + restart: unless-stopped + +volumes: + pgdata: +``` + +**`.env`** + +```bash +SIGNING_PRIVATE_KEY="your-base64-seed" + +DB_HOST=postgres +DB_PORT=5432 +DB_NAME=trustvcevents +DB_USER=trustvc +DB_PASSWORD=secret +DB_LEASE_TTL_MS=30000 +``` + +--- + +## AWS ECS — Fargate + +For production deployments on AWS, run the container as a Fargate service. The task definition below mirrors the Docker run command. + +**Task definition (excerpt)** + +```json +{ + "family": "trustvc-chain-events", + "containerDefinitions": [ + { + "name": "trustvc-chain-events", + "image": "ghcr.io/trustvc/trustvc-chain-events:latest", + "portMappings": [ + { "containerPort": 8080, "protocol": "tcp" } + ], + "mountPoints": [ + { + "sourceVolume": "config", + "containerPath": "/app/config.json", + "readOnly": true + } + ], + "environment": [ + { "name": "DB_HOST", "value": "your-rds-endpoint" }, + { "name": "DB_NAME", "value": "trustvcevents" }, + { "name": "DB_USER", "value": "trustvc" }, + { "name": "DB_LEASE_TTL_MS", "value": "30000" } + ], + "secrets": [ + { + "name": "SIGNING_PRIVATE_KEY", + "valueFrom": "arn:aws:secretsmanager:region:account:secret:trustvc/signing-key" + }, + { + "name": "DB_PASSWORD", + "valueFrom": "arn:aws:secretsmanager:region:account:secret:trustvc/db-password" + } + ], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "/ecs/trustvc-chain-events", + "awslogs-region": "ap-southeast-1", + "awslogs-stream-prefix": "ecs" + } + } + } + ] +} +``` + +:::tip `config.json` on ECS +Mount `config.json` from an S3 object using an init container, or bake a non-secret config into a custom image layer. Keep secrets in AWS Secrets Manager and inject them as environment variables as shown above. +::: + +--- + +## Health Check Integration + +All orchestrators (ECS, Kubernetes, Docker Compose) can use the `/health` endpoint to determine instance readiness: + +```json +{ + "healthCheck": { + "command": ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"], + "interval": 30, + "timeout": 5, + "retries": 3, + "startPeriod": 15 + } +} +``` + +| `/health` response | Meaning | +|---|---| +| `{"status":"ok"}` | All chains connected | +| `{"status":"starting"}` | Container is still connecting to one or more chains | +| `{"status":"degraded"}` | At least one chain has permanently failed — restart the container | diff --git a/docs/how-tos/chain-events/webhook-payload.md b/docs/how-tos/chain-events/webhook-payload.md new file mode 100644 index 0000000..da537f5 --- /dev/null +++ b/docs/how-tos/chain-events/webhook-payload.md @@ -0,0 +1,175 @@ +--- +id: webhook-payload +title: Webhook Payload & Verification +sidebar_label: Webhook Payload +sidebar_position: 4 +--- + +# Webhook Payload & Verification + +Every event is delivered as an HTTP `POST` to your configured `webhook.url`. + +--- + +## Request Format + +``` +POST /your-endpoint +Content-Type: application/json +X-TrustVC-Signature: ed25519= +``` + +The body follows the [CloudEvents 1.0](https://cloudevents.io/) specification: + +```json +{ + "specversion": "1.0", + "id": "550e8400-e29b-41d4-a716-446655440000", + "source": "urn:trustvc:11155111:0xe6b5ce7e3691a0927b2806ce6638b35237dffac4", + "type": "com.trustvc.etr.holder_transfer", + "datacontenttype": "application/json", + "time": "2024-01-15T10:31:00.000Z", + "subject": "1", + "data": { + "chainKey": "ethereum-sepolia", + "chainId": 11155111, + "registryAddress": "0xe6b5ce7e3691a0927b2806ce6638b35237dffac4", + "tokenId": "1", + "blockNumber": 6123456, + "transactionHash": "0xabcdef...", + "logIndex": 0, + "payload": { + "fromHolder": "0xSenderAddress", + "toHolder": "0xReceiverAddress" + } + } +} +``` + +### Top-Level Fields + +| Field | Description | +|---|---| +| `specversion` | Always `"1.0"` | +| `id` | UUID — globally unique event identifier | +| `source` | `urn:trustvc::` | +| `type` | Event type — see [Event Types](#event-types) below | +| `time` | ISO-8601 block timestamp | +| `subject` | Token ID as a string | +| `data` | Event-specific payload — see below | + +### `data` Fields + +| Field | Description | +|---|---| +| `chainKey` | Chain identifier (e.g. `ethereum-sepolia`) | +| `chainId` | Numeric EIP-155 chain ID | +| `registryAddress` | Token Registry contract address (lowercase) | +| `tokenId` | ETR token ID as a string | +| `blockNumber` | Block the event was confirmed in | +| `transactionHash` | Transaction that emitted the event | +| `logIndex` | Log index within the transaction | +| `payload` | Event-specific data (addresses, amounts, etc.) | + +:::tip Idempotency +Use `data.transactionHash + data.logIndex` as your idempotency key — this combination uniquely identifies any on-chain event. +::: + +--- + +## Event Types + +| `type` | Trigger | Key `payload` fields | +|---|---|---| +| `com.trustvc.etr.minted` | Token minted | `tokenId`, `owner` | +| `com.trustvc.etr.burned` | Token burned | `tokenId` | +| `com.trustvc.etr.surrendered` | Token surrendered to registry | `tokenId` | +| `com.trustvc.etr.restored` | Token restored from registry | `tokenId` | +| `com.trustvc.etr.registry_paused` | Registry paused | — | +| `com.trustvc.etr.registry_unpaused` | Registry unpaused | — | +| `com.trustvc.etr.escrow_created` | New TitleEscrow deployed | `escrowAddress` | +| `com.trustvc.etr.token_received` | Escrow took custody | `escrowAddress` | +| `com.trustvc.etr.nomination` | Beneficiary nominee set | `nominee` | +| `com.trustvc.etr.beneficiary_transfer` | Beneficiary transferred | `fromBeneficiary`, `toBeneficiary` | +| `com.trustvc.etr.holder_transfer` | Holder transferred | `fromHolder`, `toHolder` | +| `com.trustvc.etr.return_to_issuer` | Token returned to issuer | — | +| `com.trustvc.etr.shred` | Token permanently destroyed | — | +| `com.trustvc.etr.reject_transfer_beneficiary` | Beneficiary transfer rejected | — | +| `com.trustvc.etr.reject_transfer_holder` | Holder transfer rejected | — | +| `com.trustvc.etr.reject_transfer_owners` | Both roles rejected simultaneously | — | + +--- + +## Signature Verification + +Every request includes an `X-TrustVC-Signature` header: + +``` +X-TrustVC-Signature: ed25519= +``` + +The signature is computed over the **raw request body bytes** using the Ed25519 private key configured in `SIGNING_PRIVATE_KEY`. Your receiver verifies it with the corresponding public key — the public key cannot forge payloads even if your receiver is compromised. + +:::important Verify on raw bytes +Parse the signature header before parsing the JSON body. Always verify the signature against the raw, unparsed body bytes — not a re-serialized object. +::: + +### Node.js / TypeScript + +```typescript +import crypto from 'node:crypto'; +import fs from 'node:fs'; + +const publicKey = crypto.createPublicKey(fs.readFileSync('public.pem')); + +function verifyTrustVCWebhook(rawBody: Buffer, signatureHeader: string): boolean { + const b64url = signatureHeader.replace('ed25519=', ''); + const signature = Buffer.from(b64url, 'base64url'); + return crypto.verify(null, rawBody, publicKey, signature); +} + +// Express example +app.post('/trustvc/events', express.raw({ type: 'application/json' }), (req, res) => { + if (!verifyTrustVCWebhook(req.body, req.headers['x-trustvc-signature'] as string)) { + return res.status(401).send('Invalid signature'); + } + const event = JSON.parse(req.body.toString()); + // handle event... + res.status(200).send('ok'); +}); +``` + +### Python + +```python +import base64 +from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey +from cryptography.hazmat.primitives.serialization import load_pem_public_key +from cryptography.exceptions import InvalidSignature + +with open('public.pem', 'rb') as f: + public_key = load_pem_public_key(f.read()) + +def verify_trustvc_webhook(raw_body: bytes, signature_header: str) -> bool: + b64url = signature_header.replace('ed25519=', '') + # base64url → standard base64 + padding = '=' * (-len(b64url) % 4) + signature = base64.b64decode(b64url.replace('-', '+').replace('_', '/') + padding) + try: + public_key.verify(signature, raw_body) + return True + except InvalidSignature: + return False +``` + +### Getting the Public Key + +If you generated the key with `openssl rand -base64 32` (a raw seed), derive the public key: + +```bash +# Convert raw seed to PEM private key, then extract public key +echo "YOUR_BASE64_SEED" | base64 -d > seed.bin +openssl pkey -inform DER -in <(printf '\x30\x2e\x02\x01\x00\x30\x05\x06\x03\x2b\x65\x70\x04\x22\x04\x20'; cat seed.bin) -pubout -out public.pem +``` + +If you generated with `openssl genpkey`, you already have `public.pem` from [Quick Start Step 2](./quick-start#step-2--generate-a-signing-key). diff --git a/sidebars.json b/sidebars.json index deb27ce..a0708c1 100644 --- a/sidebars.json +++ b/sidebars.json @@ -52,6 +52,20 @@ "how-tos/decentralized-renderer/template-advanced-features" ] }, + { + "label": "Chain Events (ETR Webhook)", + "type": "category", + "items": [ + "how-tos/chain-events/overview", + "how-tos/chain-events/quick-start", + "how-tos/chain-events/configuration", + "how-tos/chain-events/webhook-payload", + "how-tos/chain-events/registry-api", + "how-tos/chain-events/rate-limits", + "how-tos/chain-events/opentelemetry", + "how-tos/chain-events/scaling" + ] + }, "how-tos/implementing-qr-codes", { "label": "OpenAttestation (Legacy)", diff --git a/static/docs/chain-events/config.example.json b/static/docs/chain-events/config.example.json new file mode 100644 index 0000000..ec66ece --- /dev/null +++ b/static/docs/chain-events/config.example.json @@ -0,0 +1,117 @@ +{ + "chains": [ + { + "chainKey": "ethereum-sepolia", + "rpcUrl": "wss://eth-sepolia.g.alchemy.com/v2/YOUR_ALCHEMY_API_KEY", + "registryAddresses": [ + "0xYourTokenRegistryAddress" + ], + "replayFromBlock": 6000000, + "replayBatchSize": 10000, + "replayDelayMs": 500, + "confirmations": 1 + }, + { + "chainKey": "ethereum", + "rpcUrl": "wss://eth-mainnet.g.alchemy.com/v2/YOUR_ALCHEMY_API_KEY", + "registryAddresses": [], + "replayFromBlock": 0, + "replayBatchSize": 10000, + "replayDelayMs": 500, + "confirmations": 3 + }, + { + "chainKey": "polygon", + "rpcUrl": "wss://polygon-mainnet.g.alchemy.com/v2/YOUR_ALCHEMY_API_KEY", + "registryAddresses": [], + "replayFromBlock": 0, + "replayBatchSize": 5000, + "replayDelayMs": 500, + "confirmations": 2 + }, + { + "chainKey": "polygon-amoy", + "rpcUrl": "wss://polygon-amoy-bor-rpc.publicnode.com", + "registryAddresses": [], + "replayFromBlock": 0, + "replayBatchSize": 5000, + "replayDelayMs": 500, + "confirmations": 1 + }, + { + "chainKey": "xdc", + "rpcUrl": "wss://rpc.xinfin.network/ws", + "registryAddresses": [], + "replayFromBlock": 0, + "replayBatchSize": 5000, + "replayDelayMs": 0, + "confirmations": 1 + }, + { + "chainKey": "xdc-apothem", + "rpcUrl": "wss://rpc.apothem.network/ws", + "registryAddresses": [], + "replayFromBlock": 0, + "replayBatchSize": 5000, + "replayDelayMs": 0, + "confirmations": 1 + }, + { + "chainKey": "stability", + "rpcUrl": "https://rpc.stabilityprotocol.com/zgt/YOUR_STABILITY_API_KEY", + "registryAddresses": [], + "pollIntervalMs": 10000, + "replayFromBlock": 0, + "replayBatchSize": 5000, + "replayDelayMs": 5000, + "confirmations": 1 + }, + { + "chainKey": "stability-testnet", + "rpcUrl": "https://rpc.testnet.stabilityprotocol.com/zgt/YOUR_STABILITY_API_KEY", + "registryAddresses": [], + "pollIntervalMs": 10000, + "replayFromBlock": 0, + "replayBatchSize": 5000, + "replayDelayMs": 5000, + "confirmations": 1 + }, + { + "chainKey": "astron", + "rpcUrl": "https://rpc.astron.network", + "registryAddresses": [], + "pollIntervalMs": 10000, + "replayFromBlock": 0, + "replayBatchSize": 5000, + "replayDelayMs": 0, + "confirmations": 1 + }, + { + "chainKey": "astron-testnet", + "rpcUrl": "https://rpc.testnet.astron.network", + "registryAddresses": [], + "pollIntervalMs": 10000, + "replayFromBlock": 0, + "replayBatchSize": 5000, + "replayDelayMs": 0, + "confirmations": 1 + } + ], + "webhook": { + "url": "https://your-system.example.com/trustvc/events", + "timeoutMs": 10000, + "retryAttempts": 3, + "retryBackoffMs": 1000, + "headers": { + "Authorization": "Bearer ${WEBHOOK_SECRET}" + }, + "maxConcurrentDeliveries": 10, + "maxQueueSize": 10000 + }, + "server": { + "port": 8080, + "host": "0.0.0.0", + "workerProcesses": true, + "logLevel": "info" + } +} diff --git a/static/docs/chain-events/grafana-fleet-health.json b/static/docs/chain-events/grafana-fleet-health.json new file mode 100644 index 0000000..6731ddd --- /dev/null +++ b/static/docs/chain-events/grafana-fleet-health.json @@ -0,0 +1,320 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "Prometheus / Mimir datasource connected to your OTel collector", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { "type": "grafana", "id": "grafana", "name": "Grafana", "version": "10.0.0" }, + { "type": "datasource", "id": "prometheus", "name": "Prometheus", "version": "1.0.0" }, + { "type": "panel", "id": "stat", "name": "Stat", "version": "" }, + { "type": "panel", "id": "timeseries", "name": "Time series", "version": "" }, + { "type": "panel", "id": "table", "name": "Table", "version": "" } + ], + "uid": "trustvc-fleet-health", + "title": "TrustVC — Fleet & Chain Health", + "description": "Fleet health, per-instance status, chain connection, state-change events, and escrow subscriptions", + "schemaVersion": 39, + "version": 1, + "editable": true, + "graphTooltip": 1, + "time": { "from": "now-1h", "to": "now" }, + "refresh": "15s", + "tags": ["trustvc", "fleet"], + "templating": { + "list": [ + { + "name": "instance", + "label": "Instance", + "type": "query", + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "query": "label_values(trustvc_instance_health, instance)", + "includeAll": true, + "allValue": ".*", + "multi": true, + "refresh": 2, + "sort": 1, + "current": {} + } + ] + }, + "panels": [ + { + "id": 100, + "type": "row", + "title": "Fleet Overview", + "collapsed": false, + "gridPos": { "x": 0, "y": 0, "w": 24, "h": 1 } + }, + { + "id": 1, + "type": "stat", + "title": "Active Instances", + "description": "Running process replicas currently emitting metrics", + "gridPos": { "x": 0, "y": 1, "w": 4, "h": 4 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "background", "graphMode": "none", "justifyMode": "center", "orientation": "auto", "textMode": "auto" }, + "fieldConfig": { + "defaults": { + "noValue": "0", + "thresholds": { "mode": "absolute", "steps": [{ "color": "red", "value": null }, { "color": "green", "value": 1 }] } + }, + "overrides": [] + }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "count(trustvc_instance_health{instance=~\"$instance\"})", "legendFormat": "", "refId": "A", "instant": true }] + }, + { + "id": 2, + "type": "stat", + "title": "Healthy Instances", + "description": "Instances where no chain has permanently failed", + "gridPos": { "x": 4, "y": 1, "w": 4, "h": 4 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "background", "graphMode": "none", "justifyMode": "center" }, + "fieldConfig": { + "defaults": { + "noValue": "0", + "thresholds": { "mode": "absolute", "steps": [{ "color": "red", "value": null }, { "color": "green", "value": 1 }] } + }, + "overrides": [] + }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "sum(trustvc_instance_health{instance=~\"$instance\"} == 1) or vector(0)", "legendFormat": "", "refId": "A", "instant": true }] + }, + { + "id": 3, + "type": "stat", + "title": "Degraded Instances", + "description": "Instances with at least one permanently failed chain", + "gridPos": { "x": 8, "y": 1, "w": 4, "h": 4 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "background", "graphMode": "none", "justifyMode": "center" }, + "fieldConfig": { + "defaults": { + "noValue": "0", + "thresholds": { "mode": "absolute", "steps": [{ "color": "green", "value": null }, { "color": "red", "value": 1 }] } + }, + "overrides": [] + }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "sum(trustvc_instance_health{instance=~\"$instance\"} == 0) or vector(0)", "legendFormat": "", "refId": "A", "instant": true }] + }, + { + "id": 4, + "type": "stat", + "title": "Total Active Chains", + "description": "Sum of chains running across all instances", + "gridPos": { "x": 12, "y": 1, "w": 4, "h": 4 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "background", "graphMode": "none", "justifyMode": "center" }, + "fieldConfig": { + "defaults": { + "noValue": "0", + "thresholds": { "mode": "absolute", "steps": [{ "color": "red", "value": null }, { "color": "green", "value": 1 }] } + }, + "overrides": [] + }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "sum(trustvc_instance_active_chains{instance=~\"$instance\"}) or vector(0)", "legendFormat": "", "refId": "A", "instant": true }] + }, + { + "id": 5, + "type": "stat", + "title": "Total Active Escrows", + "description": "TitleEscrow subscriptions across all chains and instances", + "gridPos": { "x": 16, "y": 1, "w": 4, "h": 4 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "value", "graphMode": "none", "justifyMode": "center" }, + "fieldConfig": { + "defaults": { + "noValue": "0", + "thresholds": { "mode": "absolute", "steps": [{ "color": "blue", "value": null }] } + }, + "overrides": [] + }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "sum(trustvc_instance_total_escrows{instance=~\"$instance\"}) or vector(0)", "legendFormat": "", "refId": "A", "instant": true }] + }, + { + "id": 6, + "type": "stat", + "title": "Active Worker Processes", + "description": "Forked child worker processes across all instances", + "gridPos": { "x": 20, "y": 1, "w": 4, "h": 4 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "reduceOptions": { "calcs": ["lastNotNull"] }, "colorMode": "value", "graphMode": "none", "justifyMode": "center" }, + "fieldConfig": { + "defaults": { + "noValue": "0", + "thresholds": { "mode": "absolute", "steps": [{ "color": "blue", "value": null }] } + }, + "overrides": [] + }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "sum(trustvc_instance_active_workers{instance=~\"$instance\"}) or vector(0)", "legendFormat": "", "refId": "A", "instant": true }] + }, + { + "id": 101, + "type": "row", + "title": "Per-Instance Status", + "collapsed": false, + "gridPos": { "x": 0, "y": 5, "w": 24, "h": 1 } + }, + { + "id": 7, + "type": "table", + "title": "Instance Status", + "description": "One row per active process replica — health, uptime, chains, workers, escrows", + "gridPos": { "x": 0, "y": 6, "w": 16, "h": 7 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "showHeader": true, "cellHeight": "sm", "footer": { "show": false }, "sortBy": [{ "desc": false, "displayName": "Instance" }] }, + "fieldConfig": { + "defaults": { "custom": { "align": "left", "displayMode": "auto" } }, + "overrides": [ + { + "matcher": { "id": "byName", "options": "Health" }, + "properties": [ + { "id": "mappings", "value": [{ "type": "value", "options": { "1": { "text": "ok", "color": "green" }, "0": { "text": "degraded", "color": "red" } } }] }, + { "id": "custom.displayMode", "value": "color-background" } + ] + }, + { "matcher": { "id": "byName", "options": "Uptime" }, "properties": [{ "id": "unit", "value": "s" }, { "id": "custom.width", "value": 110 }] } + ] + }, + "transformations": [ + { "id": "labelsToFields", "options": { "valueLabel": "instance" } }, + { "id": "merge", "options": {} }, + { "id": "organize", "options": { "renameByName": { "trustvc_instance_health": "Health", "Value #A": "Health", "trustvc_instance_uptime_seconds": "Uptime", "Value #B": "Uptime", "trustvc_instance_active_chains": "Chains", "Value #C": "Chains", "trustvc_instance_active_workers": "Workers", "Value #D": "Workers", "trustvc_instance_total_escrows": "Escrows", "Value #E": "Escrows", "instance": "Instance" }, "excludeByName": { "Time": true, "__name__": true } } } + ], + "targets": [ + { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_instance_health{instance=~\"$instance\"}", "refId": "A", "instant": true, "format": "table" }, + { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_instance_uptime_seconds{instance=~\"$instance\"}", "refId": "B", "instant": true, "format": "table" }, + { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_instance_active_chains{instance=~\"$instance\"}", "refId": "C", "instant": true, "format": "table" }, + { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_instance_active_workers{instance=~\"$instance\"}", "refId": "D", "instant": true, "format": "table" }, + { "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_instance_total_escrows{instance=~\"$instance\"}", "refId": "E", "instant": true, "format": "table" } + ] + }, + { + "id": 8, + "type": "timeseries", + "title": "Instance Uptime", + "description": "Gaps indicate process restarts", + "gridPos": { "x": 16, "y": 6, "w": 8, "h": 7 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "tooltip": { "mode": "multi", "sort": "none" }, "legend": { "displayMode": "list", "placement": "bottom" } }, + "fieldConfig": { "defaults": { "unit": "s", "custom": { "lineWidth": 2, "fillOpacity": 10 } }, "overrides": [] }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_instance_uptime_seconds{instance=~\"$instance\"}", "legendFormat": "{{instance}}", "refId": "A" }] + }, + { + "id": 102, + "type": "row", + "title": "Chain Health", + "collapsed": false, + "gridPos": { "x": 0, "y": 13, "w": 24, "h": 1 } + }, + { + "id": 9, + "type": "timeseries", + "title": "Chain RPC Connection Status", + "description": "1 = connected, 0 = not connected. Drops indicate disconnections.", + "gridPos": { "x": 0, "y": 14, "w": 12, "h": 8 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "tooltip": { "mode": "multi", "sort": "none" }, "legend": { "displayMode": "list", "placement": "bottom" } }, + "fieldConfig": { + "defaults": { + "min": 0, "max": 1, + "custom": { "lineWidth": 2, "fillOpacity": 15, "drawStyle": "line", "spanNulls": false }, + "thresholds": { "mode": "absolute", "steps": [{ "color": "red", "value": null }, { "color": "green", "value": 1 }] } + }, + "overrides": [] + }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_chain_connected{instance=~\"$instance\"}", "legendFormat": "{{chain}} ({{instance}})", "refId": "A" }] + }, + { + "id": 10, + "type": "timeseries", + "title": "Chain Reconnect Attempts", + "description": "Rising line = persistent connection trouble", + "gridPos": { "x": 12, "y": 14, "w": 12, "h": 8 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "tooltip": { "mode": "multi", "sort": "none" }, "legend": { "displayMode": "list", "placement": "bottom" } }, + "fieldConfig": { + "defaults": { + "custom": { "lineWidth": 2, "fillOpacity": 10 }, + "thresholds": { "mode": "absolute", "steps": [{ "color": "green", "value": null }, { "color": "yellow", "value": 3 }, { "color": "red", "value": 10 }] } + }, + "overrides": [] + }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_chain_reconnect_attempts{instance=~\"$instance\"}", "legendFormat": "{{chain}} ({{instance}})", "refId": "A" }] + }, + { + "id": 103, + "type": "row", + "title": "State-Change Events", + "collapsed": false, + "gridPos": { "x": 0, "y": 22, "w": 24, "h": 1 } + }, + { + "id": 11, + "type": "timeseries", + "title": "Chain State Transitions (rate)", + "description": "Spikes = flapping connection. Labels show from_status → to_status.", + "gridPos": { "x": 0, "y": 23, "w": 14, "h": 8 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "tooltip": { "mode": "multi", "sort": "none" }, "legend": { "displayMode": "list", "placement": "bottom" } }, + "fieldConfig": { + "defaults": { + "unit": "ops", + "custom": { "lineWidth": 2, "fillOpacity": 10, "drawStyle": "bars", "barAlignment": 0 } + }, + "overrides": [] + }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "rate(trustvc_chain_state_changes_total{instance=~\"$instance\"}[5m])", "legendFormat": "{{chain}}: {{from_status}} → {{to_status}}", "refId": "A" }] + }, + { + "id": 12, + "type": "table", + "title": "State Transition Counts", + "description": "Total transitions since process start", + "gridPos": { "x": 14, "y": 23, "w": 10, "h": 8 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "showHeader": true, "cellHeight": "sm", "footer": { "show": false } }, + "fieldConfig": { "defaults": { "custom": { "align": "left", "displayMode": "auto" } }, "overrides": [] }, + "transformations": [ + { "id": "organize", "options": { "renameByName": { "chain": "Chain", "from_status": "From", "to_status": "To", "Value": "Count" }, "excludeByName": { "Time": true, "__name__": true } } } + ], + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_chain_state_changes_total{instance=~\"$instance\"}", "refId": "A", "instant": true, "format": "table" }] + }, + { + "id": 104, + "type": "row", + "title": "Event Processing", + "collapsed": false, + "gridPos": { "x": 0, "y": 31, "w": 24, "h": 1 } + }, + { + "id": 13, + "type": "timeseries", + "title": "Active Escrows per Chain", + "description": "Grows as new tokens are minted", + "gridPos": { "x": 0, "y": 32, "w": 12, "h": 8 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "tooltip": { "mode": "multi", "sort": "none" }, "legend": { "displayMode": "list", "placement": "bottom" } }, + "fieldConfig": { "defaults": { "min": 0, "custom": { "lineWidth": 2, "fillOpacity": 15 } }, "overrides": [] }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_chain_active_escrows{instance=~\"$instance\"}", "legendFormat": "{{chain}} ({{instance}})", "refId": "A" }] + }, + { + "id": 14, + "type": "timeseries", + "title": "Last Seen Block per Chain", + "description": "Flat line = listener stalled", + "gridPos": { "x": 12, "y": 32, "w": 12, "h": 8 }, + "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, + "options": { "tooltip": { "mode": "multi", "sort": "none" }, "legend": { "displayMode": "list", "placement": "bottom" } }, + "fieldConfig": { "defaults": { "custom": { "lineWidth": 2, "fillOpacity": 10 } }, "overrides": [] }, + "targets": [{ "datasource": { "type": "prometheus", "uid": "${DS_PROMETHEUS}" }, "expr": "trustvc_chain_last_seen_block{instance=~\"$instance\"}", "legendFormat": "{{chain}} ({{instance}})", "refId": "A" }] + } + ], + "annotations": { "list": [] }, + "links": [] +} diff --git a/static/docs/chain-events/grafana-webhook-events.json b/static/docs/chain-events/grafana-webhook-events.json new file mode 100644 index 0000000..6523921 --- /dev/null +++ b/static/docs/chain-events/grafana-webhook-events.json @@ -0,0 +1,1344 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + }, + { + "name": "DS_TEMPO", + "label": "Tempo", + "description": "", + "type": "datasource", + "pluginId": "tempo", + "pluginName": "Tempo" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "10.0.0" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "datasource", + "id": "tempo", + "name": "Tempo", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + }, + { + "type": "panel", + "id": "table", + "name": "Table", + "version": "" + }, + { + "type": "panel", + "id": "text", + "name": "Text", + "version": "" + } + ], + "annotations": { + "list": [] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": null, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 100, + "title": "Overview", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + } + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 0, + "y": 1 + }, + "id": 1, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "textMode": "auto" + }, + "title": "Uptime", + "type": "stat", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trustvc_instance_uptime_seconds", + "legendFormat": "uptime", + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 0.5 + } + ] + }, + "unit": "short" + } + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 4, + "y": 1 + }, + "id": 2, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "textMode": "auto" + }, + "title": "Chains Connected", + "type": "stat", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(trustvc_chain_connected)", + "legendFormat": "connected", + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + } + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 8, + "y": 1 + }, + "id": 3, + "options": { + "colorMode": "background", + "graphMode": "none", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "textMode": "auto" + }, + "title": "Active Escrows", + "type": "stat", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(trustvc_chain_active_escrows)", + "legendFormat": "escrows", + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + } + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 1 + }, + "id": 4, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "textMode": "auto" + }, + "title": "Total Delivered", + "type": "stat", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(increase(trustvc_webhook_delivered_total[24h]))", + "legendFormat": "delivered", + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 1 + }, + { + "color": "red", + "value": 5 + } + ] + }, + "unit": "short" + } + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 16, + "y": 1 + }, + "id": 5, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "textMode": "auto" + }, + "title": "Total Failed", + "type": "stat", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum(increase(trustvc_webhook_failed_total[24h]))", + "legendFormat": "failed", + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "yellow", + "value": 50 + }, + { + "color": "red", + "value": 200 + } + ] + }, + "unit": "short" + } + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 20, + "y": 1 + }, + "id": 6, + "options": { + "colorMode": "background", + "graphMode": "area", + "justifyMode": "center", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ] + }, + "textMode": "auto" + }, + "title": "Queue Depth", + "type": "stat", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trustvc_webhook_queue_depth", + "legendFormat": "queue", + "refId": "A" + } + ] + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 5 + }, + "id": 102, + "title": "Webhook Delivery", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "failed" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "red", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 10, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "sum" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "title": "Delivery Rate (per minute)", + "type": "timeseries", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum by (event_type) (rate(trustvc_webhook_delivered_total[1m]) * 60)", + "legendFormat": "delivered • {{event_type}}", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum by (event_type) (rate(trustvc_webhook_failed_total[1m]) * 60)", + "legendFormat": "failed • {{event_type}}", + "refId": "B" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "ms" + } + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 11, + "options": { + "legend": { + "calcs": [ + "mean", + "p95" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "title": "Delivery Duration (p50 / p95 / p99)", + "type": "timeseries", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "histogram_quantile(0.50, sum by (le) (rate(trustvc_webhook_delivery_duration_ms_bucket[5m])))", + "legendFormat": "p50", + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "histogram_quantile(0.95, sum by (le) (rate(trustvc_webhook_delivery_duration_ms_bucket[5m])))", + "legendFormat": "p95", + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "histogram_quantile(0.99, sum by (le) (rate(trustvc_webhook_delivery_duration_ms_bucket[5m])))", + "legendFormat": "p99", + "refId": "C" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 20, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "line+area", + "steps": [ + { + "color": "transparent", + "value": null + }, + { + "color": "red", + "value": 200 + } + ] + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 200 + } + ] + }, + "unit": "short" + } + }, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 14 + }, + "id": 12, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "max" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "title": "Queue Depth", + "type": "timeseries", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trustvc_webhook_queue_depth", + "legendFormat": "pending events", + "refId": "A" + } + ] + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 20 + }, + "id": 103, + "title": "Chain Status", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [ + { + "options": { + "0": { + "color": "red", + "index": 0, + "text": "Disconnected" + }, + "1": { + "color": "green", + "index": 1, + "text": "Connected" + } + }, + "type": "value" + } + ], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "red", + "value": null + }, + { + "color": "green", + "value": 1 + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 21 + }, + "id": 20, + "options": { + "cellHeight": "sm", + "footer": { + "countRows": false, + "fields": "", + "reducer": [ + "sum" + ], + "show": false + }, + "showHeader": true, + "sortBy": [ + { + "desc": false, + "displayName": "Chain" + } + ] + }, + "title": "Chain Status Table", + "transformations": [ + { + "id": "merge", + "options": {} + }, + { + "id": "organize", + "options": { + "excludeByName": { + "Time": true, + "__name__": true, + "job": true, + "instance": true + }, + "renameByName": { + "chain": "Chain", + "transport": "Transport", + "Value #connected": "Connected", + "Value #last_block": "Last Block", + "Value #escrows": "Active Escrows", + "Value #reconnects": "Reconnects" + } + } + } + ], + "type": "table", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trustvc_chain_connected", + "legendFormat": "{{chain}}", + "refId": "connected", + "instant": true + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trustvc_chain_last_seen_block", + "legendFormat": "{{chain}}", + "refId": "last_block", + "instant": true + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trustvc_chain_active_escrows", + "legendFormat": "{{chain}}", + "refId": "escrows", + "instant": true + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trustvc_chain_reconnect_attempts", + "legendFormat": "{{chain}}", + "refId": "reconnects", + "instant": true + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "stepAfter", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + } + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 29 + }, + "id": 21, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "title": "Active Escrows per Chain", + "type": "timeseries", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trustvc_chain_active_escrows", + "legendFormat": "{{chain}}", + "refId": "A" + } + ] + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + } + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 29 + }, + "id": 22, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "title": "Latest Block per Chain", + "type": "timeseries", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "trustvc_chain_last_seen_block", + "legendFormat": "{{chain}}", + "refId": "A" + } + ] + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 37 + }, + "id": 104, + "title": "Traces", + "type": "row" + }, + { + "datasource": { + "type": "tempo", + "uid": "${DS_TEMPO}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "bars", + "fillOpacity": 100, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "s" + } + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 38 + }, + "id": 30, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "title": "Escrow Replay Duration (from Traces)", + "type": "timeseries", + "targets": [ + { + "datasource": { + "type": "tempo", + "uid": "${DS_TEMPO}" + }, + "filters": [ + { + "id": "service-name", + "operator": "=", + "scope": "resource", + "tag": "service.name", + "value": "trustvc-webhook-events", + "valueType": "string" + }, + { + "id": "span-name", + "operator": "=", + "scope": "span", + "tag": "name", + "value": "escrow.historical-replay", + "valueType": "string" + } + ], + "limit": 20, + "queryType": "traceql", + "query": "{resource.service.name=\"trustvc-webhook-events\" && name=\"escrow.historical-replay\"}", + "refId": "A", + "tableType": "spans" + } + ] + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 46 + }, + "id": 200, + "title": "Chain Events", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "short" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 24, + "x": 0, + "y": 47 + }, + "id": 201, + "options": { + "legend": { + "calcs": [ + "sum" + ], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "desc" + } + }, + "title": "On-chain Events Detected (by type, per minute)", + "type": "timeseries", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "sum by (chain, event_type) (rate(trustvc_chain_events_received_total[5m]) * 60)", + "legendFormat": "{{chain}} / {{event_type}}", + "refId": "A" + } + ] + } + ], + "refresh": "30s", + "schemaVersion": 39, + "tags": [ + "trustvc", + "webhook", + "etr" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": {}, + "timezone": "browser", + "title": "TrustVC Webhook Events", + "uid": "trustvc-webhook-events", + "version": 1 +} diff --git a/static/docs/etr-listener/how-it-works.png b/static/docs/etr-listener/how-it-works.png new file mode 100644 index 0000000000000000000000000000000000000000..149f8264536c354454fa9e3027d4cd5499702b37 GIT binary patch literal 118357 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%0AY5-rv?BI#%iv z8B$YnQA>kCECs=0#>{+t?Ox*J`C>wmIYF465Ck%+Q*eMjvPBBgXF$!^eURpV7XGCF zlGX5&77s2dI5qnlzW56Xv<2_4e)36q9lRy0NyYot?M=5Wl{jqY*8iSe;Jc>n1E2ADYT^rutI(&ejje zy&QPWzX6xl8elP_qf=?fsCu!rbfrEvVoX z9i2fw_I^gnDVZ|`=9kZ|yf0}dp1i&qywpxs`&ths`_vQk4)y%8P6FrO?H>9rTj9mY zHz4bCQ_e4%+|vCo=+E?msfE2Iit|+U*C@IsONSds>J$9Hn4eQ}ayxOw*ge%ScJ!?d z%I@{wdd5@Qh2ja}7IwD)4|ko$Zb9{^T!0Q6B1K&I3E~|^oF@1_`FY&^i`2~GDk!9X z4{HI#@S7>oFkj32Pk15QsBLD`J^Ur;e2h{LaLK_y?G?2gmV`fpE{AQk8-^yeRL9k- zK!?@G*T?9jXI3xg6zSO1EUM_c1{P_aL1YS-RlLh1OI@C2NP%)`wEu2@tql)}4vj_n6SvlzE=@lWNa1xkow^Fz zp0_DQfu?OaEN(QQ4fl6A1{q2zsVFn?PQ9^{U_tGs0;*y@z;>gkIU)>SR{^=W0yh)1A zn?&*aXXqqw9!@l)%?b9j%x7C}wqGkg&%_^-R$z6_7}^B+d}83jf4gc0Jy$lbGFCX= zlGl)SK3DYAVM4U+(z|rM54pN=4?q*`PdpKzTXn#< zPiqR5y%J}@csXAL&x0aitseLy+Fo#^PS>R1YX0r++^e12E9_MdV4#b}?MnA=&oH0J zCx6GL&F7HT$y`7c*^v zqW(R(!V8NTA^~kkpZsf;M}7^bmET^Tv$77%zh|wh7&s?l8@ydK6tJN8>DtyLZ+XjQ z&=U-{%J6Dr!Z9kcZd-h1#eCiSvi-oxB5~Dv!C$~F3x5+iGRE>pIJGm{`T?nJt+~&O zF9}M2pWmkK8!OTNswz^-TPGwZs86Szil8ybzj*aL%Kd_F%D@oyCaufaoSH{bti1WR zk7gY@OB|@u(DG8*aKL@=`h|(Q$!DM1eJiroT?6g}e^jXyX-#zX2-0i}h9C zl1}%-zf`lF0cEgsU>A)ux$4h~V1bB*8xV`)_w8lvUq4lTq#BMSRge=i!Ne#pN@{$ekwTGEXR#jm*&+Q)Ze(3Z~i4+PoPfG zS*_adPa)k0AC-EC)0a}GdpcKNmr!|`06RKkDZE-FQ$;DC+zz8gi&9sJ;uMQ{5G*FN z5JLITI{Vg#rgGnM@5enncd%zgWo;sPE0|s5;X{eoqo*m4KV5aV$yYi7;ReY-ymjHB ztwLDatbwx|H;Z7Rvkf@%XZ4LjCsm6&%$^_bF|q8T`io`KC9H)QoE(M91cJYuLw@^$ z(2+>`X+(i+(1mT2C+otWUq&#$p6I@%wQx~k7%%+L^h9| zIlnb^C_-}cClA#KuE42|vee&(CYEaEe0K_?J^DhA`sHu0?@)3h3ch0WDt^jt#LC{) zW3ufY=_D&ycI!&7w4C2~K~yw4l>l>YR(`BUtAeu{d5Bdu11(y$eQWfX^OLXlZ4T#b zloeVjPcwdNHS|&>o}DdNJUO@4XUY@S>dUGQ`nHisdO}J9; zvG%g@0&f_RP4Sb;rI}BbjK>PKION@GRdphMT2)T}CH=5oSjee(|Owl>wmD zPV8Gtle-)lr-Oq%fETO$HSR@<{X_|eiJWMAste5}&_numd`79o$(2k2P`Fw8(KgRN zxW~*i^5(7FdKB!gG+%05up8*orXo@*of4T`gzCpjgC?hn-0pIb=aaZWDliHOTQ66C>kCim{E;)PE-9Wq5Ll<;w{iQ6 zKJu_8IKWVaciH|sXg8V{? z^)|=@bEC~(^gIrH0*r0r#m@^K$yitW+CI4wCgr+PUke*q-U{#%IW9bv?Dpe%I?cCC2fBBE2*6n#H$%Siv3>93!jzO$KpLr# z_{*VV^`n2y#Ayfm#^(`L(UP#fi&AFH+ARLE~79V1hMHjzfCLT7B4r|28xatOxm<+-MK)A zx{r=OP7G#KitpuOe8@9LN%pDaIz*w}#9%TGr&O4VwbH~U(ek3}>@5}7iyxjZp<1ucIeLWmQc zJnYtO;7)V?rYJb-3(pF;;b!oUg}Ae^RX6RdP#&`H?xJoCtpuT5Bz~;it{#&E#vvsf zGe33&>q1;e7QsgfF0kw1gpM?1OIe?nLl%-ezjvbgR)OuuGpyi%fcz3qV?$fH7iFhV zT3??~#kPkXy?^O9SLZ~Y^75sb=|1Uzi-$`lXjm@J#0(Bkd?Wke0BoxiaUUKKW=Orl zynM~sQa5b9d;0Rj3r$064phgew$v>p!$~@wQn6_R593mwy+Mn9TUnregmM$*q%-)o z{4{q)Q{h#}q_;Dj<>>lK{?&pDH%p}bhSirgA6kI~L*v)Wq)T#JKV_wzjnA;ISt~(K zE7ALd`bR>njOl-Ed7%x?(op`5-#QPAz-fk**tCr=@*5hw@*~#~3QR;XSr&;?e`B&x zAoCLlopz2n{gUDOMCX{oukp2$-Ql>iKrJ3Pg@3oWJaZw`g|v{vm(lZ<9uPZd@p}%1 zq4zsZR%`u$@-~58vR)0UP+$pJ+CWq%@DNopXocMK)*lfn?J}X!Y?!k+r}Hl7?jezA z(6{0NN4Ux*M5cKv8{O(5Om6Y@n!)?#1?D}E1E0BuR*V#oagQ+ApozQ??{i}u?to5< zWQ}w0E6VL>$*%wT@cQ$R=$*(4fjbmb#4aW2&to+-k{xt#$}o?Z3R70D`FoF6hJZp* zMNixkKQU4`m@mC`?;*9~r7_tsk62OV6nUE?^4GU>I^;&SQ|M^!Y6D|c@g7mv^~{Yb zQv3yckEEO*T@(=vDTQL6)$cie(;zG$fGqG+Na4q;%IE%&Yq4hM|ACK+)W3`p#4NNr zEILbDL1&5_r%vNT#(g3_L90&mJT{(cEmB9q8?7A0{1=ahGvUdYCd4^jGK2(6QSh@xxcc(JZ9@B$# zWODq(1)|O6JD7~ev?XY%=Dp^55RY)bXuc>Rn&XiMPG8IO2=69h;|pEjC(3S;tMn;h znF?I1nAR%y65_+#!8l%6oo8vHIeemN2HMkbFA#WcHmmjN8`3t0%X0HTPyq->N$Ev% z7C$);fA5vsOfEY_hdB*fu!wWPiZhLU#^2_k(#@++>K8kNUch3*v!C)(Za;ZpN^`qB z&37ybbQ}lO4jAe&t7iFlZajNSb%G=m0`^4MonjAGe^E)jNWv2p+_B@W!bQR{_g{8> zXZGIn1^Au{o&6pu&#D3AqO*$|BU5FmH|#zuvw6qnPu_h+`8S-A#{fB<5L^JVq)tB3 zi%X*k!brH%;E<_g^W#y$8JV_ip}S+b$+uP|PH%IJjgK|ejwc57I&4kUdj5H*fBnx2 zW9bJl`OG+?F8$0$a%LV~qeD&dabB!b$GWa&jv4e_^^Jrprhp7SsYanA-R4swNDW0g zt?9#^DS%Hn)rtDGtpt}xM%SZfpAsgbI*!B{cHauAFwPO^6qj(zq+64$mCSiKWOH0E z>u=>*o8cOspWj}`WpS!;9Dd8@Q z%o@2!5Am_g=IVH_OlUFK;V56#-|quRq{1cYdYv%@U+`rZLu@B|6+f{fKa%&1gYKsv z5BTzz@-)vRn|)P$!<)Dwmcm9s+L|lXIWbrCX}rU5!ub-i)e)Ob<*1^vySQF#Q*vEC znM-o39#ojV+4GvK?LQ8oIh=qvEPb_(usAnC^F6iluCalM=7%N&?w;C5GZv6j7{ddT zJJH*u`>@L-vrO)q2Ot&S9Jf*tfLl@+GCp4A*$RROF%5*l?`& zq?Lrn@W;ez=p~#mkJ$2Cx9?v&6SI=RuQm}(Clf#wog(=tJ*N?qWl$;zUdJtf^1$lxz!6K7_8 zxe|YV2chfN0kz775zPYs1bsjwV-(8&hq-=UBm@VF*iZl)>D0V9=I^X{IUR7+AB3ji zdE-&DHLaB6wy0-s6~(`=e@pvK66Aad*Nu%0CjE!LswL~|nUA#R1y~dgo2-QG`k58e zA<7eb)N*Fw4eR!RX9vNQ_2U70JgVuU;U)v7V?-5evSZaZC{`m6 zcSunqT<=mN;vus#o~{jzf=QiHnIyz*hN6iI-K)C3qwAMx`qj451EBFRio|`O7Zgx4 zpddquq6OQoKRpRc1p}v;IH145<41{Xa+*0j=`p z$jcg)!Ln2_5>rRO6;p{c>7xYK*SPalt$GMFj3PD@z|OK3x~#KcY8!kEccCN5%TU zFY?lE?6$@tp5i5f>=0wlw6aE<4_}Wl;rNR<6YqiSs`Lo1{^&r{@*Yh4a<55a0uYPt15?R zE{wYowBrv0s|>n@AuUi9(?8V17yqne;q$)wvlB=NPYFjcg(&?k!Jo`sJ=`y0)$$uX zuZt@~U|#Ove_jWWxI&k33o!Kvq$RFJbdf@n8C_ZOIX$N&SqC052nt?shv68dFmqQu zoDW661e(2J=Vrn*W&nNGtW%b5^C4ld4nilgEsiU=fb)2|hsKKyy*>8>FrY(An z=F=DBZ?Lp!LkT)+Rf#jbTWb9In4(cO(;k4OUIw^jP(l-?d=yzneLpQ}jnvk(0l>_f;y62%_7U2Urw+$FUlt=C)x(5dz zauJUD%woo-aV#R_EO`s93bSi6NQWdUHXkb|v7%WWP(Gd}QGKqgdw9?%K5r^B!iSLP$0$Bcy4%L!Q-E=ZF1Gouv` zQmr$flz`81UaukQL1Kw$K%AG%juEBFIVOZUg8qcMiRNX5)s`mx+|qzqqtw zEOzTruAQrj$mC9^Nl4PFgKZuRPbL^)%|k56i;dJVH*sZ%UBpE{kGb!fGjKnN-i~Y+ zYK$ZxFKW^@g1Sn;4o@UeI66wDxzGA~d5H%*lr$2ag?*FH(ocdGL&kUvOYRcfF14f7 z>(aAfbW!Fo;QxaVjAAEK-5j|xhJknF>*AUaxUKNz{EKTs%|5UIW;W`YU7>RUK43|Q zv4F7XJ2T+13lAz3hftN9yfTKHmdt_=WfKh<;?k;nw%}UvCQXvz8t$Syf|5Yh^U$%V z0o@YXg!CCcI|<@RF9k69l9;6Wk}vvVz-<&thv?XXo6>6tH7mcb*8}L;{}{oCY$Qz| zjqh}-0f6JsWU6u%O!34dfAvbpX(8hX>q7tj=0(eSK@>xS5L3+xd)X=!z1~SPfEtRB zHz$m=Uf8Fr%yc46bfK*8C>i-H7d`$^6dArQvD_#Q=4Yh&kcH3bfP|0+GuLR2tp+R0 zu7zbJ#F`uFA7I!QI|g~k!kLK?O$+DaY$;g2V2$DqYs!6#UnC^(5Lii*iMV3+3MB77 z$CbR@f2+L!5jR;FT{u750hx81s2p_Syk`$)EXebqCL8zX${drGf!!&oE_XnJBRW`< zeLV%Js^raX@-Ntdh$RdFmRvA3y!evX5%eZ9r`Vr10u9-U;}AMrR_R|EQI@cbwEjFA zuqWUE4295gxOCg<_;(YCnedY=LiHH6Yq``-G#ifP6pU66CHatL7)QcI-h@(vHqKBY zxF6w8dmeB!h2EMu(DHWJ6laYvlJiZw>pkV+*EbMG7gLhLLgSD5>d(ZOaG+OZeKtsx zSa9rMA`qVwOb;+LMi_>CfYP*|$SFZzDPKHk`!JEfMYu(H7=27h4$I`)9Sh#wxq=yo z%Mdd^%L*%r)nlmHCos^r5=NiULlM=&kf0W(1xw(yD>91v)Rf?odOR76MievcF_9ox z%VB3(`MnD$Vr@DjpR;nNELGg07+&OUqqx++=8K7pO$;#&SINz#{*4TW4&)%B=JX>a zPS`sYTHOF!@6md<1WpD&W!Jnu)z;MBKgQLbyi`PT>C?@Nnb(Nvc2EY8&dr1C0RKG% z+JM}#kLLd&W?X%YHsV4bGOkb<6=9LLlnz(Ck9~8`yxQAhijC;7DRS&}{hymlqN+K| z#EJhTJup&%cpSrMw8!jRR6mXVql-&LsJ;&?J`~#i>Kih_{xpN-_Rz_@Eqr(nxc9?> zzAG0IO&EFNhU*g^7&*ItT!xW`(Q>mW3?>b79Jt6%jIerzp&V&{b@>sd!=$Cp+i$K5 z?w!m+V*u9Y>V1XzjtDd_@d*Lk!=3G;iWnkL$!K=i9_j!sVli=yDUz#}KJ0%22TA%- zMBsTXpwyeYMmMq?YP8Q3lnkfU-qX&Y;elNPPAPU2AE2j;@`Hh$2BI0<2pok~Q|vS2 zI4r=4{=^@CJiS4oj$9WNMy4v%cj{t6er0epM;8U6QBI<`#RGmVxpQ|n<=Ly*C%I ztK0({k~TePjI+Z~NMBe%9TgeN%`sdfI?9b9aTxP!*h!~C^J1ZZ?<2BFqkWO1qspsV zXMO|eu{}Vy_daYMzzrpZ>nEu-yH6!&6`y<|H+p!AiP)OYOH^cP&lk|#Kp6^ z>B1EsTUhdxym+X=0zA5hQ3GQGrpnFxJ3rvCDr&HRg)(MyM*()^=zuv=7FfGY8DCbiSQKXfrLxH!!E+6j9l$v^>nWVBC1iH|bpEmb~xyQTeCQUiH2 zpoX&8Kb~CG54kXRlDo|!wk4|U2`r=(V@Z5t;V?0BR0^&(`&x0u3bv+*_X_PktStCS zHLg4HIAdU!9r&h{@M39kUu;1XwFUGO-?#EU@w6pr`vY?G5S37mN`)&_!LVP*8wc~@ zFjONN5r!n?Myd;i+c}A-i~-Yel5HvBPsN?@TzB}^) zeJm?rO{)YzG-(;2q;gMWV&R$vO=$AgP2+GaR0N7Ll!b=7xJ9ai^IiubJ!}^iU|WK% zW9qf1F?0cW0VWD|P&_bzz>==1t=;gWq|jSyAU|JltSRPvf#OYjeqqX-M-)5@PsPy* zcIgrYoDufaQrYQd9YtcOHAUR-Zn720#=*93xqUhTUn&KV(|944!!Z$L`#9jk-y@7d zdgRdf11*w=w4^<>qr8;re44uEMh|y>oS6Ghe4#EP!XGk5wMc?bfpWwlL3Z`1a3QiAMr1df z$QJ2Hx#^iA3QL}H5o115#o6Pj>{%k@ae-?NmDLS_^(b;SDi2u-$vsiL!)E)f*-<=X z#bUd2glwHedn6+_3Qa@I%9L4!=%E|sr&Jf9tQVl<5e&aHjKXUD9>}a#TEK?Vn13hr z#Xn_ZX$U()F-aCtedGI5l;o(rTB;-1R|#8g638F2i{--$Lu=EfSMo1&kq>DAM~SPe zMyO%J*AO?blp3#;^fkuAlM2}2Mo|wXqaGno$CEBHTs9o;$~=sgyr--c#~QKNfFmJQ zw#&7|ph-eHNG3@ms_Qr-j+tY8B$celiV0^G?kGbT5orIqa#EgUXKF39cKQIn$RvBD z0Qb3iWP~3XMYIu_2ZQ>v9#mF6>d6r*p670_*7ASG*Sp%;2V52}F6~?NuNjyPs{cA* z8&~JF&LVX=U1^K>%rK5A)BdDa$BisE!=V4rAlS3`@z=vVoODa}$Qr@}R`q4LA6f4q zed~{nQjG>Izv|R)P#zrZQ97Hs+`sXZkBz}-Sk|zqZd$=nB$nehZa(3-L&yRJE@T|= z8bi|fDP%4%$};^kfr;rP>9AE~HwMUVERfw`A=h95b4%j7T+otLTsBe1689vL79C-~ zDH(2$M|b>m9kUPlCvkxfCj)R^cUDM37CbYWXk*8k!8gNEN$Nt!k2IcVX*7PsP5p$T zXr0q%0KH=OGLI9S(ACTt^r6!)ChoUg(zgx-Jl;1{5KYZDwc+>mO)_Y$NhAN)Bpk|is}g!AxJqV|35a+kgdR9>Y*JjKp^2tAd_yaIAscQidnWD>*0ZQt-o9yV?g zhOXlFeIlwbaXp||QW$d980=cFgqGs>Txk6^R$g3cPqFa4+&y`L!_obp5CCsL(E)=s zL37RZvooTY_n!nJ0K4<&ydjns%G0<3P^N&AqS)ia9e%{h(A$0c2O-eYPXRa{>6CKr zvI)Ii{Qvn{+S90W@G%4jH`_toW8d(IVt~2cCQKc-4DA!O^5_HF7pj!3IOyLx5uQ2$ z`ONeM?=(#b}IjY z0%D9saGyffQ5U|@Vp==rgXrG>PM`(sEjobf$Mg+u|5*Z{iU|;}>>L{aUI)4H0QH+? zDXkDrA<;Rw#T{H1OaNHnrXN%3B>SF*gFhpAcK#}k1aAuvlA<|4P1q$myYBy)2e5g= zN*atxp&WH>db>Tn_W*MOtU~n&Wb$3i?hM_2E(|}Rm`VZj13Mwx8&vQsq0AIuDqzOp) zpf<^h5B&$oJ(yPo@eI6=?dBMIaRvwF;m-Fe3F3sfU1?|qVX*(M$jXk4fR=m^QLOTx zK>?cZmjNBr42FBkC2-^Zc_0o#yDP#MxB-iBN!wE%SQ0>jz_(%d^Pt33*Tsf;2-TIa z5bjgGgtW`}-$4n>Gop)=hmS3=bN?UP3Yad28a8)`@Cs1A1=i&E`9|u042NB!x1%{Y zDgby0lE16z+Q7kAA4@d5=aBmk9wC{)4g8r#k`)i^2z2|OnZ^^f98hq%fk`BH>2>;^ z&ouJ}Q2>EW|7SiR&=ck%r%H7uRdmNI^i;SM55ih zKy49ttjPkMO(1b;vf``I$QW)ItMsKL%6rvDX@gMU_1`DT7{*j5cP^l=`F~b==Z!q! zL~Of)9~43WE{mZ)Pz8bWK@{ihPkHwXD5(Kl7MH}Br0Pd+xAnh26V`2KH=hCyl>zz5 z(At9l74u*w>rHyDHmzIdd|4p#;uo6zH7rTi7A72ftd?VLd8MRshLcA3pwa<`5Qn_x z6#7gxJExl9t>BOl3VlzHC8D??M3LO*^lCsM@U;Nt#$e2{!tuHRSH~_Fbx$OdRZ*CT zyKDQv;ab~)*{xIC_AC^lRTX#l)fWP|T?W{&#YmaPU8Q>mdxvSUQRFwC=EoMC#vDh~ z^}!0idiUu$H##Y&o*^#?_@YI3JAS=767Haudg{pM_xkR(KX1M1-zbP}e^p%2JXr}l z0%@)O^1D%{%orkt9lo%Gc#O>mxZw72$3MWPu;&ZlLrTMtr|h-Pnn&l!hTDHEpeR9J z=KgR*pE;nWvM1dB#vfzxhh)wCd@fr0I@I-=@73{4R@WiaDNhG|SoIGH+SGK7({}H< zlTOJbxeyvf*}Jq8DA^yb3&N&MCn=;YBaab3*^vq0RL`geNyLM;&2W2b(N7BG+>E65 zCd?MxuV&N>5(QwE6jlBfj$dbkv;Y=qnh!O|1#O03nv)6+62(6%Ivs(f1lB5vp%T}@ zNM#RPZcPX1OmgQI%l19XfMTg&NEz-c>g*A%&uZ+jMC1)Zs5o`hO*`@t22yUuNi;Gd zx&J=ZMd{|}Y|S)GlEsUQ^tXISUtuYq#QX@ahV@A*(X8^}#o=<)`?Nj{^J+UA5<-_a3Q99}<)5{nsVp60`iFBV3HF`o6Zp8BI zy}AL>T%d49kjxE1%}ZQw8C&fVj`$E;*oMZXwxo+Z-aPUhl~Vw(u8OveS)KFh{7^dmn?Vhh6!yKcn4 zrvbkqpq+*r-aH2_Itcj<9EN^IP!L=4NX^_(rId+jWQbA|@T3}*qI{kxbpjCeg_H0_ z7ScMddZ~_-)_uhYlBMnRQE|jTgQuKv-6>5XNJXA>RU;lLO&y1;N61Xe&+#ST-;t?# zrL$$X@xw=@I<)B%xqm$(N3=*s)W-esFQ90` z*dx=QlSL&-PR%L>Ft8JI=_^LGx}Q<(yNND5Nu9vcdr*+Yf!DA(OX|TwY3&quH`OX6 z@UBJJIh_%T!0Chz+!XlORX8&$Wg5F-EL-ye7lUR|reH|;0e|PZ15B1a_7PBGkzgM{ z{@Vwz8AvGJz$0b);GB@}t!V1YEPgEb^Mi90_1g3a@!}%hBLnnJV|K+*S>XOIh)V># zt%4ldk^NPC#J)hiQ-$2lddcP_<8|AC$3|GPLG$w!JPmQ=0V%}IhkqRAfzYlaEZENG zhYy7p+`L%8cT=+UQzPe^J>qg}-}8j!IgA+yuSqH;5Tg12qCG%20h>bw>p}Y%#RBV7 zmnz*b7C&mPOKy*BNp_pV_R=jpoBLS^yMHXUS>aHQ6r~3gLEQgzLne;bR2Hvbs1dE* z4pD+LdXVWxag0q!V%{Q0*Fi=yybSv996V5s^Ay@FQ-5U-3G9a0!OVZi z2X0!)d;QHn!n4VcVRrM5QOi&g_7N12V9@P5plT3;9%bEJ8#^&5i0+f&zfGDJBq)fG zF^lR7Ye$=<;(=a+6&qop5wCx))VCV|)5a=5v&Yaus>LOq0jhmm(7h*9&6Ft=?z?&M z&3gmE@e2WFKg7b(*=>LS#(#fOZ{nD;_0OOWO#&tryR=P*J|x*s5)&jp$_hc5Eol}9 z0*}FA>K;`OF6a6p7oJ(EFR=cWLbUiju-86&`GD8K`7YvpjYf`_xat9p z++f(OqeXMbxyz-aw}3%&1(hcc8Hg%&oLNtt?kLeb02Xl$auk+HJxXk^0TpwAQKQi)v#B~YL}kf?HVlolFrT05ED zAMgqAIB_9V<6^Gwyj=VIae+NBC~Nu=@<#~6g|-UdRsfswqyaZ^>qK|oKFccJCbBY8 zb-F=8ULYJnLC&QgwPeK3UJ*-+b7ai6@Mg=lU$%IIVT@E`{f`K?!UDEpPUrI_gb6Y; zhyuVGBhSH3i*l-%psnX|tND-5C?$X~@0c^LZl`&o>u3LI2A792XxOH*kKa=7` zR@t#v`0t+AwyWTg5+kl4`;y=fjpD(duMS^Zi*6R_`^ga4$D4n(;sCYs z;mrZHakSqmPgd1L+Oh3`D6T^UI}(1(o1^|-qChdX9xs&T+WnH|6#Cj-gM=G9t_pcX zF*6_XC=h+xS+YH8xAX1`~=%@49k5QL=z#(Qzd&ZKx4;{F`{CtyM`JVa&;j$O8l2uI}m z`e&@BY>LE_QE=Gsmt*I$gEC1P1UurxIg54waUsz3plpRBS405@@qF+d4#n8Okg?0D zsmQL($~-Wia4uplm5^p+yoW)!?U{l-k)(sGCBt~rLgGd9!)42TTHsV8s!u;9>h`6T zx4AXWD0whM4=(BSDVP5lCt;} zT^MWqF{nyg?l4B&Zgagl^k9mE4o)6Y+qrXB6n<;b;9tw(X8DUcJlVe?Gpou}a4uQ^}{FV(vpB z5*HcIBKYk$>*IuB`=ym13j7S+cPowa*8_Qw?Zfs~70p;ieDUhfuY;$3+vKXAEZ91{ z?mfEEYVgkr12%9JminAz0#PeS-`*u++!mRtXmgux+{YGOtFr~xZ%C93?Wa6c@)zqk>l+d=Leh4Ouo^O z`->i63AL{E)tG1^6|o56lD-f)Bb$~oaitv_sxkhx_V6!@+4(l_O3FxTu3NGk{=zlE zV|N4w42U0-{BuO*Qp8l$y`q-pK!eO(KmHPr~rkp+O zXp%}|>Ag=P?~3b4_^y0>njgPykZBs=%Bkol^euS1Y=&mY^rh<;4XwocqWwkRG)t{s z1)9W9$XwNvxh!Vnu}!0irwd^d{DyTgAF7-KEI?8I z;p;bM6K#l|KPjPm?OV%quHu~1_uijoi_1ZUf`#?rUH1b@uXJiTtR4?#OvX6$Y%X<4k9WUy*be3O{9Eq;dUnRV{Q1FhLL+#1 zxgkEI(&78B^0t%S^I|I7%IdXtG!kEKydSKx2#D>zR6taCHt(+8&853#%;PKfYf1wr zPX2kb?GAXa-R`Ev@6J0-QM4L4r(`IaUxi$*`u5bQJXA09SJ4d3CR>qtxlQnz^HaJc z?%+KATc@eHj3+T;5kPob?(ze)|IS->X*#x6uleGudj5NrMBOes{LHlGZXgo zs`TCQJgzr);$0?nyCqIOJysC@^^I4N-}URd!zYg$vzrR=avnA(3tVdrL7c9AHR
Ehr*%^6{|Hcia(0V$FJWxNW|zq8-571GHtXJE-{s?4>4nNB`Za(rj* zXsjvUU8s7<4p*EFhJRdD9TQ((rwglrS<<;ioyz+Kk z+P_iaLur$suyS!{a1sygo-d%^)+f1{YF6i#7T3GPtxk2xUp8+7sTAsB%;8Ym`iv7l zjYT}aRBjnhb{>)Na!j~YfAbf&ym>~A-LuEt7mO{k)EQIRC>f;h>V0_mt_5*=w2>@# zd)2lrTO;^(#fy8og_(o5pJ`t8TE1t2eX;7H|D$!&eRy3=xY*%~cwN3LD8A$RR9HCb zdiC#J#dbO+bervmr)nHcSB9qRggJfeZpu*4{pju(q)4{b6S$?9ee7JxrZcYp*QtJ4 z;jdL542J0Yw6x8_V^L0#SN~a7peD)?X97oWXg6l=(-ckxl=1fdD9BG#L*3yLmH?Lq)$?KfN&F>Q!;8*9AExS7guTz=h-V|9JA3 zP<;F~-sYxz<}Ob)|K?;T8)LJu1LJ#Hj5YDM*6S1|bX7&I8G@0Qbh1s)kp1Nf+c5w>FtTVFdN2 zyE!G+&yz*OKk_@0PuXwR7w639^5j^4ybN>4nDJq#;wlM?VBJBIrZf;_$%@3TgdA>Y zqrxd6E_FI(<5-g)1#PXD)151cIpOu>_lGAA=UG)IY2<^QZh5*tSan!_g&S6xp64j8 zH}6giQdH?-wYOvvG;n=Bjg6Eqbxihn6k;YZ+D5rSmnBYF4f@g-x$#TLGnd0XkCw=UpGbP$1;_bHJ`yHrhsL|m6fT#Tao$q#^0qp zt$h>C(Z2ah9druQUevUvbx;l#xBAVvPWwsx%K7QOa$vX42R%lF(^;WBlT`WRYKGNJPM ztYY~L?MQ=Xfb?h~?V{fv*d-qHWPt;gCa5v>qXCt4a&5sa=bbL<=wwJ9i`F64OP zf_}gBI;1F46O}X7<=~qq^}D-7_+gxYYs1gtlZZ>_tIt`a!Ya)9sY3sbG-!tvg9hX# z!fCw~JS!`CPRTIgZM6P-+hV7i)grA<#YV&nWB7#_WQ>lmgQF% z#TnkredQdydc=kuG=Q8Q{&yNy6KHI*lmoBGKt4F7$kr7InLM~w^aF^JmeKUsTmyDL=&^kTEsPVLGPEBk!7 zKiloTW$ZlpW3SbpkL^nG+qO&RDJ)DzE2DL?g7r^cnktw|&>(F&{m-t#oq`O8>S%Q2 zzU2&+X!bnPPO~`QqSMZ#&v)UaQ$Es^eC(8m9F{Hel$YS?m~mYXr{0TAF-KkQbf&!W z$)~XKk7Ru1SNBI(HAF^4JYYzoSt#w$agTu!_Ty`jyo6*v7M7ky^Jeei=S z3(4Ef-}f1gX4+QgrpvfLvwE-jJ*1gB_4oIVd0Eo}E3OAO6IG;R2czJX7mhziu{^G$ zYA(Im``EKdTA0i9P{WHu)PYxw@7z4%c>h-ImhL^i647)ewR5?rL$8dl$6M0LT-LiQ z>-_kntQ(GwiB>WG$4TJ=l|T>UpdA7L!6$7(Y$#a+HGXY1x!15_pZ?+Xz(9uOdSAWS#NJxC|pE@J&i+NOFqkLxB zVY&3K=Wm?W_YG^B_8Hq3YlpY#8WSC#xv#ws<$7$XjcbB8zjkjqJ^0!!w6B?TLgM;r z#YBGAago1NMtoQA8K-E;X!aG@oE>e72ThlS??~jf*=Hhimf=+|(&c?#UaZA*sgyc> z<&*bu&ColdqW9iW$#&jqFqO+8VEsHQgmbVGoqDfk`S~Pb@`Z6U&z+U!w2hlL^S&0f z4$RJ5by-!>v38c}?^}A{3mZ_}zF_Nxplb*!Vkq@yK@s+f?^!%^!E^Y*`xFpHjs8LpPE*3()#x7$U;D%eY>js7$)5rF`RH#r%q|*?dH#CE! z%4~YMGqH`vb5Xi{9RCn~`gNnQebjAxr}}3xrb~Y`B@MiV-Lu%wy>ZorLsh+5YS>46 zj(@uMz!TbsTHxCAE`owA#M_eZ!DeNb_2`4+H9b2mq#cu%MI0%HF5sK4ENoDmpoAph zpR(;_)9fI+cS_!_OW!}_GF5%Yej9nKy~R0xp$6w&#mgf{|5LQAyOAcD!t+-KkgEn2)|~ zK(_N1JIn5egODu5%}~%%2xtMcF&Xjux+kc~9=^5+tmjGr)OK{wQgiO%f9Yyb=;R?YAFk7BgO(S$Xle%iJjDb^$Hw=R@9>+;53D zav{qRI5{B8u{wC`GFCCqAp6$5?Mk_`Mr307c=?I!Tr89MTaOILjyZX4+dyG`;al<} zL53R7)LGqLCeW2&;2o@wYx2^5q#lqEegLFo0Ln^0w7KrM;vm)R1zBb_rs+isb4uNo zLO=#=~IZ8t8keq6C z0hq4Sq_`B^^cGz4TF$AX5!&(j{#kk4|60V1->qwl)BWo1 zWn0BD!$$!lM`*bqCf_Obn1lVFE<@G~R0!-HDXN0JL!9=izl&^SavwUkF8}>gM1Iu| zn$j3^FS&HvjN}22J*SunWRj zytZ~BUex%gbAqAy>?uXM1SfgEmGD8KQ{IERrr5Nz(Ium;xA_ISCtG~bKIa=3YT(&L zTY>q#C6yS@6dhxjAcybFSLe~shZjz#?Dlx)w{{FSst-FEM#!(Wp~i(Z!uKp{yF0@) zn4MN;G3Jq@C1}mdDAzhoMW*7u%5@KTa8_G5KMRLoc#|VRP&8D+72A~q+)~_q? zK4zM*`K3hvr8$$Ka|2JFaO|Ax^01wwkh~pdwX?21ZN@DKFA1flgFInDi32C_kO6^@ zQL$isB?d9zYTG7}G1vwIij75ty1=2PRVyuuD#zn%+s$=zEB$;8gb~}f6LGE3w55Am zYPxfyuB)Jwk^`^ES}@)k&f=aH7Ltu^7@RhsjsKNx@4j8W7Z79~>fjzS?^u@EMwi$; z>rjdk&#jo+ijaAdzZ|4E#=`1nS=say!=RyX3Q@V#xcMc*2btoCs8m^%X0QK{P^8S~ z8)xVp65*nyDtZIy-kY{H^=rhX^OUq&y+(%uLRI%J>_EBql^o29!<4L3^~C~#!nN_< z*2?TcbngL|c4;3saWR`@StrDf`v?qupnULAnhg7mIyZwLY6ABZlxjj;rXxQO$bI=y zE>F-SYwv(L#k5Yi-rfwu1nRoO`_z6TyV!`OF=#)`qSA$OCAShSjQlw5Y_&F&H8}^H zjA5u@7P5az`CHpO{UL*03FhO*RcYTxB<# zXfKI`%k5pwj1JYCZG6WusGEnxBSLVBADN@pt3yd=WBbj;lfxy~KAU2a~vb;ubR zaA>e8FMh!mH#4S~Ei!)rZ(n!%DP@%XAq>jN4yIoTZFBe>51JxLv+c6Et3f#8#mC=k!Rb*7TTdgh zR_mx_-4DUctywOyH*}TR#u8cP5@DvKZ+!A5;{*WU(rf=+dM=R+%0-4L6Nh@63m+1l zTOPPgPKGM;{gblY+8xDNR5DVa(x|B-$OzBlxs`%LuEsuHmQU-bVDPFeOUyRSU-%Y^ zJggw1rrho|om6Q*JzQ(?F0di?<(QIY895AXZ5`Q5-JhUQ0teL?fM6C5riX5+hA8WB=bQWBAs707;=#pE3w zLYu|^J`>qq&`dK(I_e3()bgh!G`e)T($kts!&XxUbWn*4zBOZ|{mIX*t0!`C<4Zmq z=SSP6-Qse2!d%WVGBraZtSJ?OQYxU2yzjb%g z;*2`(UvTLV_0iQ~>UO`atrIyZGcC8<(>UB%7b(YqWn7C2V!uaU?BFoLP}r?1?iMyU z>$03y-FUr5I;me(qW9i%^NGIZc1;f4YV=*ZYUeBAp}w+Pk_ZJ^g;LB1-l?7SL5Uhz zy^c(-(W3ww4|~ZkN*FsHgyoQ$^4BBRK18}*Ow}iblng51EN^R962AFERH%z+I`kX4 z%}V-SXT{*A4i6i;HlMQa8BB`{7AO~AdfVD^czklWDJ!SqD#yq97u}ZkWIbCQJnl)h zZhc5_5}xk}tz0Ts1R=R}a~iSBzS@p^v)O*bD(>f_I2jKVQ+m?!s^*yQYI}s1Q_ZqA za$P{Y^MS%z-b-(GrrxsrHlQe?DB0|)`>?KEpL$lOs=K*~eigH35rcb@olxlq)1h<2 z4aXv1mv;Dk^dZvxS3uv%=OT{nP$tZ$V`XRXMtiBU_RG7e5MOh#3Av@PA;nt3`AAn? z&o_6M-_XFoFEq7e!8zlfwwd=zhQVD1dnXM#JVv}^LYyDbhrW5OzfsVd7jA6q>lHbP zWikrmW!!u4&{?)}JqwBmfg`qT9UB|sj802eD-F&TNqq;gbJ1h$f|dmvw*AR?QYk8A z7+seB?%`Q1cG@^B55DS?mbe-Uot#U2JpNV%d2V3!z=Bfv)}tQ1yi0fL&PH#p3Ksdc ziQC3+_IX6`>->~!w%Yw!RZ!j1(i-dbPT->x)3oq|p=!gC^K~Cm@EmuYqll%xRO9~? zIuZHi2r0XORL`CYc^h{SnW4 z`?0D5h2OTIdk1LZ%n!r7B3}WwZ&xeoPQQ~J5PNChH!9}V zC{WmTQW-uyFHxkacpordDrr_w=Esa78>7g3;F_+rWI^%1Im3fuhg;+oCH5s0_w+qf zLiF;hMH&lzJB?RJDKuW0ra#75@U@vI%D(HLh3$?5$1Bcgyzov}KG|=|DJ>?+)QHWw zYb?KeT|cg}X1Ot8^1wvNm~*rk%_C_k)330WXVgKc(%0T|nd2a|F=1c2CHwyTisVlJ z&?Fg;aNGR6{Lf-ZTOF`K&GN>ETS&NUp8;%A69>HbGbUqSX+{tXq-M*UlsBYbtE9;o zy82a?saIwYO%I>IoeHp2W7fI48bo%y+rZ6^CAO7@Wl?<%Ol+zF)OhL zHkPcvhiSp@L`;_d!59vqNvnxprcf z=a2cZ(Tu@h+rJJ6A%6sM+!-og?aR7dtpQtsLp8ET-Oll}D7Pyb+suEM7Y=uUyE{MJ zjvUzt8t@8Qb`p;}#1A6z{g3Y3%?eXHdF9*N5$3(&@s^2aIutjQp>@R*XLlScmkT!Q z(0+yrf92_=kM(1V@9gm2#@M|d!)3#P1&8@Z(1Y}?mh0F}W*V`KDWaZJP%b8oi8&T! zUN~g8hL62s!Yi)bYQvp;ho=h?znwuH?U-dfl->}0P^q0sS{|vX;w$$d-f}MFvc6q) z$mOoZ>*^t2Yd+7ceD)nx@5MO!o!A!EmD^RDtyuchL5EKlf&0)i0BeQ@&cA*zFuPMo z@<99{$Bq<58eJGV#}ghY3$uUtCHvqP#%G?op%m)eMXK| z?|@&36liz7BS5&{zxL+QBJi?jD?7dpv4G1jU{EK@I^O4<-kA2Sx&GQmqr)(~OR1nd zdk%p8{RjX%;+-}bZ26-Ca<<6>;F0M`1HLY?C~bFvBRv@;lzdKA%*MI-C7T?3NqN8I329c@G2Oh(2cd9p7re; z<_(R$YU4=pl_0c$5BLbsVtVQ%A!Xz0f(N{*6l1Vne9?IAaP}k}Llcc#HeaaA(U&Epwq)0WxLPzZL%aD>0?IV{yLKNz-Z$JR@ zEdc|7BlyI+Hki6hl!EdA{c{lx+LTao@&IFMda8FTTTn#f;ai8!JT}%yg=JL~tMkDC zx|2;-C9egH%2ov8th{^Vt_^XAGmu1n9~Ys#mK^DR|+|i$+-|x_E09 z{|R+YR_Vj`0KGXwu9RM1%T*>0>~VB-XTd{gHBG`US#NFWnZG=Rs%_|mkd@&ZWxTbW zWFAyS@jy7EZ_WM0?#zdX)xmZvDZIMs=|UFpO46kY)V2N6YqK*%{b$ikg{|HGI(ES( zQS07M) zR6%evv1d`=?a?t(>m|#ctgS!jnyy1PN}s7+5tUnCBGb%lLqR?9gPCH3EQ7Ucsnsu} z9MLggId0Ayfuopxp+J3)p`o|@+3M*w{Ow=QDaBILTHmLDg85RZfvzW*(EdZj?8v1& zh!3yL$c}?~$ixp$w04>KuGra>9NV{2(-+sZq5NT0%w#1o1nbjE?*VQ|YBAMyu#OK1 zqFBrfeBUfp za*9wrt{tggS^`lxhBbA^+Ov6V4K#T@<9()&i>Jk}I0N+{IN;E8@d-;rQZiZTpF#K{ zvN*%~6}%^%1sIU=%xyfT>hgE~)+Wa890qTI z#;3YdNC06R3RtJsx_>Vkd0LB)THGtq>80hk6hp9=fsIiB+r;*bD-q?_p{o&vO=Oq0 z!!WaRk$Pe*yoW6KONkJ^$gGeFcPDnqkxfi`UGH_M#;tK*#`Qt3Q2buej?It+VBoR8 z0s;GgD|n;lx!F#JDDbLDszb@fN*}Z)7LH%JC-tqz8pV$->ccO?+`K?CK~2C5-h-pGy9 zg*T81$mZwG69T+f&P@CNvzrXO$E_pBDO<}(n ziDiGt^;Vv+6PL+fYu_JSIq+31z+g-}>A76T&xb~_)UB!C9%UMS#XN^p%@}2sq}~wefl$CzX2h~q^{v9KY%y?S1DO!!&19&M~%bE zS&X4+@g1_$C$YKpxKwm}d{5hpZk~s4#Zk}NUr={ti#MfRc&(CfX)cjX9kJH-;>ri8 zx7A~*MVI5was@xV_M@49?VY0_9H!-#P}62yfe_-mH5FdpbRMo0@Q9wP3K%iE#iN5u z0pP^{)lTL7QZ>x-agm;wsa_5$dp=B5r7>~Y>Q<PsQ0X(omlu1t%gin0<98Z*Zb9`F$3%L$Jx~-15=xge4 zw#`p%_|yv@o|TCHs4MpQb(*)Kd|qKnlFGWGd9&uY49HLtPrEdxq_JoWJ#7B@=tAtz zM|?lJ#Z`)vB?1EJyL3sJN+#Ze*-9NGU`cgI1ENB;b+Jp~Oh6)amnxT4^LuNb^y|)i zDp`w`4UX{Wnq`?Ll>Z)(d$dUE=uYg^Upl+Z#Vu5pHHlWBMSP)Zy z>UkX%K8q~g=%VP0z4c4=ebE({I|0G-b2~1hX)f+$cSLn$O((S@FIpsbmExN`Q7~kq zJWf$f)P{C05F3xbJAa6H-sJqjA|i9dQ377lf$SdV>%AltsPB)PZO#*n9Ak{cj+ z2ZzfIWEqX6hku1|biGYHtFh5~(I`dgMtNxe1+cv#~Alp)-Fc4R)U zTDy;t2rtCt=skF!4t!nGD(3B3h{^kdBPoz-U~v7oBeR@%PQuC{H8z*Vle!3O;r9dV z&Qd7i60Al3At*PD@wv#7c>FOum!x!yj3XXhJ>^t4hQ=K6#9i52= zRHl^O+gF$jCW;+$=71OHFA&lXqwa-L9XYOA+q-{1iT>4N!JpK@74xnpuAwAxihQHg zQj@qZP}P;ugt@uVutUA3WAd!|TqKRJ$?ILCr3ydlGLoCJehBnci9;Sn)TX)%BEcKp z-*e~3%hSPh%jVVTmH7Bs7pTjkraKE9E?^r#_ zx4d~xvVqgjFZ& zD?P`5=$lQ&pOaUZ4>x)zH}S6DC}+g>liA(^BWtWX6SIV|O+P6?<+_6gmV;5aTGypm zN`!~|%&*94lS;rfQY3zm+aju)r~H66R^_onlhlk=bubJ}CX&G;5QVtoicK8EAC z$l;+K(W{awkEt};y4<$ITrgZ*0Odz2Jfw*UB>K#NKw999gH6BP0_vp&=>dPf1A7XU zU8*XjQbfzS#JRae?OorlD%1#(b*wq-w;Ro1p3v#{!TY0ml9P|$WNP(9pdB(69=Kb{ zFC5(IP;G<6&M7%L2qH!_p;M6&AvYA)qaK(oN8T7KO?O{;kUT%qKQ?6Ko!M~P=a|s1 z9|M+IY7=v<9g+8Sbl(gWM_?X}mbzqz`ab+jzi~-RgP~QZ+H-LCm+xvllF9JBy>M|o zQg$njwYuT8+e>}-W~sdq&W0V2T?NOT38#rQtr||wt**nXJv=H-{ccMh#cvW@9wK1w z;jKan=ln3C;%23yDUl0%%SbleMzQ&vs_}vbMcgNR@h@${PkYO4s99Ng!&tNJQg@iB zWqN1qmdBX0MWWk~sHi2fvtg}UEl%x5#AGMj+iY;mq`LTawOpK5{7;wVuQwPkPJaw7 z?t75d(|kc-<$+NOhHqVY=xbSL2P9@ z1}<3aG7@FXhLUsL6I!*|;O(eFhKZ|kBK`IfCUMB{enWcvR+p;m;^CEhk!k7ES$$|> z`O5>dCVmJhTsFfv4kdl*!z;6A5#rG-Xy1rc-9Z~%j_jO{WC_Z#b;V#(+)&<<6oN0k zfWR>Kzm(N9UUMRP`T(%Bu|fIa7RzvBhcEg}>^&Sm&)}`4A%EY%b@r5KdyiJ!&rXlX zt)b1Mu@?IJ`rG5v>5WFlBcjQ^EAdhraT8{Jx>gb9H69OcwP}BhQ7kTs4XHaZ+#fz@ znZV28y}8^}Khz;>ua%3g8gQFlH0#-3ZS3yc&SA?lORwbG|Ysqf48p z-NUN10QJxx6;9I+_Hd}ZMuag;Vk~PL-CjB-H0)&$3P#~;ugp6JWxOANr&2`Py)wn8 z3OlR0mMZt&Jb+hAOw6Hn#XclB4n>qyIQ2LiPWw$`92^{=OP%Tw2iAw>SD0uzw-?d* z!$K*MDal?2KjjKI6YV0hwU?7~Hd7w8(dDC!D(j!n$g^Piy!ht97GRO)IVSuF>$*rJ zD0H0E(vYz5O8$_|vk|d9bp<+i3)OycS-WATeA?*z@SfKW#&e0Y6A4G*oPJh2hgrRb z20G_fWN@UIZY@(!yEc1EC*9bvdc%matxW3g^;vUA?=p9aDlfVhhuxU=(x^ssuB->bUc=H%qu%aQ2NEH5bg&Xlg>M_q8;U}ZRKd>NS% zmUJQhUS18HW_ExF^QS5j8YzO< z=w!nNCH=hDv9Y4;r{cR>W!_yx9C-km5GGiI;ED=`lj>1gAM7jZjG(;|{YVha^oH86 zug1~H31lHL@Uk?|$&(zD5KhL?lz;SKxJRKI_ZK6f#ZhAmTQZEj?y`BSCnk#(^S$Dy z<uLk9r$siVsH4-v`F?K4PA9DIyp(n zZf)&BK2}c#J+7`xl$7`+H^B7dE2lA||n`Pca!tDVUD*mk5`ywUo`gXq`y%7!zDvD`L*X zt@p%rAZ^1XP)!bYcAM^;NTa_L7#mh? zerui7eeFJE^D*d9)zYp&&z$UY)(vEtH)3BtaL+HZ>`thuWph7Iy2My>?mi2~OqmyXza%Z(&c|J_eb+JE2HdI^u414@JLV zH>_jqb4^*FVHgNPJzKGNqPTIZJ0*n^-vRTp>YK-y_x;323#Bc>2aVu6;WG;Ay(KQA zDf3f%zvOtSgDTqHc~6uN>ROc3?NX1ehWmVxE_$&+oL;_H?X*eRazFrB(9pDX60&Cz zHp->1F_d{V>nUY(ujlu4E{2R3c9wG~Ai5PS6ux7W^60=m@*&0f?817`_QKGuA-RZa ziPhOrSgOG7W;052+VjPf0(*c>QM()Z+2Ez0=qMW5aKEi}M^1 z*s0A6k}@narsnULhOg;Vuzi&1Q+3QK8%l2xKEMIr4rwa=>@pOEPtjf)_JEkQD$dB%&pH`kb%H zK)1%8#D~|mEt@M>g5snfxp0iw4uVFT3DCSIz}UHsLFW&=4UL(lYzd$aJr!pxlKtC4q2o4AvcKegSE zhgkMbG{jm!)X~4b^7fr?o`|zVWf$)eR=E45J}m+we_9j?Zi z8`zY5P<$fFIEvzN1Rjm1L&8dKcRJUJV}sGExDM;?brb7nbjmT6oEGBjuz?=5jG4wEpFV9L!zSUd4^mk?B zALqwY_E=E;*)q>dR<}Z8qbkt#ThYjK^(@ul&69D73=mdoe?8U;PSufLXWD1Q4oeiHk`ETPC_VZ(wuU8Y$QF1&C_(q2I$E3d-L=EDnw zzREF8>s*_mbf<$U>@n-@s_#FY@R_wmQTVRLcWK8CDP2jiE?-2A_Wvi2^p zzk-$Mwr(j~C9-!Sewq1VzQfGIZ-0|YfBL{9sdwW2M8vbiV_2PuMmX0fjfUL2Dh#d; z4HwtewJCq#SDrTh^LByfnAgv$GDW*eDlhdp zSOIU5!^&P+-1xdXLZEcv#pDUJubmvyv2)ZT7{88=zTJbFt}u_l-NimHijKg!`#SHq zU8FAl3hlE&Hl7U?82E~=u^$0Fmr=LIgyA;*O|uuo0=(8EHNvn{xH~qMpASw&s&Y+l z4`F?CYvzvTZ`txI@nxbb(Vth2J%eow67qXZb;Ze; zaTr&7k;ZWqpU9AHkup#R*J)gNg=S|2omA7C*rzmSvL!lf_tpm-f(Fb>7r7Dcs|WaL zKr5Blw42|rhs7Q5`-*+x@jaMcm-rERd;;|2AI9M7nlf|gk^W^49W1YspP@xF`a~Mt zF+yGgc2=3_JP|-DI5qO_qtb)#gVVcry+3vTJsg9{%Bx}2cPzB~c{nXxexs;tBf@SX zF(vtAq~dbifNiC4BQO-(%#Aj2cxZbm3xo9N=dygO2K?%gJ_vcAzS(MLjVVn%$04>w zUSP$cyP3}}G|tGI*Keaz(fxmjWxe0P=bA6BDX+e6W9&}j(!BAYq)^RTi`4pNx9re- z&DWIqm7K|Tg-VxOEPI=4<`X>(AdG+%obAysx-=|Y!&ALQ{Y0pFq^zJ&<@nIRyBFVU zR$ub-caDTis&#}*4xiaw-aZ!9*>B*|uB#`rikxnc<9sbEB!ymk zJq*ix!XM!jtKY|BrQLyjwlRjb%irLTZ79|W3*Y{$Vq2k5cM{hii>^wz?=1*qHd`D; zeM(&lo9($GtbweFeecOvkCPcgB#T%rgGQ>;y~eN9theC&(7e&hO>F1IK9@E zZDR{+CQD?^Gbj7Qd;7+u`cRJ&9=J9ak7uAcj63c#6&JaE<<*H%Fo4&!_qMjkpsbVa zYIrBiRdVUDmNwD@Hg{Eqv6fbi9_{GeuGyAbPJd6re4e?ziOiul-dcIXp43trP}r$s z*E8Gcgd0V&<=yWeI$tvC*SR^uv>v=oTP0eFMn!GTTYu_S670wHpxO2kojgBB z=CB>}bxj-aofnO2{vtplw`pB|4O5ZeRQ+y2hpCrYJU$6&gq=p1;gS$1RRk*4eZ9Md z>mQ$rep2K7aAdExE`OvYC0ousAx3hfa48>|!Wb%8=q^yxCNmjF?X|FdOeo}TX!gnZ zY{U&OBloez*5%NArr7DZB_GbFi<8$lyNlDUegv?jV87idy&<@<6ba37A579~JUg$J zy**W6XBO3<6S^K=DrnrAl~W8I0XdT4$9{S%KgH&}!3EsgShPH=uXn-&)86%#jp%mS zMq$>)UAK6Qi@gk-rU@UbUO2c3A=rGNr&kpfm+v8eB<{%}{+=>?RGz_sZuL~$+YX&% zvS2dwzM<=J$^Hp>_wfj2^ogsiubIpI8A;m}EL8ZW=;dGBk;wZx#!S6lsy-D0(NFNL zmm_W)u*sbI$y6i4O4XXS#(OVdUr59MISV4_R23i9l%+4)VPRXY(rE&a|K zh}34Eeio}fwN6=dh4Ai5k9hfTo@VPtoS%e2`FWg9>qePIaUQF9hX`!@rxhaGV3LM{ z@&sRMB~bVbAF#7p!FiUQTyLYzxbgjizGLJ;rbI=d&4$xZ(0>@?72T;YzqwgoVb@Sv zXKD`Y*7;NYWa-LuB8+JneZC-N{w6%NH)qg$C&ffH38{^d3 z7%sVYVMTDs@)L6#vt^=bAim{xB2aORJ}EuybeoSgaXYyv+rL&8Bp$No?ZKLcIQOLr zh&uanv6HW)w(+!h{@SyYV0Mo|;oP#hjc^X_YwHMvxhQDl92eJLU>2_WrFpGUJ|>9+ zto~c%mMWK^CUP5*(?KiRYP}^UBn_#dR_!aDvn94!^f6f5+1XX6&MTV96VbcI*VQ*= zwfFpo?JK;N%^88pz*0~~ugrNPz;;hc?T}rzri?^;QTNN@gM?iWuC7GN-V{u4%1-;~ zw2UJ?q5giLB9({VLzWTSB9x`wxIaxh4l9HGF;(f_7|{sD*{NpNQs5BX~^$pcy8vRklY@|=_16k z3Lpd$Eq{Pq8EJ{!wcO_BBMf{8Uo%isy-4oMnY#}`5P>A}F5(Ghcw&{wOZkL9Rf2HK zeg%qD3F`nNowtX$lKTSjn*~#}v?TB#sdS*GzzXeIi^tQbqzPUjvoC%82S_(??HSA>5b26eD(%5l<4n&l=qA8<%z`f_SCc`rbRKqUl5J#&JH1X*Dd0APjTGbZ)# z{vV11H~=6%5a@pS)*r%sY({o}lz?4)EYs<513WPMQF!cIHOVGk8=G9`juGLw>nHiU z+J^_VkB;^NVU!Fwa0pwR+$5C-tyYk|f3d~`gr+9;{0lP+Ma~~(R00d_!dzD=sdmqC znQki{Bga?#u$SWet@lUSIuFXY$|AjZ6jC{;)&lUt+dm5kKN_%go0;au)1m()e@+EL z*jr9@#MhGCtzE-Nw|03_daIH=4qoo&pf)DCzBwx&@PqyldEbdV4MF-`JYn)wf6i*{ zO#$A*MU;dsXsF0-za>Z@>rM)#H@QKCbpFs?pcb^EhYK$_kd>QEKM{f>J$O-8hX?QO ztKFq1{>nYZ2i~y$ zhT*jq$n|6tF+*(fFxetAB@4Pkacy1KGcY_@Nd@#YUu99_(mr zydP&K2{;vca4qpqDtGW@Ny(&R)W=J~tDVVQk}xAc$;}VgDA-*hwUFXZL9&hnlX>m> z3#j7>k((8S5;#UM)#Kl^r0N2LIj8WVV_yz`4i2uJ!81u+$46E{h&Xld@79;UKr9e8UIV$ ztsen{JH%Q0W45O9{~_}FO=h2?zo))b>A(5=TMw`%1MV1Gz)OZQ1?<0T^(Inyi&uBp z_>WV24B+*1cwx+`IL@;iu( zz@EaMg78Q*(bsn&s()O3DF$y(C*nHj!S7Va|CgqMhJ_s9l}5bHh0OkiK3V0~@IHw< z^a)|e)Vl}Q68=TI4EEgXe9|iar<#HaJm~vaK0@O^Oz!(QQWWTa{x@8pZ|*6S zJM0B$p1adYxux{mTM=dg7TBb zzk?VNYimCWI(lYpz@+J6Zt@HLA4e|x`@mHp2EL_0EUmw zS04V?1gZwLr->A&I^uH{%k!?pCp9jm_y_+sRsdsaJ~+>6S2dSbx^?B>v>(AI6rh>& z3ZQ|J&GpQ0O{(zUBm)3+Q2^*_VJN=)*IJdLdcP%uY#GKTNAMPZ)j(~30HXM6j${zK zbCmQz{Cckp-l|4Cp-oFm?#(1n#BvpYcqZ`Hxp^H(=f9}O>m3LlAYT9$pOnu?Od5zY zj~-mupU@SYU;1K7(1X`EAZ$bp-iH%{I>;huI^G(ml9Wg{T+v5JtLKvm%8!#3@`R&KVR4s>xOZ)fefTtNX zASKB&)APmE1!DO@D*LRTs zKcK7vS4aO7;H@*?>G8x^-vA?Mv$=_q*vk>*^8S-Z)$0JHvm$>pSYM}r{kNuqp1eR! zaK(AI;(vAW4IS(u=YC;jz}|vs0{+G`1_6yk&-d{rd;swTJP6*{T>OWW|GZQXAKp+xgKf`sDd=W`U@TsB{6%fNU|Bs|hi)6Ck`Exsx^+e(O zpf7lB5HZR*&t>_qQ8MfRL=_y#jkAf#WMwALPs|p9wtgwYA|HSTMUrw*c{4mp-sckn zj7jJ?z8B0|n*#dZ@khiHC=yL_+ROdh2_I^|AGIbb=W(FS%OX!I+4oidI4L8 zFmJHDIr^JJ0*R2iV@C*p1}fu?DRFc^^-8s(Q2s-yGq?eWGAzdp&vY43zWrbEy`2w` zXwQ9-Xh$bBi*HUSVKx0z=^^3*QGrSK;mcnq)yU-MiAefy_xnv$hV zn54Grh&AznOh{ zo@EWAL}agv5QEG9)ovHM(w{Ex?HKEyj~08>vHFA5-w8G%{H=bsdnN}=!4HMyi>`y_ zPZsp)V3xfsX;V4u!{~qh=m{m8Jx_;%J z`95R#4|g8QM)apnh-f%U$|y@Zk`i cjRSwV4E@+?O?kBrj{J4&hWho~t0s^C5A@y9A^-pY literal 0 HcmV?d00001 From 252f43fe138f7618ca5e9452c473281d47bbfbd6 Mon Sep 17 00:00:00 2001 From: manishdex25 Date: Fri, 29 May 2026 13:58:38 +0530 Subject: [PATCH 2/5] Update docs/how-tos/chain-events/opentelemetry.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- docs/how-tos/chain-events/opentelemetry.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/how-tos/chain-events/opentelemetry.md b/docs/how-tos/chain-events/opentelemetry.md index 079e768..64640b1 100644 --- a/docs/how-tos/chain-events/opentelemetry.md +++ b/docs/how-tos/chain-events/opentelemetry.md @@ -22,13 +22,6 @@ OTEL_ENABLED=true OTEL_SERVICE_NAME=trustvc-chain-events OTEL_EXPORTER_OTLP_ENDPOINT=https://your-otlp-endpoint OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION=explicit_bucket_histogram -``` - -| Variable | Default | Description | -|---|---|---| -| `OTEL_ENABLED` | `false` | Set `true` to enable telemetry export | -| `OTEL_SERVICE_NAME` | `trustvc-webhook-events` | Service name shown in your observability backend | -| `OTEL_EXPORTER_OTLP_ENDPOINT` | `http://localhost:4318` | OTLP HTTP endpoint of your collector | | `OTEL_EXPORTER_OTLP_HEADERS` | — | Auth headers required by your backend (see examples below) | | `OTEL_INSTANCE_ID` | `-` | Custom instance identifier shown in metrics labels | | `OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION` | — | Set to `explicit_bucket_histogram` for Prometheus-compatible histograms | From c364aae5df2c41830b2698306a4ccfd8370c56cb Mon Sep 17 00:00:00 2001 From: manishdex25 Date: Fri, 29 May 2026 14:08:44 +0530 Subject: [PATCH 3/5] fix: update code block formatting in chain events documentation Changed code block syntax from plain to text for better clarity in the quick start and webhook payload documentation sections. --- docs/how-tos/chain-events/quick-start.md | 2 +- docs/how-tos/chain-events/webhook-payload.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/how-tos/chain-events/quick-start.md b/docs/how-tos/chain-events/quick-start.md index 3a44b3c..39da51c 100644 --- a/docs/how-tos/chain-events/quick-start.md +++ b/docs/how-tos/chain-events/quick-start.md @@ -148,7 +148,7 @@ docker logs trustvc-events You should see output similar to: -``` +```text INFO [startup]: trustvc-webhook-events starting version: "0.1.0" INFO [startup]: Database connected INFO [startup]: Chain worker ready chain: "ethereum-sepolia" escrows: 22 diff --git a/docs/how-tos/chain-events/webhook-payload.md b/docs/how-tos/chain-events/webhook-payload.md index da537f5..081dc39 100644 --- a/docs/how-tos/chain-events/webhook-payload.md +++ b/docs/how-tos/chain-events/webhook-payload.md @@ -13,7 +13,7 @@ Every event is delivered as an HTTP `POST` to your configured `webhook.url`. ## Request Format -``` +```text POST /your-endpoint Content-Type: application/json X-TrustVC-Signature: ed25519= @@ -104,7 +104,7 @@ Use `data.transactionHash + data.logIndex` as your idempotency key — this comb Every request includes an `X-TrustVC-Signature` header: -``` +```text X-TrustVC-Signature: ed25519= ``` From a7041a30da257e22eae60eb78f71eba143f53300 Mon Sep 17 00:00:00 2001 From: manishdex25 Date: Fri, 29 May 2026 14:09:26 +0530 Subject: [PATCH 4/5] Update docs/how-tos/chain-events/rate-limits.md Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- docs/how-tos/chain-events/rate-limits.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/how-tos/chain-events/rate-limits.md b/docs/how-tos/chain-events/rate-limits.md index d572dd9..285c74d 100644 --- a/docs/how-tos/chain-events/rate-limits.md +++ b/docs/how-tos/chain-events/rate-limits.md @@ -101,10 +101,6 @@ Check logs for these patterns: Enable debug logging to see each batch request: -```bash -# In config.json -{ "logLevel": "debug" } -``` --- From 22757ceaa30657bccf1174f809db6c189289c28b Mon Sep 17 00:00:00 2001 From: manishdex25 Date: Fri, 29 May 2026 14:13:14 +0530 Subject: [PATCH 5/5] fix: update code block formatting in rate limits documentation Changed code block syntax from plain to text for improved clarity in the rate limits section. --- docs/how-tos/chain-events/rate-limits.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/how-tos/chain-events/rate-limits.md b/docs/how-tos/chain-events/rate-limits.md index 285c74d..93d39c0 100644 --- a/docs/how-tos/chain-events/rate-limits.md +++ b/docs/how-tos/chain-events/rate-limits.md @@ -28,7 +28,7 @@ The following config fields let you tune replay speed to stay within your RPC's On startup the container scans from `replayFromBlock` to the current block in chunks of `replayBatchSize`: -``` +```text Block 6,000,000 ──► [batch 1: 6,000,000 – 6,002,000] ──wait replayDelayMs──► [batch 2: 6,002,000 – 6,004,000] ──wait replayDelayMs──► ...