# Explorer API Access – Checklist and Fixes

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.

---

## No feeds (blocks/transactions empty or stuck on “Loading…”)

- **Frontend** now loads stats, blocks, and transactions **immediately** on page load (it no longer waits for the ethers.js library). If feeds are still empty:
  1. **Check the API** – Open DevTools → Network, reload the page, and look for requests to `/api/v2/blocks` and `/api/v2/transactions`. If they return **502** or fail, use [Fix 502 Bad Gateway](#fix-502-bad-gateway-one-script) and ensure Blockscout is up and nginx proxies `/api/` to port 4000.
  2. **Same-origin /api** – When the site is served from the explorer host (FQDN `https://explorer.d-bis.org` or VM IP `http://192.168.11.140` / `https://192.168.11.140`), the frontend uses relative `/api` so all requests go through the same nginx proxy. If you open the frontend from elsewhere, the code falls back to the full Blockscout URL (CORS must allow it).
- If the API returns **200** but the UI still shows no data, check the browser console for JavaScript errors (e.g. CSP or network errors).

### Frontend env contract

For the Next frontend in `frontend/`, keep the runtime base URL at the **host origin**, not the `/api` subpath:

```env
NEXT_PUBLIC_API_URL=https://explorer.d-bis.org
NEXT_PUBLIC_CHAIN_ID=138
```

Why:

- The Next pages now call live **Blockscout v2** endpoints under `${NEXT_PUBLIC_API_URL}/api/v2/*`.
- Setting `NEXT_PUBLIC_API_URL=https://explorer.d-bis.org/api` will incorrectly produce requests like `/api/api/v2/*`.
- Token aggregation remains under `/token-aggregation/api/v1/*` and is linked separately by the frontend.

### Local frontend validation

From `frontend/`, the current local validation flow is:

```bash
npm run build:check
BASE_URL=http://127.0.0.1:3000 npm run smoke:routes
```

- `build:check` runs lint, type-check, and a production build.
- `smoke:routes` performs a lightweight route and content sweep against a running frontend instance.
- `npm run dev` now binds to `127.0.0.1` by default, but still honors `HOST` and `PORT` overrides.

---

## CSP blocks eval / “script-src blocked”

The ethers.js v5 UMD bundle from the CDN uses `eval`/`new Function()` for ABI decoding, so the site’s CSP must allow **`'unsafe-eval'`** in **script-src**. The repo already includes it in:
- `frontend/public/index.html` (meta tag)
- `deployment/nginx/explorer.conf` (if nginx serves the page)
- `backend/api/middleware/security.go` (if the Go API serves the page)

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/`).
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.

---

## One command: complete all steps

From the **explorer-monorepo** directory:

**On the Proxmox host that has VMID 5000** (e.g. r630-02):

```bash
cd /path/to/explorer-monorepo
bash scripts/complete-explorer-api-access.sh
```

**From your laptop** (if you have SSH to the Proxmox node):

```bash
cd /path/to/explorer-monorepo
# Uses PROXMOX_R630_02 from repo parent .env if set
EXPLORER_VM_HOST=root@192.168.11.12 bash scripts/complete-explorer-api-access.sh
```

The script will: start Blockscout in VMID 5000, deploy the frontend, configure nginx with `location /api/` → port 4000, wait up to 45s for the API, and run the verification script. If Blockscout does not respond in time, check inside VMID 5000: `docker ps` and `docker logs <blockscout-container>` (see [Check Blockscout](#a-ensure-blockscout-is-running-vmid-5000)).

---

## Fix 502 Bad Gateway (one script)

If the explorer shows **502 Bad Gateway** on `/api/`, Blockscout is not responding on port 4000. Run:

**On Proxmox host (where VMID 5000 lives):**
```bash
cd /path/to/explorer-monorepo
bash scripts/fix-502-blockscout.sh
```

**From your machine (SSH to node):**
```bash
cd /path/to/explorer-monorepo
EXPLORER_VM_HOST=root@192.168.11.12 bash scripts/fix-502-blockscout.sh
```

The script will: start PostgreSQL, **restart** Blockscout (so the app inside binds to port 4000 again), print container status and recent logs, wait for the API on port 4000, and report the public API check. If the container is "Up" but API still does not respond, the script restarts the container to recover. If the container keeps exiting, check logs for database/migrations or `command: bin/blockscout start` in docker-compose.

---

## Automated maintenance (cron, permanent)

To prevent 502s and disk-full from recurring, install cron jobs **inside VMID 5000**:

```bash
# From repo root (on Proxmox host or with SSH)
bash scripts/cron/install-explorer-cron.sh
# or: EXPLORER_VM_HOST=root@192.168.11.12 bash scripts/cron/install-explorer-cron.sh
```

- **Every 5 min:** Health check; if API is not 200, restart Blockscout (or start from `/opt/blockscout`); if nginx is down, start it.
- **Daily 03:15:** Safe disk prune (unused images + build cache only) when disk usage ≥ 90%. Does **not** prune containers.

See `scripts/cron/README.md` for details and uninstall.

**If you see "no space left on device"** when restarting, the disk on VMID 5000 is full. Free space first, then rerun the fix script. On the Proxmox host run:
```bash
pct exec 5000 -- bash -c 'df -h; echo "---"; docker system df; docker system prune -f; docker volume prune -f'
# Then rerun: EXPLORER_VM_HOST=root@192.168.11.12 bash scripts/fix-502-blockscout.sh
```
Or SSH into the VM (192.168.11.140) and run `df -h`, clear logs, remove unused Docker images/containers/volumes, then restart the Blockscout container.

---

## 1. What the frontend expects

| Purpose | URL pattern | Backend |
|--------|-------------|--------|
| Chain 138 (Blockscout) | `https://explorer.d-bis.org/api/v2/blocks`, `/api/v2/transactions`, `/api/v2/addresses`, `/api/v2/stats`, etc. | **Blockscout** (Elixir) on port **4000** |
| Stats (optional) | `/api/v2/stats` | Blockscout or Go API |
| Config | `/api/config/token-list`, `/api/config/networks` | Go API (if used) |
| Explorer backend v1 | `/explorer-api/v1/features`, `/explorer-api/v1/auth/*`, `/explorer-api/v1/ai/*` | **Explorer Config API** (Go) on port **8081** |
| Explorer AI metrics | `/explorer-api/v1/ai/metrics` | **Explorer Config API** (Go) on port **8081** |
| Token aggregation | `/token-aggregation/api/v1/routes/*`, `/token-aggregation/api/v1/partner-payloads*` | **token-aggregation** service on port **3001** |

For the **static frontend + Blockscout** setup (VMID 5000), **nginx** must proxy `/api/` to **Blockscout** at `http://127.0.0.1:4000`. A 502 means nginx is up but the upstream (Blockscout) is down or unreachable.

### API ownership normalization

Use these ownership rules consistently:

- `/api/*` is reserved for **Blockscout** compatibility and v2 explorer reads.
- `/explorer-api/v1/*` is reserved for the **Go explorer backend** (auth, features, AI, explorer-owned helpers).
- `/token-aggregation/api/v1/*` is reserved for the **token-aggregation** service.

Avoid routing mixed services behind the same `/api/v1/*` prefix. That pattern caused the earlier conflicts where AI and feature endpoints were accidentally sent to token-aggregation or Blockscout.

### RPC and WebSocket (Chain 138)

The explorer uses **either FQDN or IP and port** for the Chain 138 RPC/WebSocket:

| How you open the explorer | RPC / WebSocket used | Why |
|---------------------------|------------------------|-----|
| **HTTPS** (e.g. `https://explorer.d-bis.org`) | **FQDN**: `https://rpc-http-pub.d-bis.org`, `wss://rpc-ws-pub.d-bis.org` | Avoids mixed-content blocking (HTTPS page cannot call plain `http://` or `ws://` in many browsers). |
| **HTTP** (e.g. `http://192.168.11.140`) | **IP and port**: `http://192.168.11.221:8545`, `ws://192.168.11.221:8546` (VMID 2201) | Same-LAN; no mixed content when the page is already HTTP. |

The explorer always uses **VMID 2201** (besu-rpc-public-1, 192.168.11.221) for Chain 138 RPC/WebSocket — via FQDN when the page is HTTPS and via IP:port when HTTP. So: **RPC/WebSocket are not required to be FQDN**; when the explorer is loaded over HTTPS, the frontend uses the public FQDN so MetaMask and provider calls are not blocked by mixed-content.

---

## 2. Quick verification (from your machine)

```bash
# Replace with your explorer host if different
BASE="https://explorer.d-bis.org"

# Should return JSON (not HTML 502)
curl -sS -o /dev/null -w "%{http_code}" "$BASE/api/v2/stats"
curl -sS -o /dev/null -w "%{http_code}" "$BASE/api/v2/blocks?page=1&page_size=1"
curl -sS -o /dev/null -w "%{http_code}" "$BASE/api/v2/transactions?page=1&page_size=1"
```

- **200** = API is reachable.
- **502** = nginx is replying but the backend (Blockscout on 4000) is not.

---

## 3. Checklist to provide access to the API

### A. Ensure Blockscout is running (VMID 5000)

Blockscout must listen on **port 4000** so nginx can proxy to it.

**From Proxmox host:**

```bash
# Check if Blockscout container/process is running
pct exec 5000 -- bash -c 'ss -tlnp | grep 4000 || netstat -tlnp 2>/dev/null | grep 4000'
# Or if using Docker
pct exec 5000 -- docker ps --format '{{.Names}}\t{{.Ports}}' | grep 4000
```

**From inside VMID 5000:**

```bash
curl -sS -o /dev/null -w "%{http_code}" http://127.0.0.1:4000/api/v2/stats
```

- If this returns **200**, Blockscout is up; the problem is nginx or routing.
- If this fails (connection refused / timeout), **start Blockscout** (Docker or systemd, depending on your setup).

### B. Ensure nginx proxies `/api/` to port 4000

**Config to have** (in `/etc/nginx/sites-available/blockscout` or equivalent):

```nginx
location /api/ {
    proxy_pass http://127.0.0.1:4000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 300s;
    proxy_connect_timeout 75s;
    add_header Access-Control-Allow-Origin *;
    add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
    add_header Access-Control-Allow-Headers "Content-Type";
}
```

- `location /api/` must appear **before** a catch-all `location /` that might serve the frontend.
- Reload nginx after changes: `nginx -t && systemctl reload nginx`.

**Apply a known-good config (from repo):**

```bash
# From repo root
bash scripts/fix-explorer-complete.sh
# or only nginx + API proxy:
bash scripts/fix-nginx-serve-custom-frontend.sh
```

### C. Deploy or refresh the explorer AI backend

Use the dedicated deployment script when you need to:

- rebuild the Go explorer backend
- refresh `/opt/explorer-ai-docs`
- ensure a real `JWT_SECRET`
- install or refresh the explorer database override used for AI indexed context
- optionally install `XAI_API_KEY`
- recommended local secret file: `~/.secure-secrets/explorer-ai.env`
- normalize nginx for `/explorer-api/v1/*`

```bash
cd /path/to/explorer-monorepo
XAI_API_KEY=... bash scripts/deploy-explorer-ai-to-vmid5000.sh

# or keep the key outside the repo and let the deploy script source it:
cat > ~/.secure-secrets/explorer-ai.env <<'EOF'
XAI_BASE_URL=https://api.x.ai/v1
EXPLORER_AI_MODEL=grok-3
XAI_API_KEY=...
EOF
chmod 600 ~/.secure-secrets/explorer-ai.env
bash scripts/deploy-explorer-ai-to-vmid5000.sh
```

If `XAI_API_KEY` is omitted, the AI context endpoint will still work, but chat will remain disabled with a backend `service_unavailable` response. The deploy script will automatically source `~/.secure-secrets/explorer-ai.env` when it exists.

On VMID `5000`, the script also writes a dedicated `database.conf` drop-in for `explorer-config-api` so AI context can query the live Blockscout Postgres container instead of assuming `localhost:5432`.

### D. CORS (browser)

The frontend is same-origin (`https://explorer.d-bis.org`), so `/api/` is same-origin and CORS is not required for same-origin requests. The `add_header Access-Control-Allow-Origin *` above helps if you ever call the API from another origin.

### E. Optional: OPTIONS preflight

If you need CORS preflight (e.g. custom headers from another site), add inside `location /api/`:

```nginx
if ($request_method = OPTIONS) {
    add_header Access-Control-Allow-Origin "*";
    add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
    add_header Access-Control-Allow-Headers "Content-Type";
    add_header Access-Control-Max-Age 1728000;
    add_header Content-Length 0;
    return 204;
}
```

---

## 4. Run the verification script

From the repo (Proxmox host or inside VMID 5000):

```bash
cd /path/to/explorer-monorepo
bash scripts/verify-explorer-api-access.sh [BASE_URL]
```

The script checks:

- Blockscout (or API) responding on port 4000 (when run inside the VM).
- Nginx serving `/api/` and having `location /snap/` (when run inside the VM or with `pct`).
- HTTP 200 on `/api/v2/stats`, `/api/v2/blocks`, `/api/v2/transactions`.
- Explorer frontend at `/` returns 200.
- Chain 138 Snap companion site at `/snap/` returns 200 or 301 and contains expected content when 200.

**Full verification (single place for all checks — API, explorer, Snap):**

```bash
bash scripts/verify-vmid5000-all.sh [BASE_URL]
```

Run this after every deploy or nginx change to confirm explorer and Snap site are reachable and correct.

---

## 5. Two deployment patterns (reference)

| Setup | Frontend | `/api/` upstream | Notes |
|-------|----------|------------------|--------|
| **VMID 5000 static + Blockscout** | `/var/www/html/index.html` | `http://127.0.0.1:4000` (Blockscout) | What this doc and the fix scripts assume. 502 = Blockscout not running or not on 4000. |
| **Go stack** (`deployment/nginx/explorer.conf`) | `explorer_frontend` (port 3000) | `explorer_api` (port 8080) | Go API has `/api/v2/stats` and v1 routes but **not** Blockscout v2 blocks/transactions. For Chain 138 the frontend needs Blockscout; either proxy `/api/v2/*` to Blockscout or use the static + Blockscout setup above. |

---

## 6. Summary

1. **502 on `/api/v2/*`** → nginx is up, backend (Blockscout on 4000) is down or not proxied.
2. **Provide access**: Start Blockscout on 4000, ensure nginx has `location /api/ { proxy_pass http://127.0.0.1:4000; ... }`, then `nginx -t && systemctl reload nginx`.
3. **Verify**: `curl -sS -o /dev/null -w "%{http_code}" https://explorer.d-bis.org/api/v2/stats` returns **200** and `scripts/verify-explorer-api-access.sh` passes.

**Authenticated API (api.explorer.d-bis.org):** Auth/nonce and full Track 2–4 require a database for nonce storage and operator data. Health may report DEGRADED when the DB is unavailable. See repo root `docs/00-meta/REMAINING_TASKS_AND_API_FEATURES.md` (Explorer API) and `explorer-monorepo/docs/DEPLOYMENT_COMPLETE.md`.
