lahza / Docs
Documentation · v1.0

Lahza, specified.

The engineering description of the observatory: what is observed, how it is stored, how it is scored, how it is delivered. The site describes the promise; this document describes the machine that keeps it.

Overview

Lahza observes the reliability of Avalanche L1s from inside their networks. Three observatories — Frankfurt, Virginia, Singapore — each run full-capability nodes that join every covered chain as peers. They send nothing. They record, with their own clocks, when every user transaction is first seen, when it is included, and when it becomes final; they follow every cross-chain message leg by leg; they watch validator sets and block production. From that record the system derives a public, deterministic score per chain, an ecosystem index and an incident stream.

  • Listen — nodes as peers: transactions as they arrive, blocks, validator sets, ICM traffic.
  • Measure — three timestamps per transaction from our own clock; percentiles with confidence intervals; three-leg message timing.
  • Tell — the public sees the dashboard, the public stream and the X feed; partners receive the evidence layer.

Architecture

ComponentRoleNotes
observerFull-capability AvalancheGo node per observatory, tracking covered L1s as a peer; stamps t₀/t₁/t₂ for every transaction, records blocks, validator changes and ICM messages; ships signed observations to the collectorNo wallet, no keys for any chain. Partial primary sync, state sync, pruning: full capability, no history.
collectorIngests observations from the three observatories, validates observer signatures, writes the raw storeIdempotent by observation id
scorerComputes percentiles and intervals, components, score, rank bands, index, incidentsPure function of the raw store and the methodology version
deliveryREST API, public and evidence WebSocket streams, webhook dispatcher, X publisher, hourly on-chain commitmentAll read from one event log
dashboardPublic console; a subscriber of the public stream like anyone elseStatic files; no private view

Two invariants: every published number is a deterministic function of the raw store and a methodology version — no component may write a score by hand; and no component ever sends a transaction to, or runs on, an observed chain.

Observatories & coverage

ObservatoryCodeRole
FrankfurtfraEurope; transit corridor for western CIS traffic
VirginiaiadNorth America
SingaporesinAsia-Pacific

Roadmap: São Paulo, Johannesburg, Dubai, Tokyo. Coverage: every mainnet L1 is in scope; a chain is observed (our nodes are its peers), listed (registered on the P-Chain, not yet covered), private (does not admit outside nodes; observed the day it grants peering) or dormant (no user transactions in 30 days). Coverage grows in order of activity and is stated on the dashboard. There is one class of measurement; nothing is measured second-hand.

Observation protocol

  1. t₀ — a user transaction is first seen by the observer node in the chain's network; wall-clock and monotonic time recorded.
  2. t₁ — the transaction is observed included in a block.
  3. t₂ — the block is observed irreversibly accepted by the validator set.
  4. Emit an observation: {{id, chain, observatory, tx, t0, t1, t2, observer_sig, methodology}}. A transaction seen but never included within the window emits observation.dropped.

Finality is t₂ − t₀. Per chain, per observatory and per window the scorer reports p50, p95 and p99 with bootstrap 95% intervals. There is no cadence and no sampling: every transaction is an observation.

Data model

observation   { id, chain, observatory, tx, t0, t1, t2, observer_sig, methodology }            visibility: evidence
icm_trace     { id, route:{from,to}, message_id, leg1_ms, leg2_ms, leg3_ms, delivered, evidence_tx }  visibility: evidence
validator_obs { chain, observed_at, count, hhi, churn_24h }
block_obs     { chain, observatory, height, seen_at, tx_count }
incident      { id, chain, type, leg?, started, ended, duration_ms, evidence_tx, public_post }        visibility: public
score         { chain, computed_at, F, U, M, V, score, ci_low, ci_high, rank_band, coverage, methodology } visibility: public
index         { period, median_finality_ms, chains, methodology }                                  visibility: public
commitment    { period, merkle_root, methodology, tx }                                              visibility: public

Raw observations are immutable. Aggregates are derived and can be rebuilt from raw at any time.

Score specification

score = 100 × ( 0.35·F + 0.30·U + 0.20·M + 0.15·V )
ComponentDefinitionNormalisation
F · finalityp99 of t₂ − t₀ over 24 h, cross-observatory median1 at ≤ 1 s, 0 at ≥ 10 s, log-scaled between
U · uptimeshare of 90-day windows with blocks on schedule and arriving transactions included1 at 100 %, 0 at 95 %, linear
M · message deliveryshare of observed cross-chain messages delivered within 60 s, 24 h; not applicable where no relayer serves a route (weights renormalised)1 at 100 %, 0 at 80 %, linear
V · validator healthset size, stake concentration (HHI), 24 h churncomposite 0–1 against public thresholds; set size published beside the score

Scores carry a 95% interval; chains with overlapping intervals share a rank band. Weights and thresholds are part of the methodology version; a change is a new version, announced before it takes effect. The dashboard shows a chain as healthy at score ≥ 93, degraded ≥ 87, incident below.

