agent-play-scanner-architecture
Agent Play Scanner architecture
Runbook for the Scanner indexer, Redis keys, and write-through hooks.
Separation of concerns
| Layer | Redis prefix | Purpose |
|---|---|---|
| Daily close | econext:{hostId}:market:daily-activity |
Published Scanner series (Today / 7D / 30D) |
| Scanner journal | agent-play:{hostId}:scanner:* |
Global tx index, blocks, wallet + economy caches |
| Analytics | agent-play:{hostId}:analytics:* |
Segment-style events + properties |
Scanner never uses analytics keys for economic truth. Headlines never walk scanner:txs — they HGETALL the daily hash and ZCARD the journal.
SOL and P2P are first-class journal rows. Scanner visual tokens live only in scanner-page.module.css (--sc-* on the Scanner root).
Scanner Redis keys
econext:{hostId}:market:daily-activity
econext:{hostId}:market:daily-activity:rebuild
econext:{hostId}:market:daily-activity:ready
econext:{hostId}:market:apw-cap
econext-p2p:{hostId}:escrow:apu:total
econext-p2p:{hostId}:orders:open
agent-play:{hostId}:scanner:txs
agent-play:{hostId}:scanner:tx:{id}
agent-play:{hostId}:scanner:tx:by-player:{playerId}
agent-play:{hostId}:scanner:blocks
agent-play:{hostId}:scanner:wallets
agent-play:{hostId}:scanner:wallet:{playerId}
agent-play:{hostId}:scanner:migration:state
agent-play:{hostId}:scanner:cache:supply
agent-play:{hostId}:scanner:cache:space:{spaceId}
agent-play:{hostId}:scanner:cache:talk
agent-play:{hostId}:scanner:cache:game:{gameId}
agent-play:{hostId}:scanner:cache:node:{nodeId}
Analytics materialized overview:
agent-play:{hostId}:analytics:cache:overview
agent-play:{hostId}:analytics:cache:events:hour:{yyyy-MM-dd-HH}
Cache bump diagram
flowchart LR
writePath["PlayWorldReader / EconextStore / indexPurchaseRecord"]
journal["scanner:txs journal"]
daily["market:daily-activity hash"]
spaceCache["scanner:cache:space / talk / game"]
supply["scanner:cache:supply + escrow total"]
head["GET /api/scanner/head"]
list["GET /api/scanner/txs page"]
writePath --> journal
writePath --> daily
writePath --> spaceCache
writePath --> supply
daily --> head
spaceCache --> head
supply --> head
journal --> list
Write-through hooks
Implemented in redis-session-store.ts via scanner-hooks.ts, plus Econext indexEconextScannerTx:
appendPurchaseRecord→ global tx index + daily increment + space/talk/game hashes- P2P settlement → seller/buyer scanner rows with SOL + fee
- Convert / SOL deposit / SOL payout → journal + daily increment in the same Redis
MULTI - Wallet snapshot →
scanner:cache:supply(circulating APU write-through) addEscrowed→ host-leveleconext-p2p:{host}:escrow:apu:total
Indexer failures are logged and swallowed; user-facing RPCs must not fail because indexing failed.
Backfill
scanner-backfill.ts and Econext rebuildDailyMarketActivity:
- Page
scanner:txswith no 5,000 cap - Scan
econext:{host}:account:*:ledgerfor historicalsol_deposit/sol_payout/apu_convert_out - Parse old P2P
detaillamports whensolLamportsDeltais missing - Rewrite the daily hash (
DELrebuild key,RENAME); do not re-HINCRBYvia existing tx keys - Rebuild space/talk/game/supply hashes
- Set
daily-activity:ready
Triggered lazily on first GET /api/scanner/head or via POST /api/admin/scanner/backfill.
Code map
| Module | Role |
|---|---|
packages/sdk/src/lib/scanner-model.ts |
Zod schemas |
packages/web-ui/src/server/scanner/daily-market-activity.ts |
Daily increment / parse / summarize |
packages/web-ui/src/server/scanner/scanner-indexer.ts |
Index writes + supply write-through |
packages/web-ui/src/server/scanner/scanner-economy.ts |
Space/talk/game reads from hashes |
packages/web-ui/src/server/scanner/scanner-cache.ts |
Cache rebuild from uncapped journal |
packages/web-ui/src/app/scanner/scanner-page.module.css |
Isolated Scanner visual tokens |
packages/web-ui/src/server/analytics/analytics-cache.ts |
Materialized overview cache |
packages/web-ui/src/server/scanner/scanner-http-cache.ts |
ETag + Cache-Control helpers |
packages/web-ui/src/server/scanner/scanner-node-profile.ts |
Public node profile payload |
Contributor checklist
- New economic side-effect → append
PurchaseRecordor callsafeIndexPurchaseRecord - New behavioral signal →
safeTrackAnalyticsEventwith catalog event name - Keep
scanner:*andanalytics:*keys separate - Add/backfill tests for idempotent indexing
- Do not restyle Scanner by editing
agent-play.module.cssorglobals.css