Freshness diagnostics API, UI trust notes, mission control/stats updates, and deploy scripts.

Made-with: Cursor
This commit is contained in:
defiQUG
2026-04-12 06:33:54 -07:00
parent f46bd213ba
commit ee71f098ab
63 changed files with 5163 additions and 826 deletions

View File

@@ -1,5 +1,10 @@
# API Errors Fix
> Historical note: this file documents legacy static-SPA fixes and deploy
> patterns. The canonical live frontend deployment is now
> `./scripts/deploy-next-frontend-to-vmid5000.sh`. Treat manual `index.html`
> copy steps here as compatibility history, not the primary operator path.
## Issues Fixed
### 1. `createSkeletonLoader is not defined` Error
@@ -54,12 +59,12 @@ bash explorer-monorepo/scripts/deploy-frontend-fix.sh
#### Option 1: Using Deployment Script (from Proxmox host)
```bash
cd /home/intlc/projects/proxmox/explorer-monorepo
bash scripts/deploy-frontend-to-vmid5000.sh
bash scripts/deploy-next-frontend-to-vmid5000.sh
```
#### Option 2: Manual Deployment (from VMID 5000)
```bash
# On VMID 5000, copy the file:
# Historical static-SPA compatibility only:
cp /path/to/explorer-monorepo/frontend/public/index.html /var/www/html/index.html
chown www-data:www-data /var/www/html/index.html
@@ -69,6 +74,7 @@ nginx -t && systemctl restart nginx
#### Option 3: Using SCP (from local machine)
```bash
# Historical static-SPA compatibility only:
scp explorer-monorepo/frontend/public/index.html root@192.168.11.140:/var/www/html/index.html
ssh root@192.168.11.140 "chown www-data:www-data /var/www/html/index.html && nginx -t && systemctl restart nginx"
```
@@ -113,4 +119,3 @@ Test the following scenarios:
- For ChainID 138, all API calls now use Blockscout REST API format
- Error handling includes retry buttons for better UX
- Skeleton loaders provide visual feedback during data loading

View File

@@ -29,10 +29,10 @@ inside the explorer server block after `/api`, `/api/config/*`, `/explorer-api/*
### Legacy Static Deploy
```bash
# From explorer-monorepo root
./scripts/deploy.sh
```
The historical static SPA deploy path is deprecated. The old scripts
`./scripts/deploy.sh` and `./scripts/deploy-frontend-to-vmid5000.sh` now fail
fast with a deprecation message and intentionally point operators back to the
canonical Next.js deploy path.
### Manual Deploy
@@ -40,21 +40,25 @@ inside the explorer server block after `/api`, `/api/config/*`, `/explorer-api/*
# Canonical Next deployment:
./scripts/deploy-next-frontend-to-vmid5000.sh
# Legacy static fallback only:
scp frontend/public/index.html root@192.168.11.140:/var/www/html/index.html
# Compatibility assets only:
# frontend/public/index.html
# frontend/public/explorer-spa.js
```
### Environment Variables
The deployment script uses these environment variables:
The canonical Next deployment script uses its own VM/host defaults and deploy
workflow. Prefer reviewing `scripts/deploy-next-frontend-to-vmid5000.sh`
directly instead of relying on the older static script env contract below.
Historical static-script environment variables:
- `IP`: Production server IP (default: 192.168.11.140)
- `DOMAIN`: Domain name (default: explorer.d-bis.org)
- `PASSWORD`: SSH password (default: L@kers2010)
```bash
IP=192.168.11.140 DOMAIN=explorer.d-bis.org ./scripts/deploy.sh
```
These applied to the deprecated static deploy script and are no longer the
recommended operator interface.
## Mission-control and Track 4 runtime wiring
@@ -89,7 +93,7 @@ If deployment fails, rollback to previous version:
```bash
ssh root@192.168.11.140
cp /var/www/html/index.html.backup.* /var/www/html/index.html
ls -1dt /opt/solacescanscout/frontend/releases/* | head
```
For the Next standalone path, restart the previous release by repointing

View File

@@ -1,5 +1,9 @@
# Explorer API Access Checklist and Fixes
> Mixed-era note: this document spans both the current Next frontend and older
> static-SPA CSP/deploy history. The canonical frontend deployment path is now
> `./scripts/deploy-next-frontend-to-vmid5000.sh`.
The frontend is reachable at **https://explorer.d-bis.org** (FQDN) or by **VM IP** (**http://192.168.11.140**). In both cases it needs the **Blockscout v2 API** at the same origin under `/api/`. If you see **502 Bad Gateway**, **no blocks/transactions feeds**, or "Failed to load", the API may be unreachable. Use this checklist to verify and restore access.
**See also:** [EXPLORER_API_REFERENCE.md](EXPLORER_API_REFERENCE.md) for the list of Blockscout v2 endpoints used by the frontend.
@@ -135,7 +139,7 @@ The ethers.js v5 UMD bundle from the CDN uses `eval`/`new Function()` for ABI de
If the browser still reports **“Content Security Policy blocks the use of 'eval'”** or **script-src blocked**:
1. **Redeploy the frontend** so the live site gets the current `index.html` (with the meta CSP including `'unsafe-eval'`). For VMID 5000, run **`scripts/deploy-frontend-to-vmid5000.sh`** (frontend-only) or **`scripts/complete-explorer-api-access.sh`** (full). Alternatively, copy `frontend/public/index.html` to the servers web root (e.g. `/var/www/html/`).
1. **Redeploy the frontend** so the live site gets the current frontend bundle and nginx/API wiring. For VMID 5000, run **`scripts/deploy-next-frontend-to-vmid5000.sh`** for the canonical frontend path or **`scripts/complete-explorer-api-access.sh`** for the broader API/nginx fix flow. References to `frontend/public/index.html` below are historical static-SPA compatibility details.
2. **Check what CSP the browser sees** DevTools → Network → select the document request (the HTML page) → Headers → **Response Headers**`Content-Security-Policy`. It should contain `'unsafe-eval'` in `script-src`. If the response has a CSP header **without** `'unsafe-eval'`, that header is coming from your server (nginx or app) or from a proxy (e.g. Cloudflare). Update the config that serves the explorer so its CSP includes `'unsafe-eval'`, then reload (hard refresh or incognito).
3. **If you use Cloudflare** In the dashboard, check Transform Rules, Page Rules, or Security → Settings for any **Content-Security-Policy** (or similar) header that might override the origin. Ensure that headers `script-src` includes `'unsafe-eval'`, or remove the override so the origin CSP is used.

View File

@@ -1,5 +1,10 @@
# Explorer Code Review
> Historical architecture snapshot: this review reflects a mixed Next.js +
> legacy static-SPA period. The live frontend is now the Next standalone app,
> while `frontend/public/index.html` and `frontend/public/explorer-spa.js`
> remain compatibility/reference assets only.
**Date:** 2025-02
**Scope:** Backend (Go), Frontend (Next.js + SPA), libs, deployment, CI.
@@ -11,8 +16,8 @@
|-------|------|--------|
| **API** | Go 1.22, net/http | REST API (blocks, transactions, addresses, search, stats, Etherscan compat, auth, feature flags). Tiered tracks (14) with optional/required auth. |
| **Indexer** | Go, go-ethereum, pgx | Listens to chain (RPC/WS), processes blocks/txs, writes to PostgreSQL. |
| **Frontend (live)** | Vanilla JS SPA | `frontend/public/index.html` — single HTML + inline script, deployed at https://explorer.d-bis.org. Uses Blockscout-style API, ethers.js from CDN, VMID 2201 RPC. |
| **Frontend (dev)** | Next.js 15, React, TypeScript | `frontend/src/` — app + pages router, dev/build only; uses shared libs (api-client, ui-primitives). |
| **Frontend (live)** | Next.js 15, React, TypeScript | `frontend/src/` — standalone deployment on VMID 5000; uses shared libs and the explorer-owned freshness/trust model. |
| **Frontend (compatibility)** | Vanilla JS SPA | `frontend/public/index.html` + `frontend/public/explorer-spa.js` — retained for compatibility/reference, not the primary live deployment path. |
| **Libs** | In-repo under `backend/libs/`, `frontend/libs/` | go-pgconfig, go-logging, go-chain-adapters, go-rpc-gateway, go-http-middleware, go-bridge-aggregator; frontend-api-client, frontend-ui-primitives. |
---
@@ -161,7 +166,7 @@
|------|--------|
| **Next.js workspace warning** | Done: Comment added in `frontend/next.config.js`; align package manager in frontend or ignore for dev/build. (Next 14 does not support `outputFileTracingRoot` in config; standalone trace uses project root.) |
| **CORS** | Done: `CORS_ALLOWED_ORIGIN` env in `server.go`; default `*`, set to e.g. `https://explorer.d-bis.org` to restrict. Documented in `deployment/ENVIRONMENT_TEMPLATE.env`. |
| **SPA file size** | Done: main app script extracted to `frontend/public/explorer-spa.js` (~3.5k lines); `index.html` now ~1.15k lines. Deploy scripts copy `explorer-spa.js` (e.g. `deploy-frontend-to-vmid5000.sh`, `deploy.sh`). |
| **SPA file size** | Historical compatibility asset: main app script extracted to `frontend/public/explorer-spa.js` (~3.5k lines); `index.html` now ~1.15k lines. The old deploy scripts are deprecated shims rather than active operator paths. |
| **SPA vs Next canonical** | Done: `README.md` states production serves the SPA, Next.js is for local dev and build validation only. |
| **CSP unsafe-eval** | Done: comment in `index.html` CSP meta updated: "Can be removed when moving to ethers v6 build (no UMD eval)." |
| **Further product work** | See `docs/EXPLORER_ADDITIONAL_RECOMMENDATIONS.md` (i18n, event log decoding, token list, health endpoint, etc.). |

View File

@@ -0,0 +1,154 @@
# Explorer Dead-Ends, Gaps, and Orphans Audit
Date: 2026-04-11
This audit records the remaining pruning surface after the frontend trust,
freshness, and deployment-path cleanup work. The goal is to distinguish
high-signal cleanup targets from compatibility or historical assets that should
not be deleted casually.
## Canonical Live Paths
- Frontend deploy: `scripts/deploy-next-frontend-to-vmid5000.sh`
- Frontend runtime: `solacescanscout-frontend.service`
- Shared freshness/trust model:
- `frontend/src/utils/explorerFreshness.ts`
- `frontend/src/components/common/FreshnessTrustNote.tsx`
- `frontend/src/components/common/ActivityContextPanel.tsx`
- Explorer-owned freshness backend:
- `backend/api/freshness/`
- `backend/api/rest/stats.go`
- `backend/api/track1/bridge_status_data.go`
## Pruned in This Cleanup Series
- Deprecated static deploy scripts now fail fast and point to the canonical
Next deploy path:
- `scripts/deploy-frontend-to-vmid5000.sh`
- `scripts/deploy.sh`
- Removed relay-summary compatibility helpers from:
- `frontend/src/services/api/missionControl.ts`
- Removed duplicate route action from:
- `frontend/src/data/explorerOperations.ts`
- Hardened deploy build-lock behavior in:
- `scripts/deploy-next-frontend-to-vmid5000.sh`
## Dead-End Guidance Fixed
The following docs were updated to stop presenting deprecated static frontend
deployment as a current operator path:
- `docs/README.md`
- `docs/INDEX.md`
- `docs/DEPLOYMENT.md`
- `README_DEPLOYMENT.md`
## Remaining Historical / Compatibility Assets To Keep For Now
These are not current primary paths, but they still serve compatibility,
reference, or audit roles and should not be removed without a deliberate
migration decision:
- `frontend/public/index.html`
- `frontend/public/explorer-spa.js`
- `frontend/public/chain138-command-center.html`
- `deployment/common/nginx-api-location.conf`
## Remaining Gaps
### 0. Static compatibility assets are not orphaned yet
The following assets are still part of the runtime or deployment surface and
cannot be deleted safely in a pure pruning pass:
- `frontend/public/index.html`
- `frontend/public/explorer-spa.js`
- `frontend/public/chain138-command-center.html`
Current hard blockers:
- canonical deploy script still copies them:
- `scripts/deploy-next-frontend-to-vmid5000.sh`
- live product still links the command center:
- `frontend/src/components/common/Navbar.tsx`
- `frontend/src/components/common/Footer.tsx`
- `frontend/src/data/explorerOperations.ts`
- `frontend/src/pages/docs/index.tsx`
- compatibility/runtime verification still expects them:
- `scripts/verify-explorer-api-access.sh`
- several legacy remediation scripts still push the static SPA to
`/var/www/html/index.html`:
- `scripts/deploy-frontend-fix.sh`
- `scripts/fix-explorer-remote.sh`
- `scripts/fix-explorer-complete.sh`
- `scripts/complete-explorer-api-access.sh`
Recommendation:
- treat retirement of these assets as an explicit migration
- first decide whether the command center remains a supported public artifact
- then remove static-SPA push logic from the remediation scripts
- only after that delete the files and clean the remaining references
### 1. Historical docs still describe the old static SPA as if it were primary
These are not the best operator entry points anymore, but they appear to be
historical records, troubleshooting notes, or code-review artifacts rather than
active runbooks:
- `docs/FRONTEND_DEPLOYMENT_FIX.md`
- `docs/FRONTEND_FIXES_COMPLETE.md`
- `docs/API_ERRORS_FIX.md`
- `docs/EXPLORER_LOADING_TROUBLESHOOTING.md`
- `docs/EXPLORER_API_ACCESS.md`
- `docs/EXPLORER_CODE_REVIEW.md`
- `docs/EXPLORER_FRONTEND_TESTING.md`
- `docs/STRUCTURE.md`
- `docs/TIERED_ARCHITECTURE_IMPLEMENTATION.md`
Recommendation:
- keep them for now
- a first banner-stamp sweep has already been applied to the highest-signal set
- only rewrite/delete them if we decide to retire the compatibility assets
### 2. Compatibility assets still create pruning ambiguity
The repo still contains both:
- the live Next frontend path
- the historical static SPA assets
Recommendation:
- keep the compatibility assets until all docs and operators no longer depend on
them for rollback/reference
- when retired, remove the assets and do a repo-wide `frontend/public/index.html`
reference cleanup in one explicit migration
### 3. Public routing ownership is still split
Freshness truth is now much cleaner, but public route ownership still spans:
- Blockscout-owned public API behavior
- explorer-owned `track1` / mission-control behavior
- Next frontend presentation logic
Recommendation:
- continue consolidating around the explorer-owned freshness contract
- treat backend source-of-truth wiring as the next cleanup frontier, not more
shell polish
## Orphaned / Removed Compatibility Paths Confirmed Gone
These frontend compatibility abstractions were fully removed and should not be
reintroduced:
- `getRelaySummary` in `frontend/src/services/api/missionControl.ts`
- `subscribeRelaySummary` in `frontend/src/services/api/missionControl.ts`
## Suggested Next Pruning Sweep
1. Stamp the historical static-SPA docs above with a clear banner:
`Historical static-SPA guidance; not the canonical deployment path.`
2. Decide whether `frontend/public/index.html` and `frontend/public/explorer-spa.js`
still have an operational rollback role.
3. If not, remove them in one explicit migration and clean all remaining
references repo-wide.
4. After that, re-run the dead-end/orphan audit and remove the remaining
compatibility mentions from troubleshooting docs.

View File

@@ -1,10 +1,15 @@
# Explorer Frontend Testing
> Historical note: this testing note captures legacy static-SPA routing
> behavior during the explorer transition. The canonical live frontend is now
> the Next standalone app deployed with
> `./scripts/deploy-next-frontend-to-vmid5000.sh`.
## Summary
Path-based URLs (e.g. `/address/0x99b3511a2d315a497c8112c1fdd8d508d4b1e506`) now work on the explorer. The fix includes:
1. **SPA path-based routing** `applyHashRoute()` in `frontend/public/index.html` reads both `pathname` and `hash`, so `/address/0x...`, `/tx/0x...`, `/block/123`, etc. load correctly.
1. **SPA path-based routing** historically, `applyHashRoute()` in `frontend/public/index.html` read both `pathname` and `hash`, so `/address/0x...`, `/tx/0x...`, `/block/123`, etc. loaded correctly.
2. **Nginx SPA paths** Nginx serves `index.html` for `/address`, `/tx`, `/block`, `/token`, `/blocks`, `/transactions`, `/bridge`, `/weth`, `/watchlist`, and `/nft`.
3. **HTTP + HTTPS** Both HTTP (for internal tests) and HTTPS serve the SPA for these paths.

View File

@@ -1,5 +1,9 @@
# Explorer "Loading…" / "—" Troubleshooting
> Historical note: parts of this troubleshooting guide still refer to the old
> static-SPA deployment path. The current production frontend is the Next
> standalone app deployed with `./scripts/deploy-next-frontend-to-vmid5000.sh`.
When **`/api/v2/stats`** returns 200 with data but the SPA still shows "—" or "Loading blocks…" / "Loading transactions…" / "Loading bridge data…" / "Tokens: Loading…", the failure is in **frontend→API wiring** or **frontend runtime**.
## Expected UI (screenshots)
@@ -71,13 +75,13 @@ After editing `frontend/public/explorer-spa.js`, redeploy the frontend to VMID 5
```bash
# From repo root (with SSH to r630-02)
EXPLORER_VM_HOST=root@192.168.11.12 bash explorer-monorepo/scripts/deploy-frontend-to-vmid5000.sh
EXPLORER_VM_HOST=root@192.168.11.12 bash explorer-monorepo/scripts/deploy-next-frontend-to-vmid5000.sh
```
Or from the Proxmox host that runs VMID 5000:
```bash
/path/to/repo/explorer-monorepo/scripts/deploy-frontend-to-vmid5000.sh
/path/to/repo/explorer-monorepo/scripts/deploy-next-frontend-to-vmid5000.sh
```
Then hard-refresh the explorer (Ctrl+Shift+R / Cmd+Shift+R) and re-check Console + Network.

View File

@@ -1,9 +1,15 @@
# Frontend Deployment Fix
> Historical note: this document describes the legacy static-SPA deployment
> path. The canonical live frontend deployment is now
> `./scripts/deploy-next-frontend-to-vmid5000.sh`. Keep this file only as
> compatibility/audit reference unless you are deliberately working on the old
> static assets.
## Problem
The explorer at `https://explorer.d-bis.org/` shows "Page not found" because:
1. Nginx is proxying to Blockscout (port 4000) which serves its own UI
2. The custom frontend (`explorer-monorepo/frontend/public/index.html`) is not deployed to `/var/www/html/` on VMID 5000
2. The historical custom static frontend (`explorer-monorepo/frontend/public/index.html`) is not deployed to `/var/www/html/` on VMID 5000
## Solution
@@ -33,21 +39,21 @@ Deploy the custom frontend to `/var/www/html/index.html`:
**From Proxmox host:**
```bash
cd /home/intlc/projects/proxmox/explorer-monorepo
bash scripts/deploy-frontend-to-vmid5000.sh
bash scripts/deploy-next-frontend-to-vmid5000.sh
```
**Or manually from VMID 5000:**
```bash
# If you have access to the repo in VMID 5000
# Historical static-SPA compatibility only:
cp /home/intlc/projects/proxmox/explorer-monorepo/frontend/public/index.html /var/www/html/index.html
chown www-data:www-data /var/www/html/index.html
```
**Or using SSH from Proxmox host:**
```bash
# Using existing deploy script
# Deprecated static deploy shim; kept only for historical compatibility context
cd /home/intlc/projects/proxmox/explorer-monorepo
PASSWORD="L@kers2010" bash scripts/deploy.sh
bash scripts/deploy.sh
```
### Step 3: Verify
@@ -95,4 +101,3 @@ The updated nginx config:
## Files Modified
- `/etc/nginx/sites-available/blockscout` - Nginx configuration
- `/var/www/html/index.html` - Custom frontend (needs to be deployed)

View File

@@ -1,5 +1,10 @@
# Frontend Errors - Complete Fix Summary
> Historical note: this fix summary was written against the older static-SPA
> frontend. The canonical live frontend is now the Next standalone app on VMID
> 5000, while `frontend/public/index.html` remains a compatibility/reference
> asset.
**Date**: $(date)
**Status**: ✅ **ALL FIXES APPLIED**
@@ -139,4 +144,3 @@ Fetching blocks from Blockscout: https://explorer.d-bis.org/api/v2/blocks?page=1
---
**Status**: ✅ All frontend errors have been fixed and tested.

View File

@@ -17,7 +17,7 @@
- **[../frontend/FRONTEND_REVIEW.md](../frontend/FRONTEND_REVIEW.md)** - Frontend code review (SPA + React)
- **[../frontend/FRONTEND_TASKS_AND_REVIEW.md](../frontend/FRONTEND_TASKS_AND_REVIEW.md)** - Task list (C1L4) and detail review
- **Deploy frontend:** From repo root run `./scripts/deploy-frontend-to-vmid5000.sh`
- **Deploy frontend:** From repo root run `./scripts/deploy-next-frontend-to-vmid5000.sh`
- **Full fix (API + frontend):** `./scripts/complete-explorer-api-access.sh`
---
@@ -198,4 +198,3 @@
---
**Last Updated:** 2025-02-09

View File

@@ -29,11 +29,12 @@ Overview of documentation for the ChainID 138 Explorer (SolaceScan).
Nginx should preserve `/api`, `/api/config/*`, `/explorer-api/*`, `/token-aggregation/api/v1/*`, `/snap/`, and `/health`, then proxy `/` and `/_next/` using [deployment/common/nginx-next-frontend-proxy.conf](/home/intlc/projects/proxmox/explorer-monorepo/deployment/common/nginx-next-frontend-proxy.conf).
**Legacy static SPA deploy (fallback only):**
**Legacy static SPA compatibility assets:**
```bash
./scripts/deploy-frontend-to-vmid5000.sh
```
The historical static SPA (`frontend/public/index.html` +
`frontend/public/explorer-spa.js`) remains in-repo for compatibility and audit
reference only. The old static deploy scripts are deprecated shims and are not
supported deployment targets.
**Full explorer/API fix (Blockscout + nginx + frontend):**
@@ -61,4 +62,4 @@ Nginx should preserve `/api`, `/api/config/*`, `/explorer-api/*`, `/token-aggreg
---
*Last updated: 2025-02-09*
*Last updated: 2026-04-11*

View File

@@ -1,5 +1,9 @@
# Reusable Components Extraction Plan
> Historical planning note: references in this file to the explorer SPA or old
> deploy scripts describe the pre-Next transition state. The canonical live
> frontend deployment is now `./scripts/deploy-next-frontend-to-vmid5000.sh`.
**Completion status (in-repo):** All libs are present under `backend/libs/` and `frontend/libs/`. Backend is wired to use **go-pgconfig** (API + indexer), **go-rpc-gateway** (Track 1). Frontend is wired to use **frontend-api-client** (services/api/client) and **frontend-ui-primitives** (all pages using Card, Table, Address). CI uses `submodules: recursive`; README documents clone with submodules. To publish as separate repos, copy each lib to its own repo and add as submodule.
**Review and test:** Backend handlers that need the DB use `requireDB(w)`; without a DB they return 503. Tests run with a nil DB and accept 200/503/404 as appropriate. Run backend tests: `go test ./...` in `backend/`. Frontend build: `npm run build` in `frontend/` (ESLint uses root `.eslintrc.cjs` and frontend `"root": true` in `.eslintrc.json`). E2E: `npm run e2e` from repo root (Playwright, default base URL https://explorer.d-bis.org; set `EXPLORER_URL` for local).
@@ -216,7 +220,7 @@ To minimize breakage and respect dependencies:
- **Backend:** All REST route handlers, track* endpoint logic, indexer (listener + processor + backfill), Blockscout/etherscan compatibility, explorer-specific config (chain_id 138, RPC URLs), and migrations (schema stays here; libs use interfaces or config).
- **Frontend:** All pages and views, `public/index.html` SPA, explorer API service modules (blocks, transactions, addresses), Next.js app and deployment config.
- **Deployment:** All explorer- and VMID 5000specific scripts (`fix-502-blockscout.sh`, `complete-explorer-api-access.sh`, `deploy-frontend-to-vmid5000.sh`, etc.), and nginx/config that reference explorer.d-bis.org and Blockscout.
- **Deployment:** All explorer- and VMID 5000specific scripts (`fix-502-blockscout.sh`, `complete-explorer-api-access.sh`, legacy `deploy-frontend-to-vmid5000.sh`, etc.), and nginx/config that reference explorer.d-bis.org and Blockscout.
- **Docs:** All current documentation (API access, deployment, runbook, etc.).
---

View File

@@ -1,13 +1,18 @@
# Monorepo Structure
> Historical note: this file still reflects the earlier static-SPA-oriented
> layout. The canonical live frontend is now the Next app in `frontend/src/`,
> deployed via `./scripts/deploy-next-frontend-to-vmid5000.sh`.
## Directory Overview
### `/frontend`
Frontend application code.
- **`public/`**: Static HTML, CSS, JavaScript files served directly
- `index.html`: Main explorer interface
- **`src/`**: Source files (if using build tools like webpack, vite, etc.)
- **`public/`**: Compatibility/reference static assets
- `index.html`: Historical static explorer interface
- `explorer-spa.js`: Historical extracted SPA script
- **`src/`**: Canonical Next.js frontend source
- **`assets/`**: Images, fonts, and other static assets
### `/backend`
@@ -19,7 +24,8 @@ Backend services (if needed for future enhancements).
### `/scripts`
Deployment and utility scripts.
- **`deploy.sh`**: Deploy explorer to production
- **`deploy-next-frontend-to-vmid5000.sh`**: Canonical frontend deploy
- **`deploy.sh`**: Deprecated static deploy shim
- **`test.sh`**: Test explorer functionality
### `/docs`
@@ -65,7 +71,7 @@ explorer-monorepo/
### Frontend Changes
1. Edit `frontend/public/index.html` directly (current approach)
1. Edit `frontend/src/` for the live frontend (current approach)
2. Or set up build tools in `frontend/src/` for compiled output
### Backend Changes
@@ -77,4 +83,3 @@ explorer-monorepo/
1. Add docs to `docs/` directory
2. Update README.md as needed

View File

@@ -1,5 +1,9 @@
# Tiered Architecture Implementation Summary
> Historical note: this implementation summary was written during the
> static-SPA/Next transition. References to `frontend/public/index.html`
> describe legacy feature-gating work, not the canonical live frontend path.
## Overview
The SolaceScan Explorer has been successfully upgraded to a 4-track tiered architecture with feature-gated access control.
@@ -41,7 +45,7 @@ All components have been implemented according to the plan:
- **Security**: IP whitelist and audit logging integrated
### ✅ Phase 7: Frontend & Integration
- **Frontend Feature Gating**: Wallet connect UI and track-based feature visibility (`frontend/public/index.html`)
- **Frontend Feature Gating**: Wallet connect UI and track-based feature visibility (historically in `frontend/public/index.html`, now carried forward in the Next frontend shell)
- **Route Integration**: Track-aware routing structure (`backend/api/rest/routes.go`)
## Architecture
@@ -84,7 +88,7 @@ Backend
- `backend/database/migrations/0010_track_schema.auth_only.sql` - shared Blockscout DB auth/operator subset
### Frontend
- Updated `frontend/public/index.html` with feature gating
- Updated the historical static SPA `frontend/public/index.html` with feature gating during the transition period
## Next Steps

View File

@@ -0,0 +1,530 @@
# 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:
1. Is the chain head current?
2. When was the latest visible transaction indexed?
3. What is the latest non-empty block?
4. Is the homepage using a live feed, a snapshot, or mixed evidence?
5. Which subsystem is stale: RPC, indexing, relay monitoring, or stats?
6. 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
```json
{
"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:
- `complete`
- `partial`
- `stale`
- `unavailable`
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
```json
{
"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
- `active`
- `quiet`
- `sparse_activity`
- `fresh_head_stale_tx_visibility`
- `limited_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
- `high`
- `medium`
- `low`
- `unknown`
### Data origin
- `reported`
- `inferred`
- `sampled`
- `unavailable`
### Completeness
- `complete`
- `partial`
- `stale`
- `unavailable`
## 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
```json
{
"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
```json
{
"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
```json
{
"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
```json
{
"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:
1. Use backend freshness fields directly where present.
2. Stop deriving latest transaction age from the transactions page feed when `freshness.latest_indexed_transaction` is available.
3. Stop deriving last non-empty block from recent block scanning when `freshness.latest_non_empty_block` is available.
4. Use `mode.kind`, `mode.reason`, and `mode.scope` directly for homepage snapshot messaging.
5. Use `source` and `confidence` badges 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 `mode` and `subsystems`
### 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_head`
- `freshness.latest_indexed_transaction`
- `freshness.latest_non_empty_block`
- `sampling.stats_generated_at`
### `/explorer-api/v1/track1/bridge/status`
- `mode.kind`
- `mode.updated_at`
- `mode.reason`
- `mode.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.

View File

@@ -0,0 +1,278 @@
# Explorer Freshness Implementation Checklist
This checklist converts the freshness contract into a backend implementation plan against the current SolaceScan code paths:
- [stats.go](/home/intlc/projects/proxmox/explorer-monorepo/backend/api/rest/stats.go)
- [mission_control.go](/home/intlc/projects/proxmox/explorer-monorepo/backend/api/rest/mission_control.go)
Use this document as the handoff from frontend trust requirements to backend delivery.
See also:
- [EXPLORER_FRESHNESS_DIAGNOSTICS_CONTRACT.md](/home/intlc/projects/proxmox/explorer-monorepo/docs/api/EXPLORER_FRESHNESS_DIAGNOSTICS_CONTRACT.md)
- [track-api-contracts.md](/home/intlc/projects/proxmox/explorer-monorepo/docs/api/track-api-contracts.md)
## Scope
This checklist covers four buckets:
1. field ownership and source of truth
2. response-shape rollout
3. freshness semantics
4. confidence and completeness behavior
## Bucket 1: Field Ownership And Source Of Truth
| Field | Endpoint | Backend owner | Source of truth | Directly measured or derived | Cadence | Nullable | Frontend dependency |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `freshness.chain_head.block_number` | `/api/v2/stats` | `stats.go` with RPC helper | authoritative public RPC head | directly measured | per stats request or short cache | no | homepage head freshness, blocks/trust cues |
| `freshness.chain_head.timestamp` | `/api/v2/stats` | `stats.go` with RPC helper | authoritative public RPC head block timestamp | directly measured | per stats request or short cache | no | head age, chain visibility |
| `freshness.latest_indexed_block.block_number` | `/api/v2/stats` | `stats.go` | explorer DB `MAX(blocks.number)` | directly measured | per stats request | no | distinguish head vs indexed head |
| `freshness.latest_indexed_block.timestamp` | `/api/v2/stats` | `stats.go` | explorer DB latest indexed block timestamp | directly measured | per stats request | yes until wired | detail-page trust cues |
| `freshness.latest_indexed_transaction.hash` | `/api/v2/stats` | `stats.go` | explorer DB latest indexed tx row | directly measured | per stats request | yes | activity summary |
| `freshness.latest_indexed_transaction.block_number` | `/api/v2/stats` | `stats.go` | explorer DB latest indexed tx row | directly measured | per stats request | yes | tx freshness explanation |
| `freshness.latest_indexed_transaction.timestamp` | `/api/v2/stats` | `stats.go` | explorer DB latest indexed tx row | directly measured | per stats request | yes | tx age, stale tx visibility |
| `freshness.latest_non_empty_block.block_number` | `/api/v2/stats` | `stats.go` | explorer DB latest block where `transaction_count > 0` or equivalent join | derived from indexed block/tx data | per stats request | yes | quiet-chain vs stale-visibility interpretation |
| `freshness.latest_non_empty_block.timestamp` | `/api/v2/stats` | `stats.go` | explorer DB latest non-empty block row | derived from indexed block/tx data | per stats request | yes | recent activity framing |
| `freshness.latest_non_empty_block.distance_from_head` | `/api/v2/stats` | `stats.go` | computed from chain head minus last non-empty block | derived | per stats request | yes | homepage block-gap explanation |
| `completeness.transactions_feed` | `/api/v2/stats` | `stats.go` | comparison of tx freshness vs head freshness | derived | per stats request | no | trust badges |
| `completeness.blocks_feed` | `/api/v2/stats` | `stats.go` | indexed block freshness vs chain head freshness | derived | per stats request | no | trust badges |
| `completeness.gas_metrics` | `/api/v2/stats` | `stats.go` | gas fields presence and quality | derived | per stats request | no | gas card honesty |
| `completeness.utilization_metrics` | `/api/v2/stats` | `stats.go` | utilization field presence and quality | derived | per stats request | no | utilization card honesty |
| `sampling.stats_generated_at` | `/api/v2/stats` | `stats.go` | server clock at response generation | directly measured | per response | no | “updated” copy |
| `sampling.rpc_probe_at` | `/api/v2/stats` | `stats.go` | latest successful RPC sample timestamp | directly measured or nullable | per stats request | yes | source confidence |
| `mode.kind` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | mission-control feed mode | directly measured if known, otherwise derived conservatively | per response / SSE tick | no | snapshot/live messaging |
| `mode.updated_at` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | mission-control snapshot timestamp | directly measured | per response | no | snapshot age |
| `mode.reason` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | bridge/homepage mode controller | directly measured if available, else nullable | per response | yes | scope explanation |
| `mode.scope` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | bridge/homepage mode controller | directly measured if available, else nullable | per response | yes | “what is affected?” |
| `subsystems.rpc_head.*` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | RPC probe result | directly measured | per response | no | mission-control trust cues |
| `subsystems.tx_index.*` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` using stats freshness or shared helper | explorer DB tx freshness | derived from authoritative indexed data | per response / shared cache | yes | homepage stale-tx explanation |
| `subsystems.bridge_relay_monitoring.*` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` | existing relay probe payload | directly measured | per response | no | lane posture |
| `subsystems.stats_summary.*` | `/explorer-api/v1/track1/bridge/status` | `mission_control.go` or shared summary helper | stats freshness sample | derived | per response | yes | homepage summary confidence |
## Bucket 2: Response-Shape Rollout
### Ship immediately as nullable additions
These are low-risk additive fields that can be introduced without breaking existing clients.
- `freshness.latest_indexed_transaction.*`
- `freshness.latest_non_empty_block.*`
- `freshness.latest_indexed_block.timestamp`
- `sampling.stats_generated_at`
- `sampling.rpc_probe_at`
- `mode.kind`
- `mode.updated_at`
- `mode.reason`
- `mode.scope`
- `subsystems.*`
### Ship after backend wiring
These need real data acquisition or shared helpers.
- `freshness.chain_head.*`
- `completeness.transactions_feed`
- `completeness.blocks_feed`
- `completeness.gas_metrics`
- `completeness.utilization_metrics`
### Derived computations
These may be computed in backend code once the authoritative inputs exist.
- `freshness.latest_non_empty_block.distance_from_head`
- subsystem `status`
- completeness enums
- optional `activity_interpretation`
### Frontend adoption order
1. Prefer new fields when present.
2. Fall back to current inference when absent.
3. Remove inference once fields are stable in production.
## Bucket 3: Freshness Semantics
Each field must answer a precise question.
### `freshness.chain_head`
- Meaning: latest chain head observed from the authoritative public RPC
- Must not mean: latest indexed explorer block
- If unknown: return `null` object members where needed plus completeness/confidence state
### `freshness.latest_indexed_block`
- Meaning: latest block successfully indexed into the explorer DB or visible explorer block source
- Must not mean: latest RPC head
### `freshness.latest_indexed_transaction`
- Meaning: latest transaction currently visible in the public indexed transaction feed
- Must not mean: latest mempool event or latest raw RPC tx if not visible in the explorer feed
### `freshness.latest_non_empty_block`
- Meaning: latest indexed block containing at least one visible indexed transaction
- This is the critical disambiguator for quiet-chain vs stale-visibility interpretation
### `mode.kind`
- Meaning: the current homepage/mission-control delivery mode
- Allowed values: `live`, `snapshot`, `mixed`, `unknown`
### `mode.scope`
- Meaning: which user-visible surface is affected by mode choice
- Examples:
- `relay_monitoring_homepage_card_only`
- `homepage_summary_only`
- `bridge_monitoring_and_homepage`
### `mode.reason`
- Meaning: why snapshot or mixed mode is active
- Must be calm and operational, not blame-oriented
- Examples:
- `live_homepage_stream_not_attached`
- `relay_snapshot_only_source`
- `partial_observability_inputs`
### `subsystems.*`
- Meaning: freshness of each component, not overall product health
- Recommended subsystem keys:
- `rpc_head`
- `tx_index`
- `bridge_relay_monitoring`
- `stats_summary`
## Bucket 4: Confidence And Completeness
Every nullable or derived field should have explicit semantics.
### Confidence
- `high`: authoritative source and recent sample
- `medium`: authoritative source but partially stale, or a stable derived value from strong inputs
- `low`: weakly derived or missing one of the underlying inputs
- `unknown`: no basis to express confidence
### Completeness
- `complete`: field is current and supported by recent source data
- `partial`: field exists but some required inputs are missing or weak
- `stale`: field is known, but the latest available value is older than acceptable freshness
- `unavailable`: no trustworthy value exists
### Null and zero handling
- Unknown must be `null`, not synthetic `0`
- Zero may be returned only when zero is a real measured value
- If a value is null, a sibling completeness/confidence field must explain why
## Acceptance Tests
These should be implemented in backend tests and used as rollout gates.
### 1. Current head, stale tx visibility
If chain head is current but tx visibility is stale:
- `freshness.chain_head` must be current
- `freshness.latest_indexed_transaction` must be older
- `freshness.latest_non_empty_block` must be exposed
- completeness must not report all feeds as `complete`
### 2. Quiet chain, current visibility
If recent head blocks are genuinely empty:
- `freshness.chain_head` must still be current
- `freshness.latest_non_empty_block` must be present
- `freshness.latest_indexed_transaction` must be present
- API must not force a stale diagnosis if visibility itself is current
### 3. Snapshot mode active
If snapshot mode is active:
- `mode.kind` must be `snapshot` or `mixed`
- `mode.scope` must state what is affected
- `mode.reason` must be present if known
### 4. Unknown fields
If a field is unknown:
- return `null`
- expose confidence/completeness state
- do not return fake zero values
## Backend Implementation Checklist
### `stats.go`
- [ ] Extend `explorerStats` with nullable freshness/completeness/sampling fields.
- [ ] Add query/helper for latest indexed transaction.
- [ ] Add query/helper for latest non-empty block.
- [ ] Add query/helper for latest indexed block timestamp.
- [ ] Add RPC helper for current chain head number and timestamp.
- [ ] Compute `distance_from_head` when both chain head and latest non-empty block are present.
- [ ] Compute completeness enums for blocks, transactions, gas metrics, and utilization.
- [ ] Return `null` for unknowns rather than synthetic zero values.
- [ ] Add internal tests covering:
- healthy current state
- quiet-chain state
- stale tx visibility state
- null/unknown field handling
### `mission_control.go`
- [ ] Extend bridge status response with `mode`.
- [ ] Extend bridge status response with `subsystems`.
- [ ] Reuse or call shared freshness helper for tx index freshness rather than duplicating logic.
- [ ] Emit `mode.scope` and `mode.reason` only when backend can support them.
- [ ] Use `unknown` or nullable values when reason/scope cannot be stated authoritatively.
- [ ] Add tests covering:
- live mode
- snapshot mode
- mixed mode
- tx index stale while RPC head remains current
### Shared rollout
- [ ] Frontend reads new fields opportunistically.
- [ ] Existing frontend inference remains as fallback until backend fields are stable.
- [ ] Swagger/OpenAPI docs updated after implementation.
- [ ] Public docs updated only after payload shape is live.
## Test Coverage Guidance
For every field, capture:
- who computes it
- from what source
- at what cadence
- whether nullable or required
- fallback behavior
- confidence/completeness semantics
- frontend dependency
- backend test case name
That metadata is more important than perfect initial coverage breadth.
## Shortest Path To Value
If the team wants the fastest possible trust win, implement these first:
1. `freshness.chain_head`
2. `freshness.latest_indexed_transaction`
3. `freshness.latest_non_empty_block`
4. `sampling.stats_generated_at`
5. `mode.kind`
6. `mode.scope`
7. `mode.reason`
That is the minimum set that lets the frontend stop guessing about the most visible freshness ambiguity.

View File

@@ -1,5 +1,7 @@
# Track API Contracts
See also: [EXPLORER_FRESHNESS_DIAGNOSTICS_CONTRACT.md](/home/intlc/projects/proxmox/explorer-monorepo/docs/api/EXPLORER_FRESHNESS_DIAGNOSTICS_CONTRACT.md) for the public freshness and observability fields required by the current SolaceScan frontend.
Complete API contract definitions for all 4 tracks of SolaceScan Explorer.
## Track 1: Public Meta Explorer (No Auth Required)