Files
explorer-monorepo/docs/DEPLOYMENT.md
Devin ad69385beb fix(security): fail-fast on missing JWT_SECRET, harden CSP, strip hardcoded passwords
backend/api/rest/server.go:
- NewServer() now delegates to loadJWTSecret(), which:
    - Rejects JWT_SECRET < 32 bytes (log.Fatal).
    - Requires JWT_SECRET when APP_ENV=production or GO_ENV=production.
    - Generates a 32-byte crypto/rand ephemeral secret in dev only.
    - Treats rand.Read failure as fatal (removes the prior time-based
      fallback that was deterministic and forgeable).
- Default Content-Security-Policy rewritten:
    - Drops 'unsafe-inline' and 'unsafe-eval'.
    - Drops private CIDRs (192.168.11.221:854[5|6]).
    - Adds frame-ancestors 'none', base-uri 'self', form-action 'self'.
    - CSP_HEADER is required in production; fatal if unset there.

backend/api/rest/server_security_test.go (new):
- Covers the three loadJWTSecret() paths (valid, whitespace-trimmed,
  ephemeral in dev).
- Covers isProductionEnv() across APP_ENV / GO_ENV combinations.
- Asserts defaultDevCSP contains no unsafe directives or private CIDRs
  and includes the frame-ancestors / base-uri / form-action directives.

scripts/*.sh:
- Removed '***REDACTED-LEGACY-PW***' default value from SSH_PASSWORD / NEW_PASSWORD in
  7 helper scripts. Each script now fails with exit 2 and points to
  docs/SECURITY.md if the password isn't supplied via env or argv.

EXECUTE_DEPLOYMENT.sh, EXECUTE_NOW.sh:
- Replaced hardcoded DB_PASSWORD='***REDACTED-LEGACY-PW***' with a ':?' guard that
  aborts with a clear error if DB_PASSWORD (and, for EXECUTE_DEPLOYMENT,
  RPC_URL) is not exported. Other env vars keep sensible non-secret
  defaults via ${VAR:-default}.

README.md:
- Removed the hardcoded Database Password / RPC URL lines. Replaced with
  an env-variable reference table pointing at docs/SECURITY.md and
  docs/DATABASE_CONNECTION_GUIDE.md.

docs/DEPLOYMENT.md:
- Replaced 'PASSWORD: SSH password (default: ***REDACTED-LEGACY-PW***)' with a
  required-no-default contract and a link to docs/SECURITY.md.

docs/SECURITY.md (new):
- Full secret inventory keyed to the env variable name and the file that
  consumes it.
- Five-step rotation checklist covering the Postgres role, the Proxmox
  VM SSH password, JWT_SECRET, vendor API keys, and a gitleaks-based
  history audit.
- Explicit note that merging secret-scrub PRs does NOT invalidate
  already-leaked credentials; rotation is the operator's responsibility.

Verification:
- go build ./... + go vet ./... pass clean.
- Targeted tests (LoadJWTSecret*, IsProduction*, DefaultDevCSP*) pass.

Advances completion criterion 2 (Secrets & config hardened). Residual
leakage from START_HERE.md / LETSENCRYPT_CONFIGURATION_GUIDE.md is
handled by PR #2 (doc consolidation), which deletes those files.
2026-04-18 19:02:27 +00:00

4.3 KiB

Deployment Guide

Production Deployment

Prerequisites

  • SSH access to production server (192.168.11.140)
  • Password for root user
  • sshpass installed (or use SSH keys)

Quick Deploy

For the current frontend, use the Next standalone deploy path:

# From explorer-monorepo root
./scripts/deploy-next-frontend-to-vmid5000.sh

This builds frontend/, uploads the standalone bundle, installs the solacescanscout-frontend.service unit, and starts the frontend on 127.0.0.1:3000 inside VMID 5000.

Nginx should keep the existing explorer API routes and proxy / plus /_next/ to the frontend service. Use nginx-next-frontend-proxy.conf inside the explorer server block after /api, /api/config/*, /explorer-api/*, /token-aggregation/api/v1/*, /snap/, and /health.

Legacy Static Deploy

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

# Canonical Next deployment:
./scripts/deploy-next-frontend-to-vmid5000.sh

# Compatibility assets only:
# frontend/public/index.html
# frontend/public/explorer-spa.js

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 (required; no default)
  • DOMAIN: Domain name (required; no default)
  • SSH_PASSWORD: SSH password (required; no default; previous hardcoded default has been removed — see SECURITY.md)

These applied to the deprecated static deploy script and are no longer the recommended operator interface.

Mission-control and Track 4 runtime wiring

If you are deploying the Go explorer API with the mission-control additions enabled, set these backend env vars as well:

  • RPC_URL - Chain 138 RPC for Track 1 and mission-control status/SSE data
  • TOKEN_AGGREGATION_BASE_URL - used by GET /api/v1/mission-control/liquidity/token/{address}/pools
  • BLOCKSCOUT_INTERNAL_URL - used by GET /api/v1/mission-control/bridge/trace
  • EXPLORER_PUBLIC_BASE - public base URL returned in bridge trace links
  • CCIP_RELAY_HEALTH_URL - optional relay probe URL, for example http://192.168.11.11:9860/healthz
  • CCIP_RELAY_HEALTH_URLS - optional comma-separated named relay probes, for example mainnet=http://192.168.11.11:9860/healthz,bsc=http://192.168.11.11:9861/healthz,avax=http://192.168.11.11:9862/healthz
  • MISSION_CONTROL_CCIP_JSON - optional JSON-file fallback for relay health snapshots
  • OPERATOR_SCRIPTS_ROOT - root directory for Track 4 script execution
  • OPERATOR_SCRIPT_ALLOWLIST - comma-separated allowlist for POST /api/v1/track4/operator/run-script
  • OPERATOR_SCRIPT_TIMEOUT_SEC - optional per-script timeout in seconds

For nginx, include nginx-mission-control-sse.conf inside the same server block that proxies /explorer-api/, and update the proxy_pass target if your Go API is not listening on 127.0.0.1:8080.

Quick verification

curl -N https://explorer.d-bis.org/explorer-api/v1/mission-control/stream
curl "https://explorer.d-bis.org/explorer-api/v1/mission-control/bridge/trace?tx=0x..."
curl "https://explorer.d-bis.org/explorer-api/v1/mission-control/liquidity/token/0x93E66202A11B1772E55407B32B44e5Cd8eda7f22/pools"
# Optional relay probe from the explorer host:
curl http://192.168.11.11:9860/healthz

Rollback

If deployment fails, rollback to previous version:

ssh root@192.168.11.140
ls -1dt /opt/solacescanscout/frontend/releases/* | head

For the Next standalone path, restart the previous release by repointing /opt/solacescanscout/frontend/current to the prior release and restarting solacescanscout-frontend.

Testing

After deployment, test the explorer:

./scripts/test.sh

Or manually:

curl -k -I https://explorer.d-bis.org/