Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
af852ba
chore(indexer): upgrade apibara and switch to accepted finality
loothero Mar 4, 2026
090da3f
add consumables table migration
loothero Mar 4, 2026
8f642d9
register consumables migration in drizzle journal
loothero Mar 4, 2026
02cf96e
chore(indexer): bump drizzle-orm to 0.45.1
loothero Mar 5, 2026
6454a34
chore(indexer): bump pg and @types/pg to latest
loothero Mar 5, 2026
c875346
fix(observability): address metrics review findings
Codex-Agent Mar 5, 2026
3e1df5c
fix(api): reduce hot-path logging with sampled structured logs
loothero Mar 6, 2026
d24118c
perf(api): add thin strategic response cache
loothero Mar 6, 2026
b9443d5
perf(api,indexer): optimize hot queries and add index support
loothero Mar 6, 2026
fb11e74
chore(api): add stress test runner script
loothero Mar 6, 2026
440c8e5
docs(indexer): add stress-test benchmarking guidance
loothero Mar 6, 2026
947e7c8
chore(client): point API host to summit-api-production-ca43
loothero Mar 6, 2026
e0db113
fix(client): guard null owners in leaderboard address handling
loothero Mar 6, 2026
f9898fb
fix(client): prevent ws event crashes on null player addresses
loothero Mar 6, 2026
c9b7dd5
fix(indexer): patch apibara drizzle reorg trigger churn
loothero Mar 7, 2026
85f7189
fix(indexer): include pnpm patches in docker build
loothero Mar 7, 2026
4671556
fix(indexer): mark reorg triggers registered after commit
loothero Mar 7, 2026
bbd27a5
Set Apibara finality to pending
loothero Mar 7, 2026
3a0910e
perf(indexer): avoid eager id extraction in reorg trigger
loothero Mar 7, 2026
4c384ab
Revert "perf(indexer): avoid eager id extraction in reorg trigger"
loothero Mar 7, 2026
c8204ac
perf(indexer): add covering index on rewards_earned(owner, amount)
loothero Mar 7, 2026
091a156
fix(indexer): use IF NOT EXISTS in leaderboard index migration
loothero Mar 7, 2026
3d496f2
fix(indexer): set Railway restart policy to ALWAYS
loothero Mar 7, 2026
c38d4a7
fix(indexer): isolate invalidate from block reorg order key txn
loothero Mar 7, 2026
dd8fe53
chore: add Railway watch patterns for monorepo PR environments
loothero Mar 8, 2026
347e49e
fix(api): keep /beasts/all total accurate on empty pages
loothero Mar 9, 2026
d0f541d
refactor: consolidate address utils, extract API constants, remove ph…
loothero Mar 9, 2026
93f5dc3
fix(client): add missing mock fields in ActionBar and Leaderboard tests
loothero Mar 9, 2026
04806b0
merge: resolve conflicts between feature branch and origin/next
loothero Mar 9, 2026
0b2b148
fix(indexer): include .mjs scripts in ESLint node globals config
loothero Mar 9, 2026
be01615
fix(client): remove unused normalizeAddress import in GameDirector
loothero Mar 9, 2026
5be347f
fix(telemetry): harden metrics intervals, empty-block tracking, and W…
loothero Mar 9, 2026
45e7f1a
fix(client): normalize Leaderboard type and handle null owner addresses
loothero Mar 9, 2026
526ffa1
fix(indexer): make reorg trigger registration idempotent across restarts
loothero Mar 9, 2026
13ce183
fix(api): exclude /beasts/:owner from response cache
loothero Mar 9, 2026
1dc8969
docs(api): fix README to match runtime behavior
loothero Mar 9, 2026
2ff2c97
fix(api): make /beasts/all ordering deterministic with NULLS LAST
loothero Mar 10, 2026
0651feb
fix(indexer): remove SIGINT/SIGTERM metric handlers that block gracef…
loothero Mar 10, 2026
ad7c9cf
docs(indexer): document split-transaction tradeoff in drizzle patch
loothero Mar 10, 2026
0d13099
fix(api): close HTTP server before draining DB pool on shutdown
loothero Mar 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,35 @@ PR CI is path-filtered and runs component-specific checks:
- [Game Docs](https://docs.provable.games/summit)
- [View & Trade Beasts](https://beast-dex.vercel.app/marketplace)
- [Collect Beasts in Loot Survivor](https://lootsurvivor.io)

## Resource Metrics from Railway Logs

API and indexer now emit structured metric lines in logs with the prefix:

`METRIC resource_metric_v1 {...}`

Defaults:

- `METRICS_ENABLED=true` in production by default
- `METRICS_INTERVAL_MS=30000`
- `DB_METRICS_INTERVAL_MS=60000`

To summarize current RAM/CPU/DB pressure from Railway logs:

```bash
cd indexer
pnpm metrics:snapshot -- --minutes 10
```

JSON output (for agent/tool consumption):

```bash
pnpm metrics:snapshot -- --minutes 10 --json
```

Verify duplicated metrics modules are in sync:

```bash
cd indexer
pnpm metrics:check-sync
```
1 change: 1 addition & 0 deletions api/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ node_modules/

# Build output
dist/
.cache/

# Environment files
.env
Expand Down
10 changes: 6 additions & 4 deletions api/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ Read [`../AGENTS.md`](../AGENTS.md) first for shared addresses/mechanics and ind
- subscribe payload: `{"type":"subscribe","channels":["summit","event"]}`

Query/pagination rules agents usually need:
- `/beasts/all`: `limit` default `25`, max `100`; `offset`; filters `prefix`, `suffix`, `beast_id`, `name`, `owner`; `sort` in `summit_held_seconds|level`.
- `/logs`: `limit` default `50`, max `100`; `offset`; `category`, `sub_category` (comma-separated), `player`.
- `/beasts/stats/top`: `limit` default `25`, max `100`; `offset`.
- `/beasts/all`: `limit` default `25`, max `100`; `offset`; filters `prefix`, `suffix`, `beast_id`, `name`, `owner`; `sort` in `summit_held_seconds|level`; `include_total` optional (`false` skips `count(*)`).
- `/logs`: `limit` default `50`, max `100`; `offset`; `category`, `sub_category` (comma-separated), `player`; `include_total` optional (`false` skips `count(*)`).
- `/beasts/stats/top`: `limit` default `25`, max `100`; `offset`; `include_total` optional (`false` skips `count(*)`).
- `/diplomacy`: `prefix` and `suffix` required; returns HTTP `400` if missing.
- Paginated routes return `{ data, pagination: { limit, offset, total, has_more } }`.

Expand All @@ -64,7 +64,7 @@ Behavior details that affect integration:
- lowercase
- 66-char `0x` padded form.
- No auth layer (public read API).
- No cache layer (responses are DB-backed).
- Thin in-memory SWR cache is enabled for high-traffic read endpoints.

## TypeScript and DB Settings
- `tsconfig.json`: `strict: true`.
Expand Down Expand Up @@ -94,3 +94,5 @@ Behavior details that affect integration:
- `DB_POOL_MAX` (default `15`)
- `PORT` (default `3001`)
- `NODE_ENV` (`production` hides debug entries from `/` response)
- `API_CACHE_ENABLED` (optional; defaults to enabled in production)
- `API_CACHE_MAX_ENTRIES` (default `500`)
29 changes: 23 additions & 6 deletions api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ For AI-oriented coding guidance and deeper architecture notes, read `AGENTS.md`
## Environment

- `DATABASE_URL` (required)
- `DATABASE_SSL` (`"true"` or `"false"`; required in production)
- `DATABASE_SSL` (`"true"` or `"false"`; defaults to `"true"` in production with a warning when unset)
- `DB_POOL_MAX` (default `15`)
- `PORT` (default `3001`)
- `NODE_ENV` (`production` hides debug entries from `/` discovery payload)
- `API_CACHE_ENABLED` (optional; defaults to enabled in production)
- `API_CACHE_MAX_ENTRIES` (optional; default `500`)

Production note:
- API startup fails fast when `NODE_ENV=production` and `DATABASE_SSL` is unset.
- When `NODE_ENV=production` and `DATABASE_SSL` is unset, SSL defaults to enabled and a warning is logged.

## Quick Start

Expand Down Expand Up @@ -88,18 +90,23 @@ curl http://localhost:3001/health
### Query Parameters and Response Shapes

`GET /beasts/all`
- params: `limit` (default `25`, max `100`), `offset`, `prefix`, `suffix`, `beast_id`, `name`, `owner`, `sort` (`summit_held_seconds|level`)
- params: `limit` (default `25`, max `100`), `offset`, `prefix`, `suffix`, `beast_id`, `name`, `owner`, `sort` (`summit_held_seconds|level`), `include_total` (`true|false`, default `true`)
- returns: `{ data: Beast[], pagination: { limit, offset, total, has_more } }`

`GET /logs`
- params: `limit` (default `50`, max `100`), `offset`, `category`, `sub_category`, `player`
- params: `limit` (default `50`, max `100`), `offset`, `category`, `sub_category`, `player`, `include_total` (`true|false`, default `true`)
- `category`/`sub_category` accept comma-separated values
- returns: `{ data: LogEntry[], pagination: { limit, offset, total, has_more } }`

`GET /beasts/stats/top`
- params: `limit` (default `25`, max `100`), `offset`
- params: `limit` (default `25`, max `100`), `offset`, `include_total` (`true|false`, default `true`)
- returns: paginated top beasts sorted by summit hold time, bonus XP, death timestamp

`include_total=false` behavior:
- skips `count(*)` query for lower latency
- returns `pagination.total = null`
- computes `has_more` via `limit + 1` fetch strategy
Comment on lines +93 to +108
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Document pagination.total as nullable when include_total=false.

The new behavior returns pagination.total = null, but the response shapes above still read like total is always present as a number. Please spell that out as number | null for the affected endpoints so clients do not over-assume the shape.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/README.md` around lines 93 - 108, Update the API docs so the
pagination.total field is explicitly nullable for the endpoints shown: change
the return shapes for GET /beasts, GET /logs and GET /beasts/stats/top to show
pagination.total as "number | null" (e.g., pagination: { limit, offset, total:
number | null, has_more }). Also add a short note near the existing
"include_total=false" behavior paragraph that clarifies when include_total is
false the server sets pagination.total = null so clients should expect null
instead of a numeric total.


`GET /diplomacy`
- params: `prefix` (required), `suffix` (required)
- returns HTTP `400` if either is missing
Expand Down Expand Up @@ -155,7 +162,17 @@ Realtime pipeline:

- Address inputs are normalized to lowercase 66-char `0x`-padded form.
- API is public read-only (no auth layer).
- No dedicated caching layer is used.
- A thin in-memory SWR cache is applied to high-traffic read endpoints:
- `/beasts/all` (common public list patterns)
- `/logs`
- `/beasts/stats/counts`
- `/beasts/stats/top`
- `/diplomacy`
- `/diplomacy/all`
- `/leaderboard`
- `/quest-rewards/total`
- `/consumables/supply`
- Cached responses include `X-Cache` with `HIT`, `MISS`, `STALE`, or `BYPASS`.
Comment on lines +165 to +175
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

List /consumables/supply with the other documented endpoints.

Right now this route only appears in the cache note. Anyone scanning the endpoint summary or response-shape sections will miss that it exists and has no documented contract.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/README.md` around lines 165 - 176, Add the /consumables/supply endpoint
to the README's endpoint summary and response-shape sections so it isn't only
mentioned in the cache note; update the endpoints list (the same bullet list
that contains /beasts/all, /logs, /leaderboard, etc.) to include
/consumables/supply and add a corresponding response-shape entry describing its
request params, JSON response fields, and any headers (including X-Cache) so the
contract is documented alongside other routes like /beasts/:owner and
/quest-rewards/total.

- Graceful shutdown closes WS subscriptions/listeners on `SIGINT`/`SIGTERM`.

## Deployment Notes
Expand Down
3 changes: 2 additions & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
"lint:ci": "eslint . --max-warnings=0 --report-unused-inline-configs error",
"start": "node dist/index.js",
"test": "vitest run",
"test:coverage": "vitest run --coverage"
"test:coverage": "vitest run --coverage",
"stress:test": "tsx scripts/stress-test.ts"
},
"description": "Summit API server with REST endpoints and WebSocket subscriptions",
"dependencies": {
Expand Down
6 changes: 6 additions & 0 deletions api/railway.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"$schema": "https://railway.com/railway.schema.json",
"build": {
"watchPatterns": ["api/**"]
}
}
Loading
Loading