chore: remove the Postgres API and syncer - #700
Conversation
…l-core RPC with a sqlite index, no Postgres
… events in sqlite
… formatting - rate-limit key from X-Forwarded-For behind the Railway edge - Cosmos poller keeps its cursor on non-2xx responses - staking event pagination orders on (block_height, _id) - withdraw proof cache no longer stores failed responses - DataCache bounded to 1000 entries with LRU eviction - hot-key decay timer catches sqlite errors - per-minute TPS buckets, GET /api/charts with Cache-Control and nginx proxy_cache, home charts fetch it first - fee chart USD values use convertToUsd formatting and full-hour buckets - biome format on loadtest scripts
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR SummaryHigh Risk Overview Adds a CI gains a scheduled Reviewed by Cursor Bugbot for commit e3be466. Bugbot is set up for automated code reviews on this repo. Configure here. |
- empty environment variables are treated as unset by loadConfig - nginx listens on IPv6 as well - rate limit keys on the TCP peer by default; RATE_LIMIT_KEY=$xff_last opts into the last X-Forwarded-For hop behind a proxy
3725a9f to
2278c73
Compare
2278c73 to
6fc4493
Compare
- assets, contracts and predicates are exempt from index retention; asset lookups fall back to fuel-core assetDetails and the verified-assets registry; registry assets are seeded on boot - suspicious flag derived from subId collisions with the registry - USD values for base-asset fees, inputs, outputs, mints and balances from the ETH price - exact 1-based counts on account, contract and block transaction lists and on minted assets - transactionsByOwner no longer falls back to fuel-core on forward paging past the index; fallback block rendering is parallel and bounded - search returns null and logs on malformed input or lookup failures - dashboard and chart series serve numbers, timestamps in milliseconds, per-bucket max tps and gas - utxos cap 2500 like production - scheduled uptime probe workflow for the explorer
6fc4493 to
520232d
Compare
- Playwright suite under tests/api-lite: home, block, transaction, account, search and staking pages render real data from api-lite in RPC mode against the local node - playwright-api-lite.config.ts boots api-lite on 3004 and the vite preview on 3000 - tests-e2e-api-lite job in pr-tests.yml
520232d to
a06432b
Compare
… field is non-nullable
a06432b to
6f198d6
Compare
6f198d6 to
42f74cc
Compare
42f74cc to
6855514
Compare
- fuel-core fallback pages number from the oldest end instead of reusing the newest ranks - a failed fallback render is backfilled from the remaining items instead of shrinking the page - list counts capped at 1001 and bounded to the contiguous index window - asset registry matches compare lowercase ids
6855514 to
ae60d2a
Compare
ae60d2a to
02622db
Compare
- REST errors distinguish client from upstream: ValidationError maps to 400, a missing event to 404, everything else to 502 with a server-side log - index writes happen before the disk-cache write, so a crash between them re-fetches instead of leaving a pinned block unindexed - backfill clamps indexed_from to the retention floor when retention advanced it mid-fetch (real race found by the new regression test) - backfill gaps surfaced in /health - a 200 cosmos response without a tx_responses array no longer advances the cursor - long-lived viem client, single index.db path constant, decoder policies trimmed to the schema-backed four
02622db to
9240d8d
Compare
- block cache accounting is heap-aware: a measured 2.25x object-vs-JSON multiplier (applied as 2.5x) counts against the budget - 768 MB heap in the image with the memory math documented; the 960 MB compose tier overrides the cache to 48 MB - backfill batch size wired to config instead of a silent 20 - eviction under pinning terminates and logs once per minute - Railway restart policy retries 100 times instead of stranding the service after a crash loop
9240d8d to
f4e5136
Compare
Bugbot is paused — on-demand spend limit reachedBugbot uses usage-based billing for this team and has hit its on-demand spend limit. A team admin can raise the spend limit in the Cursor dashboard, or wait for the next billing cycle to continue. |
…d hop is a Cloudflare address The explorer's public domain is a Cloudflare-proxied CNAME to Railway, so the first X-Forwarded-For hop is a Cloudflare address for every visitor. A geo block over Cloudflare's published ranges, fetched at image build, selects CF-Connecting-IP only for those requests; direct traffic keeps the first hop, which Railway's edge rewrites. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…i-lite - delete the graphql package's server, syncer, cosmos worker, pm2 configs, Postgres DAOs, migrations and tests; keep schemas, generated SDK, domain layer and value objects as a library - delete the helm chart, cluster/minikube scripts, Tiltfile, deployment/Dockerfile, the docker-publish and helm-publish workflows and the run/setup backend scripts - drop the Postgres service and indexer env from docker/docker-compose.yml - graphql package dependencies 36 to 9, devDependencies 31 to 21
f4e5136 to
e3be466
Compare
| runs-on: ubuntu-latest | ||
| timeout-minutes: 5 | ||
| env: | ||
| EXPLORER_URL: ${{ vars.API_LITE_EXPLORER_URL || 'https://explorer-production-f355.up.railway.app' }} | ||
| MAX_LAG_BLOCKS: "120" | ||
| steps: | ||
| - name: Health and index lag | ||
| run: | | ||
| body=$(curl -fsS --max-time 20 "$EXPLORER_URL/api/health") | ||
| echo "$body" | ||
| ok=$(echo "$body" | jq -r '.ok') | ||
| lag=$(echo "$body" | jq -r '.lag') | ||
| test "$ok" = "true" | ||
| test "$lag" -le "$MAX_LAG_BLOCKS" | ||
| - name: Home page | ||
| run: curl -fsS --max-time 20 -o /dev/null "$EXPLORER_URL/" | ||
| - name: Charts endpoint | ||
| run: | | ||
| curl -fsS --max-time 20 "$EXPLORER_URL/api/charts" | jq -e '.statistics and .tps' > /dev/null |
| name: E2E Tests api-lite | ||
| runs-on: warp-ubuntu-latest-x64-4x | ||
| timeout-minutes: 14 | ||
| env: | ||
| TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} | ||
| TURBO_TEAM: ${{ secrets.TURBO_TEAM }} | ||
| TURBO_REMOTE_ONLY: true | ||
| steps: | ||
| - name: Checkout | ||
| uses: actions/checkout@v3 | ||
| with: | ||
| fetch-depth: 0 | ||
|
|
||
| - name: Cache turbo build setup | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: .turbo | ||
| # Distinguish between a normal build and a E2E build, which disables code splitting for faster builds | ||
| key: ${{ runner.os }}-e2e-turbo-${{ hashFiles('pnpm-lock.yaml') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-e2e-turbo- | ||
|
|
||
| - name: Setup Node | ||
| uses: FuelLabs/github-actions/setups/node@master | ||
| with: | ||
| node-version: 20.15.1 | ||
| pnpm-version: 9.10.0 | ||
|
|
||
| - name: Create .env file explorer | ||
| run: cp packages/app-explorer/.env.example packages/app-explorer/.env | ||
|
|
||
| - name: Build production version of explorer | ||
| env: | ||
| NODE_ENV: test | ||
| run: pnpm build:prod | ||
|
|
||
| - name: Setup Docker | ||
| uses: FuelLabs/github-actions/setups/docker@master | ||
| with: | ||
| username: ${{ github.repository_owner }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Start Test Node | ||
| timeout-minutes: 7 | ||
| run: pnpm node:start | ||
|
|
||
| - name: Install Playwright Browsers | ||
| run: pnpm exec playwright install --with-deps chromium | ||
|
|
||
| - name: Run E2E Tests | ||
| run: xvfb-run --auto-servernum -- pnpm test:e2e-api-lite | ||
| env: | ||
| PLAYWRIGHT_JSON_OUTPUT_NAME: playwright-report/api-lite/results.json | ||
| PLAYWRIGHT_FORCE_TTY: true | ||
| PLAYWRIGHT_FORCE_COLOR: true | ||
|
|
||
| - uses: actions/upload-artifact@v4 | ||
| if: always() | ||
| with: | ||
| name: playwright-report-api-lite | ||
| path: packages/e2e-tests/playwright-report/api-lite/ | ||
| retention-days: 30 | ||
|
|
||
| - name: Stop Test Node | ||
| run: pnpm node:stop |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit e3be466. Configure here.
| { sed 's/$/ 1;/' /tmp/ips-v4; sed 's/$/ 1;/' /tmp/ips-v6; } > /etc/nginx/cloudflare-ips.conf; \ | ||
| rm -f /tmp/ips-v4 /tmp/ips-v6 | ||
| COPY docker/vps/nginx.conf.template /etc/nginx/templates/default.conf.template | ||
| ENV API_UPSTREAM=api:3000 NGINX_RESOLVER=127.0.0.11 RATE_LIMIT_KEY='$binary_remote_addr' NGINX_ENVSUBST_FILTER='^(API_UPSTREAM|NGINX_RESOLVER|RATE_LIMIT_KEY)$' |
There was a problem hiding this comment.
Nginx rate-limit key is empty
High Severity
RATE_LIMIT_KEY is assigned '$binary_remote_addr' in a Dockerfile ENV. Docker expands $ variables even inside single quotes, so the value becomes empty at image build. Local compose and the droplet compose never override it, and nginx envsubst then writes an invalid limit_req_zone directive, so the explorer container exits on startup.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit e3be466. Configure here.


Summary
Stacked on #699. The explorer is now served by
packages/api-lite, so the Postgres-backed GraphQL API, the syncer, the cosmos worker and everything that only existed to build, deploy or run them are removed. The@fuel-explorer/graphqlpackage stays as a library: schemas, generated SDK types, the domain layer and value objects that both the frontend and api-lite import. 202 files changed, 41,523 lines deleted.Changes
config.tsreads onlyFUEL_PROVIDER,FUEL_CHAINandDEBUG;.env.exampletrimmed to those,.env.productiondeleteddeployment/Dockerfile, the docker-publish and helm-publish workflows, and the run and setup backend scriptsdocker/docker-compose.ymlanddocker/envs; the bridge profile used by e2e is unchangedrun:backend,setup:backend,dev:graphqlanddev:graphql:alldocs/GETTING_STARTED.mdand the graphql README describe the api-lite setup