Freshness diagnostics API, UI trust notes, mission control/stats updates, and deploy scripts.
Made-with: Cursor
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 server’s 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 header’s `script-src` includes `'unsafe-eval'`, or remove the override so the origin CSP is used.
|
||||
|
||||
|
||||
@@ -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 (1–4) 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.). |
|
||||
|
||||
154
docs/EXPLORER_DEADENDS_GAPS_ORPHANS_AUDIT_2026-04-11.md
Normal file
154
docs/EXPLORER_DEADENDS_GAPS_ORPHANS_AUDIT_2026-04-11.md
Normal 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.
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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 (C1–L4) 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
|
||||
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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 5000–specific 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 5000–specific 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.).
|
||||
|
||||
---
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
530
docs/api/EXPLORER_FRESHNESS_DIAGNOSTICS_CONTRACT.md
Normal file
530
docs/api/EXPLORER_FRESHNESS_DIAGNOSTICS_CONTRACT.md
Normal 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.
|
||||
278
docs/api/EXPLORER_FRESHNESS_IMPLEMENTATION_CHECKLIST.md
Normal file
278
docs/api/EXPLORER_FRESHNESS_IMPLEMENTATION_CHECKLIST.md
Normal 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.
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user