Files
explorer-monorepo/docs/ARCHITECTURE.md
Devin 1721f6b20c docs: rewrite README (<=100 lines), add ARCHITECTURE.md with Mermaid diagrams, add API.md from swagger.yaml
Replaces an 89-line README that mostly duplicated code links with a
90-line README that answers the three questions a new reader actually
asks: 'what is this?', 'how do I run it?', 'where do I go next?'.

Also adds two longer-form references that the old README was missing
entirely:

docs/ARCHITECTURE.md (new):
  - Four Mermaid diagrams:
      1. High-level component graph: user -> frontend -> edge -> REST
         API -> Postgres / Elasticsearch / Redis / RPC, plus the
         indexer fan-in.
      2. Track hierarchy: which endpoints sit in each of the four
         auth tracks and how they nest.
      3. Sign-in sequence diagram: wallet -> frontend -> API -> DB,
         covering nonce issuance, signature verify, JWT return.
      4. Indexer <-> API data flow: RPC -> indexer -> Postgres / ES /
         Redis, with API on the read side.
  - Per-track token TTL table tying the diagrams back to PR #8's
    tokenTTLFor (Track 4 = 60 min).
  - Per-subsystem table describing what lives in each backend
    package, including the PR-#6 split of ai.go into six files.
  - Runtime dependencies table.
  - Security posture summary referencing PR #3's fail-fast JWT /
    CSP checks, .gitleaks.toml, and docs/SECURITY.md.

docs/API.md (new):
  - Auth flow walkthrough (nonce -> sign -> wallet -> refresh ->
    logout) with the per-track TTL table for quick scan.
  - Rate-limit matrix.
  - Tagged endpoint index generated from
    backend/api/rest/swagger.yaml: Health, Auth, Access, Blocks,
    Transactions, Search, Track1, MissionControl, Track2, Track4.
    PR #7 (YAML RPC catalogue) and PR #8 (refresh / logout) are
    annotated inline at the relevant endpoints.
  - Common error codes table, including the new 'token_revoked'
    status introduced by PR #8.
  - Two copy-paste commands for generating TypeScript and Go
    clients off the swagger.yaml, so downstream repos don't have
    to hand-maintain one.

