14 KiB
Explorer Freshness And Diagnostics Contract
This document defines the minimum public freshness and diagnostics payloads the SolaceScan frontend needs in order to present chain activity, transaction visibility, and snapshot posture without relying on frontend inference.
It is intended to close the remaining gap between:
- a frontend that now renders and explains state honestly, and
- upstream APIs that still omit authoritative freshness metadata for several critical surfaces.
Goal
The frontend should be able to answer these questions directly from public API fields:
- Is the chain head current?
- When was the latest visible transaction indexed?
- What is the latest non-empty block?
- Is the homepage using a live feed, a snapshot, or mixed evidence?
- Which subsystem is stale: RPC, indexing, relay monitoring, or stats?
- Which values are reported directly vs inferred vs unavailable?
The frontend should not have to infer these from a combination of:
/api/v2/stats/api/v2/main-page/blocks/api/v2/main-page/transactions/explorer-api/v1/track1/bridge/status
unless there is no backend alternative.
Design Principles
- Prefer explicit freshness fields over derived heuristics.
- Separate chain freshness from indexed-transaction freshness.
- Distinguish reported facts from inferred or partial facts.
- Make incompleteness first-class.
- Keep the contract calm and operational, not alarmist.
Proposed Public Endpoints
Two additions are recommended.
1. Extend GET /api/v2/stats
This endpoint already feeds the homepage summary cards. It should become the authoritative public summary for chain freshness and indexed activity freshness.
2. Extend GET /explorer-api/v1/track1/bridge/status
This endpoint already powers Mission Control. It should expose snapshot/feed posture and subsystem freshness more directly.
If backend implementation prefers separation, these fields may instead be exposed from a new endpoint:
GET /explorer-api/v1/track1/observability/freshness
The frontend does not require a separate endpoint as long as the fields below are available from a stable public contract.
Required Additions To /api/v2/stats
Current gaps
The current stats payload gives totals, but it does not reliably expose:
- latest indexed transaction timestamp
- latest non-empty block
- authoritative utilization freshness
- confidence/completeness metadata
Required fields
{
"total_blocks": 3873353,
"total_transactions": 52391,
"total_addresses": 10294,
"latest_block": 3873353,
"average_block_time": 2000,
"gas_prices": {
"slow": 0.02,
"average": 0.03,
"fast": 0.05
},
"network_utilization_percentage": 0,
"transactions_today": 18,
"freshness": {
"chain_head": {
"block_number": 3873353,
"timestamp": "2026-04-10T21:42:15Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
},
"latest_indexed_transaction": {
"hash": "0x...",
"block_number": 3858013,
"timestamp": "2026-04-10T12:31:05Z",
"age_seconds": 33070,
"source": "reported",
"confidence": "high"
},
"latest_non_empty_block": {
"block_number": 3858013,
"timestamp": "2026-04-10T12:31:05Z",
"age_seconds": 33070,
"distance_from_head": 15340,
"source": "reported",
"confidence": "high"
},
"latest_indexed_block": {
"block_number": 3873353,
"timestamp": "2026-04-10T21:42:15Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
}
},
"completeness": {
"transactions_feed": "complete",
"blocks_feed": "complete",
"gas_metrics": "partial",
"utilization_metrics": "partial"
},
"sampling": {
"stats_generated_at": "2026-04-10T21:42:16Z",
"stats_window_seconds": 300,
"rpc_probe_at": "2026-04-10T21:42:15Z"
}
}
Field Semantics
freshness.chain_head
The latest chain head known from the authoritative public RPC or canonical head source.
This is the answer to:
- "Is the chain alive?"
- "Is head visibility current?"
freshness.latest_indexed_transaction
The most recent transaction currently visible in the public indexed transaction feed.
This is the answer to:
- "How recent is the latest visible transaction?"
freshness.latest_non_empty_block
The most recent indexed block containing one or more transactions.
This is the answer to:
- "Are head blocks empty because the chain is quiet?"
- "What is the last block with visible activity?"
freshness.latest_indexed_block
The latest block successfully indexed into the explorer's public block dataset.
This disambiguates:
- current chain head
- current explorer indexed head
completeness.*
Simple public-facing availability states for each summary subsystem:
completepartialstaleunavailable
These should not be interpreted as outage severity; they describe data completeness only.
sampling.*
Metadata for when the summary itself was generated and what freshness window it represents.
Required Additions To Mission Control Payload
Mission Control currently provides useful relay detail, but the homepage still infers snapshot scope and partial feed posture from surrounding evidence.
Required fields
{
"data": {
"status": "degraded",
"checked_at": "2026-04-10T21:42:16Z",
"mode": {
"kind": "snapshot",
"updated_at": "2026-04-10T21:42:16Z",
"age_seconds": 1,
"reason": "live_homepage_stream_not_attached",
"scope": "relay_monitoring_homepage_card_only",
"source": "reported",
"confidence": "high"
},
"subsystems": {
"rpc_head": {
"status": "operational",
"updated_at": "2026-04-10T21:42:15Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
},
"tx_index": {
"status": "stale",
"updated_at": "2026-04-10T12:31:05Z",
"age_seconds": 33070,
"source": "reported",
"confidence": "high"
},
"bridge_relay_monitoring": {
"status": "degraded",
"updated_at": "2026-04-10T21:42:16Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
},
"stats_summary": {
"status": "partial",
"updated_at": "2026-04-10T21:42:16Z",
"age_seconds": 1,
"source": "reported",
"confidence": "medium"
}
}
}
}
Required Enumerations
These enums should be consistent across public surfaces.
Activity interpretation
activequietsparse_activityfresh_head_stale_tx_visibilitylimited_observability
This value should be emitted only when the backend can support it directly. Otherwise the frontend may continue to derive it as a presentation layer.
Data source confidence
highmediumlowunknown
Data origin
reportedinferredsampledunavailable
Completeness
completepartialstaleunavailable
Example Payloads
These examples are intended to accelerate frontend/backend alignment by showing how the contract should represent common live states.
Example A: Healthy Live State
{
"freshness": {
"chain_head": {
"block_number": 3874000,
"timestamp": "2026-04-10T22:10:14Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
},
"latest_indexed_block": {
"block_number": 3874000,
"timestamp": "2026-04-10T22:10:14Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
},
"latest_indexed_transaction": {
"hash": "0x...",
"block_number": 3873998,
"timestamp": "2026-04-10T22:10:10Z",
"age_seconds": 5,
"source": "reported",
"confidence": "high"
},
"latest_non_empty_block": {
"block_number": 3873998,
"timestamp": "2026-04-10T22:10:10Z",
"age_seconds": 5,
"distance_from_head": 2,
"source": "reported",
"confidence": "high"
}
},
"completeness": {
"transactions_feed": "complete",
"blocks_feed": "complete",
"gas_metrics": "complete",
"utilization_metrics": "complete"
}
}
Example B: Quiet Chain But Current
{
"freshness": {
"chain_head": {
"block_number": 3875000,
"timestamp": "2026-04-10T23:10:14Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
},
"latest_indexed_block": {
"block_number": 3875000,
"timestamp": "2026-04-10T23:10:14Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
},
"latest_indexed_transaction": {
"hash": "0x...",
"block_number": 3874902,
"timestamp": "2026-04-10T23:01:42Z",
"age_seconds": 512,
"source": "reported",
"confidence": "high"
},
"latest_non_empty_block": {
"block_number": 3874902,
"timestamp": "2026-04-10T23:01:42Z",
"age_seconds": 512,
"distance_from_head": 98,
"source": "reported",
"confidence": "high"
}
},
"activity_interpretation": "quiet"
}
Example C: Fresh Head, Stale Transaction Visibility
{
"freshness": {
"chain_head": {
"block_number": 3876000,
"timestamp": "2026-04-11T00:10:14Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
},
"latest_indexed_block": {
"block_number": 3875999,
"timestamp": "2026-04-11T00:10:12Z",
"age_seconds": 3,
"source": "reported",
"confidence": "high"
},
"latest_indexed_transaction": {
"hash": "0x...",
"block_number": 3860660,
"timestamp": "2026-04-10T15:02:10Z",
"age_seconds": 32900,
"source": "reported",
"confidence": "high"
},
"latest_non_empty_block": {
"block_number": 3860660,
"timestamp": "2026-04-10T15:02:10Z",
"age_seconds": 32900,
"distance_from_head": 15340,
"source": "reported",
"confidence": "high"
}
},
"activity_interpretation": "fresh_head_stale_tx_visibility",
"completeness": {
"transactions_feed": "stale",
"blocks_feed": "complete",
"gas_metrics": "partial",
"utilization_metrics": "partial"
}
}
Example D: Snapshot Mode State
{
"data": {
"status": "degraded",
"checked_at": "2026-04-11T00:10:15Z",
"mode": {
"kind": "snapshot",
"updated_at": "2026-04-11T00:10:15Z",
"age_seconds": 1,
"reason": "live_homepage_stream_not_attached",
"scope": "relay_monitoring_homepage_card_only",
"source": "reported",
"confidence": "high"
},
"subsystems": {
"rpc_head": {
"status": "operational",
"updated_at": "2026-04-11T00:10:14Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
},
"tx_index": {
"status": "stale",
"updated_at": "2026-04-10T15:02:10Z",
"age_seconds": 32900,
"source": "reported",
"confidence": "high"
},
"bridge_relay_monitoring": {
"status": "degraded",
"updated_at": "2026-04-11T00:10:15Z",
"age_seconds": 1,
"source": "reported",
"confidence": "high"
}
}
}
}
Frontend Usage Rules
Once the fields above exist, the frontend should follow these rules:
- Use backend freshness fields directly where present.
- Stop deriving latest transaction age from the transactions page feed when
freshness.latest_indexed_transactionis available. - Stop deriving last non-empty block from recent block scanning when
freshness.latest_non_empty_blockis available. - Use
mode.kind,mode.reason, andmode.scopedirectly for homepage snapshot messaging. - Use
sourceandconfidencebadges only where they improve trust and do not clutter.
Backward-Compatible Rollout Plan
Phase A
Add fields without removing any current keys:
- extend
/api/v2/stats - extend bridge status payload with
modeandsubsystems
Phase B
Frontend prefers new fields when available and falls back to inference when absent.
Phase C
Once fields are consistently present in production:
- reduce frontend inference paths
- remove duplicate explanatory fallback logic where it is no longer needed
Minimum Viable Backend Implementation
If full rollout is not possible immediately, the minimum high-leverage addition is:
/api/v2/stats
freshness.chain_headfreshness.latest_indexed_transactionfreshness.latest_non_empty_blocksampling.stats_generated_at
/explorer-api/v1/track1/bridge/status
mode.kindmode.updated_atmode.reasonmode.scope
That alone would materially reduce frontend ambiguity.
Why This Contract Matters
The frontend now presents state honestly enough that the remaining ambiguity is no longer visual. It is contractual.
Without these fields, the UI must keep inferring:
- whether the chain is quiet or stale
- whether the homepage is in snapshot mode because of relay posture or indexing posture
- whether low activity is real or a visibility gap
With these fields, the product becomes:
- more trustworthy
- easier to evaluate externally
- less likely to be misread as broken
Summary
The next backend milestone is not broad API expansion. It is a targeted public freshness contract.
The public explorer needs explicit answers for:
- current chain head
- current indexed head
- latest visible transaction
- last non-empty block
- snapshot/feed mode
- subsystem freshness/completeness
That is the smallest backend addition with the highest frontend trust impact.