Three-leg ICM

Every cross-chain message observed on a covered chain is timed in three legs: leg 1 source finality, leg 2 relayer pickup, leg 3 destination delivery. A message undelivered after 90 s opens icm.stalled attributed to the leg that broke. Lahza never operates a relayer.

Incident model

TypeOpens whenCloses when
finality.degradedp99 above 5× the 30-day baseline, sustained across a window5 clean minutes
uptime.incidentblock production stalls, or arriving transactions are not included for 60 s as seen from ≥ 2 observatories5 clean minutes
icm.stalledobserved message undelivered after 90 sdelivery, or 24 h expiry
validators.changedset size or HHI shifts beyond thresholdinformational; closes immediately
region.gapone observatory's p50 drifts > 25 % from the others5 clean minutes

Every incident carries an evidence transaction — a real user transaction on the chain — and, on open and on recovery, a public post on X.

Proof of observation

Every observation names a real transaction; that proves what happened. Every hour, the Merkle root of that hour's raw observations is written to the chain in one transaction, together with the methodology version in force; that proves the record was never altered. Each monthly index value is committed the same way. This is the only thing Lahza ever writes on-chain.

REST API

Base https://api.lahza.xyz/v1. JSON, UTC, milliseconds. Public, no key: /chains, /chains/{{id}}, /chains/{{id}}/incidents, /badge/{{id}}.svg, /index, /methodology. Evidence, partnership key (Authorization: Bearer): /chains/{{id}}/finality, /icm/routes. See the API page for samples.

WebSocket

Two streams from one event log. wss://…/v1/public — no key: score.updated, incident.opened, incident.recovered, index.updated; the dashboard is a subscriber. wss://…/v1/stream?chain={{id}} — partnership key, one subscription per chain: every observation and trace for that chain. Heartbeat every 15 s; reconnect with ?since=<event id> to replay a gap.

Webhook

JSON POST per event. Headers X-Lahza-Event, X-Lahza-Signature: sha256=HMAC(body, secret). Non-2xx responses retried 5 times with exponential backoff (1 m → 30 m). Events are idempotent by id; stream and webhook share ids.

Public layer

Two layers. Public, for everyone, no key: the dashboard, the public stream and API (scores, incidents with evidence transactions, the index, badges) and the incident feed on X. Evidence, for an ecosystem through its foundation's partnership: raw observations, three-leg timings, region and incident detail, history, webhooks, signed scorecards — provisioned to L1 teams by the foundation. Raw historical data is licensed to institutions. The score is never sold.

Operations

  • Observer node. AvalancheGo with partial primary-network sync (P-Chain only), state sync for tracked L1s, pruning. Full capability, no history: an observer needs the present, not the past. Per observatory today: 2 vCPU · 8 GB · 150 GB NVMe covers the most active 25 L1s; disk and memory grow with coverage, the machine class does not change.
  • Clocks. chrony against the provider's time service; offset under 1 ms; cross-observatory consistency checked continuously.
  • Collector, scorer, delivery. Run on the Frankfurt observatory host today; separate host when load requires.
  • Nothing on-chain but the commitment. One funded wallet exists in the whole system: on the C-Chain, for the hourly Merkle commitment. No wallet exists on any observed chain.

Retention

Raw observations: 90 days. Hourly aggregates: indefinitely. Incidents and evidence transactions: indefinitely. Methodology versions and the raw store are sufficient to rebuild every published number.

Deployment

The site is static: HTML, CSS and JavaScript with Three.js from a CDN; no build step. Upload the folder to Cloudflare Pages; _headers sets security headers. The dashboard reads demo data until it is pointed at the public stream; switch by setting DEMO=false in assets/dashboard.js and filling window.ES_CH from the subscriber with the same objects.

Build order

  1. One observatory. Frankfurt node, partial sync, five active L1s; first signed observations in the raw store. Proves t₀/t₁/t₂ from inside the network.
  2. Three observatories, 25 L1s. Virginia and Singapore online; coverage by activity. Proves cross-observatory consistency.
  3. Scorer, v1.0 frozen. Percentiles with intervals, rank bands, components, score, incidents, index. From here on scores are published. This is the step that changes the foundation conversation: a number anyone can recompute.
  4. Delivery. REST, public and evidence streams, webhooks, X publisher, hourly commitment on C-Chain.
  5. Dashboard on the stream. DEMO=false.
  6. Coverage growth. Disk and memory per observatory; more L1s, in order of activity, until every active mainnet L1 is observed.

Glossary

TermMeaning
observationone user transaction with its t₀/t₁/t₂ timestamps from one observatory
observatorya region running our observer node
finalityt₂ − t₀; what a user waits
incidenta threshold crossing with a start, an end and evidence
evidence txthe real user transaction behind an observation or incident
legone of the three timed stages of a cross-chain message
region gap(max − min) / median of p50 across observatories
indexmonthly median measured finality across every observed L1
commitmentthe hourly Merkle root of raw observations, written on-chain