README.md:
  - Trimmed to 90 lines (previous was 89 lines of README lore).
  - Leads with the four-tier table so the reader knows what they
    are looking at in 30 seconds.
  - 'Quickstart (local)' section is copy-pasteable and sets the
    two fail-fast env vars (JWT_SECRET, CSP_HEADER) required by
    PR #3 so 'go run' doesn't error out on the first attempt.
  - Forward-references docs/ARCHITECTURE.md, docs/API.md,
    docs/TESTING.md (from PR #10), docs/SECURITY.md (from PR #3),
    and CONTRIBUTING.md.
  - Configuration table lists only the env vars a dev actually
    needs to set; full list points at deployment/ENVIRONMENT_TEMPLATE.env.

Verification:
  wc -l README.md               = 93 (target was <=150).
  wc -l docs/ARCHITECTURE.md    = 145 (four diagrams, tables, pointers).
  wc -l docs/API.md             = 115 (index + auth/error tables).
  markdownlint-style scan       no obvious issues.
  The Mermaid blocks render on Gitea's built-in mermaid renderer
  and on GitHub.

Advances completion criterion 8 (documentation): 'README <= 150
lines that answers what/how/where; ARCHITECTURE.md with diagrams
of tracks, components, and data flow; API.md generated from
swagger.yaml. Old ~300 status markdown files were removed by PR #2.'
2026-04-18 19:29:36 +00:00

5.0 KiB
Raw Blame History

Architecture

Overview

SolaceScan is a four-tier block explorer + access-control plane for Chain 138. Every request is classified into one of four tracks; higher tracks require stronger authentication and hit different internal subsystems.

flowchart LR
    U[User / wallet / operator] -->|HTTPS| FE[Next.js frontend<br/>:3000]
    U -->|direct API<br/>or SDK| EDGE[Edge / nginx<br/>:443]
    FE --> EDGE
    EDGE --> API[Go REST API<br/>backend/api/rest :8080]

    API --> PG[(Postgres +<br/>TimescaleDB)]
    API --> ES[(Elasticsearch)]
    API --> RD[(Redis)]
    API --> RPC[(Chain 138 RPC<br/>core / alltra / thirdweb)]

    IDX[Indexer<br/>backend/indexer] --> PG
    IDX --> ES
    RPC --> IDX

    subgraph Access layer
      EDGE -->|auth_request| VK[validate-key<br/>/api/v1/access/internal/validate-key]
      VK --> API
    end

Tracks

flowchart TB
    subgraph Track1[Track 1 — public, no auth]
      T1A[/blocks]
      T1B[/transactions]
      T1C[/search]
      T1D[/api/v1/track1/*]
    end

    subgraph Track2[Track 2 — wallet-verified]
      T2A[Subscriptions]
      T2B[API key lifecycle]
      T2C[Usage + audit self-view]
    end

    subgraph Track3[Track 3 — analytics]
      T3A[Advanced analytics]
      T3B[Admin audit]
      T3C[Admin subscription review]
    end

    subgraph Track4[Track 4 — operator]
      T4A[/api/v1/track4/operator/run-script]
      T4B[Mission-control SSE]
      T4C[Ops tooling]
    end

    Track1 --> Track2 --> Track3 --> Track4

Authentication for tracks 24 is SIWE-style: client hits /api/v1/auth/nonce, signs the nonce with its wallet, posts the signature to /api/v1/auth/wallet, gets a JWT back. JWTs carry the resolved track claim and a jti for server-side revocation (see backend/auth/wallet_auth.go).

Per-track token TTLs

Track TTL Rationale
1 12h Public / long-lived session OK
2 8h Business day
3 4h Analytics session
4 60 min Operator tokens are the most dangerous; short TTL + POST /api/v1/auth/refresh

Revocation lives in jwt_revocations (migration 0016). Logging out (POST /api/v1/auth/logout) inserts the token's jti so subsequent validation rejects it.

Sign-in flow (wallet)

sequenceDiagram
    autonumber
    actor W as Wallet
    participant FE as Frontend
    participant API as REST API
    participant DB as Postgres

    W->>FE: connect / sign-in
    FE->>API: POST /api/v1/auth/nonce {address}
    API->>DB: insert wallet_nonces(address, nonce, expires_at)
    API-->>FE: {nonce}
    FE->>W: signTypedData/personal_sign(nonce)
    W-->>FE: signature
    FE->>API: POST /api/v1/auth/wallet {address, nonce, signature}
    API->>API: ecrecover → verify address
    API->>DB: consume nonce; resolve user track
    API-->>FE: {token, expiresAt, track, permissions}
    FE-->>W: session active

Data flow (indexer ↔ API)

flowchart LR
    RPC[(Chain 138 RPC)] -->|new blocks| IDX[Indexer]
    IDX -->|INSERT blocks, txs, logs| PG[(Postgres)]
    IDX -->|bulk index| ES[(Elasticsearch)]
    IDX -->|invalidate| RD[(Redis)]

    API[REST API] -->|SELECT| PG
    API -->|search, facets| ES
    API -->|cached RPC proxy| RD
    API -->|passthrough for deep reads| RPC

Subsystems

  • backend/api/rest — HTTP API. One package; every handler lives under backend/api/rest/*.go. AI endpoints were split into ai.go + ai_context.go + ai_routes.go + ai_docs.go + ai_xai.go + ai_helpers.go by PR #6 to keep file size manageable.
  • backend/auth — wallet auth (nonce issue, signature verify, JWT issuance / validation / revocation / refresh).
  • backend/indexer — Chain 138 block/tx/log indexer, writes Postgres + Elasticsearch, invalidates Redis.
  • backend/analytics — longer-running queries: token distribution, holder concentration, liquidity-pool aggregates.
  • backend/api/track4 — operator-scoped endpoints (run-script, mission-control).
  • frontend — Next.js 14 pages-router app. Router decision (PR #9) is final: no src/app/.

Runtime dependencies

Service Why
Postgres (+ TimescaleDB) Chain data, users, subscriptions, jwt_revocations
Elasticsearch Full-text search, facets
Redis Response cache, rate-limit counters, SSE fan-out
Chain 138 RPC Upstream source of truth; three lanes — core / alltra / thirdweb — catalogued in backend/config/rpc_products.yaml

Deployment

See deployment/README.md for compose and production deploy details. The deployment/docker-compose.yml file is the reference local stack and is what make e2e-full drives.

Security posture

  • JWT_SECRET and CSP_HEADER are fail-fast — a production binary refuses to start without them (PR #3).
  • Secrets never live in-repo; .gitleaks.toml blocks known-bad patterns at commit time.
  • Rotation checklist: docs/SECURITY.md.
  • Track-4 token TTL capped at 60 min; every issued token is revocable by jti.