# 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. ```mermaid flowchart LR U[User / wallet / operator] -->|HTTPS| FE[Next.js frontend
:3000] U -->|direct API
or SDK| EDGE[Edge / nginx
:443] FE --> EDGE EDGE --> API[Go REST API
backend/api/rest :8080] API --> PG[(Postgres +
TimescaleDB)] API --> ES[(Elasticsearch)] API --> RD[(Redis)] API --> RPC[(Chain 138 RPC
core / alltra / thirdweb)] IDX[Indexer
backend/indexer] --> PG IDX --> ES RPC --> IDX subgraph Access layer EDGE -->|auth_request| VK[validate-key
/api/v1/access/internal/validate-key] VK --> API end ``` ## Tracks ```mermaid 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 2–4 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) ```mermaid 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) ```mermaid 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](../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](SECURITY.md). - Track-4 token TTL capped at 60 min; every issued token is revocable by `jti`.