Visual management of the Exchange Asset Map — toggle prewarm feeds, enable/disable assets, and sync to running containers without writing SQL
EAM stands for Exchange Asset Map. It is the Aurora table (exchange_asset_map) that is the single source of truth for every crypto asset The Trinity Beast tracks. Each row answers three questions about one asset on one exchange:
Does this asset appear in the subscriber demo dropdown? Controls /asset-categories.
Does this asset get a persistent WebSocket subscription at startup? false = REST fallback only.
What symbol does this exchange use? Coinbase: BTC-USD. OKX: BTC-USDT. Gate.io: BTC_USDT.
Which category group the asset appears under in the demo dropdown. 7 categories: Major Currencies, DeFi, Layer 2, AI & Data, Gaming & NFTs, Meme Coins, Infrastructure.
asset-rebalance routine enforces this — when adding assets to an exchange, it automatically excludes assets already covered by any other exchange.
| Exchange | Enabled | Prewarmed | Total Rows |
|---|---|---|---|
| Coinbase | 26 | 26 | 28 |
| Crypto.com | 24 | 24 | 24 |
| Gate.io | 24 | 24 | 27 |
| Gemini | 25 | 25 | 41 |
| Kraken | 26 | 26 | 33 |
| OKX | 36 | 36 | 43 |
| Bybit (legacy, disabled) | 0 | 24 | 25 |
| Total | 161 | 161 | 221 |
Before the EAM Admin Panel, managing the asset map required three separate manual steps every time an asset needed to be added, toggled, or rebalanced:
| Before (Manual) | After (EAM Admin Panel) |
|---|---|
Write raw SQL against Aurora via /admin/sql — easy to make a typo, no visual confirmation |
Click a toggle switch — change is applied instantly with optimistic UI and toast confirmation |
Run bash scripts/kcc.sh sync-eam from the terminal to push EAM rows into legacy application_parameters |
Click "Run Sync" in the Sync & Deploy panel — same operation, visual per-exchange results |
Run bash scripts/kcc.sh force-deploy to push parameters to running containers |
Click "Force Deploy" (unlocked after sync) — containers pick up changes immediately |
| No visibility into current state — had to query Aurora to see what was enabled/prewarmed | Full table view with per-exchange stats, search, filter by enabled/prewarm/disabled |
*_prewarm_assets parameters in application_parameters are what the running containers use for their in-memory prewarm lists. Sync bridges the two. Force-deploy then pushes the updated parameters into every container's live cache without a restart.
The EAM Admin Panel is a standalone HTML page at /docs/eam-admin.html. It is admin-key authenticated — all API calls include the X-Admin-Key header. No login screen; access is controlled at the WAF and network level.
The default view shows all 221 EAM rows across all exchanges. The table is sortable by asset name or exchange, searchable by asset symbol or exchange ticker, and filterable by state.
| Asset ↑ | Exchange Symbol | Category | Exchange | Enabled | Prewarm | |
|---|---|---|---|---|---|---|
AAVE | aaveusd | DeFi | gemini | On | Live | |
ACE | ACE-USDT | Gaming & NFTs | okx | On | Live | |
ADA | ADA-USD | Major Currencies | coinbase | On | Live | |
1INCH | 1INCHUSDT | DeFi | bybit | Off | Live |
Figure 3.1 — Main asset table showing all exchanges. Sidebar shows per-exchange enabled/prewarmed counts. Stats bar updates as filters are applied.
Clicking any exchange tab filters the table to that exchange only. The sidebar shows two numbers per exchange: enabled/total and the prewarmed count in purple. The active tab is highlighted in amber.
| Asset ↑ | Exchange Symbol | Category | Exchange | Enabled | Prewarm | |
|---|---|---|---|---|---|---|
AAVE | aaveusd | DeFi | gemini | On | Live | |
AMP | ampusd | Infrastructure | gemini | On | Live | |
APE | apeusd | Gaming & NFTs | gemini | Off | REST | |
AVAX | avaxusd | Major Currencies | gemini | Off | REST |
Figure 3.2 — Gemini tab selected. Stats bar updates to show Gemini-only counts. Disabled rows (APE, AVAX) show Off/REST toggles — they exist in EAM but are not active.
Each asset row has two independent toggle switches. Changes are applied immediately via POST /admin/exchange-assets/toggle — no save button required. The UI updates optimistically and reverts if the API call fails.
| Toggle | Color | On State | Off State | Effect |
|---|---|---|---|---|
| Enabled | Green | On — appears in /asset-categories dropdown | Off — hidden from subscribers | Immediate in Aurora. Dropdown refreshes within 5 min (CDN cache) or on force-deploy. |
| Prewarm | Purple | Live — persistent WebSocket subscription | REST — served via REST fallback on demand | Immediate in Aurora. Takes effect on next container restart or force-deploy after sync. |
The + Add Asset button opens a two-step picker. For a new asset, the panel first calls POST /admin/eam/discover to fetch the top 18 uncovered candidates for the current exchange, ranked by 24h volume. You see a grid of cards — each showing the asset name, exchange symbol, price, and 24h volume. Click to select one or more, choose a category, set Enabled and Prewarm, then click Add Selected.
If you need to enter an asset manually (e.g. a newly listed token not yet ranked), click Enter manually to switch to the form view. The edit flow (pencil icon on row hover) always opens the form view directly — Asset and Exchange are locked since they form the primary key.
Figure 3.4 — Add Asset modal (manual entry view). The default view shows the discovery picker with top 18 uncovered candidates. Click "Enter manually" to reach this form. Both Enabled and Prewarm default to checked.
The Sync & Deploy button (header or sidebar) reveals the sync panel. This is a two-step operation: Run Sync writes the current EAM state to application_parameters, then Force Deploy (unlocked after sync) pushes those parameters to all running containers immediately.
Writes all prewarm=true, enabled=true rows from EAM into application_parameters, then triggers a force-deploy so running containers pick up the changes immediately.
Figure 3.5 — Sync panel after a successful sync + force-deploy. Each exchange parameter is confirmed individually. Force Deploy is unlocked only after a successful sync.
Click Audit in the header or Prewarm Audit in the sidebar to open the audit panel. Click Run Audit to start the 4-layer check. The audit runs asynchronously on the server (~12–15 seconds) — the panel polls every 3 seconds and renders results automatically when ready.
| Layer | What It Checks | How |
|---|---|---|
| Layer 1 | Every prewarm asset's exchange_symbol returns a live price from that exchange's public API | Bulk ticker fetch (Gemini, OKX, Gate.io, Crypto.com) + per-asset calls (Coinbase, Kraken) |
| Layer 2 | Each exchange's *_prewarm_assets parameter matches EAM | Aurora SQL diff — EAM vs application_parameters |
| Layer 3 | Master prewarm_assets_list matches the combined EAM prewarm set | Aurora SQL diff |
| Layer 4 | 10 random prewarm assets return 200 from the live LPO /price endpoint | End-to-end smoke test via demo API key |
enabled=false and prewarm=false immediately, then re-runs the audit automatically. Follow up with Sync & Deploy to push the change to running containers.
The last audit result is cached in Valkey (eam:audit:latest, 24h TTL). If you open the panel and a recent result exists, it renders immediately without re-running the audit.
When you click + Add Asset with an exchange tab selected, the panel calls POST /admin/eam/discover and shows a spinner while the server fetches the live catalog, filters out everything already in our collection across all exchanges, and ranks the remainder by 24h volume. Results appear as a card grid — click to select, click again to deselect. Multi-select is supported.
| Card Field | Source |
|---|---|
Asset name (e.g. AI) | Canonical uppercase base asset |
Exchange symbol (e.g. AI-USDT) | Exact format for that exchange — used as-is when saving to EAM |
| Price | Live from the exchange's ticker API at discovery time |
| 24h volume | USD-denominated 24h volume — primary sort key |
usdc, usdt, dai, eur, etc.). The exchange catalog filtering uses exact field comparisons (quote == "USDT", state == "live"). Deterministic, precise, no false positives.
Discovery results are cached in Valkey for 10 minutes per exchange (eam:discover:{exchange}). Opening the picker a second time for the same exchange within that window is instant — no re-fetch.
The Replace flow (from the audit panel's dead asset rows) uses the same picker, pre-loaded for the dead asset's exchange. When you confirm the replacement, the dead asset is automatically disabled and the new asset is added in a single operation.
The most common operations and the exact steps to perform them in the panel.
For bulk additions, use the asset-rebalance KCC command which handles discovery, deduplication, and volume ranking automatically. The panel is best for individual asset management and state inspection.
bash scripts/kcc.sh asset-rebalance gemini 5 # preview top 5 candidates
bash scripts/kcc.sh asset-rebalance gemini 5 --apply # commit + sync + deploy
After the rebalance command completes, refresh the EAM Admin Panel to see the new rows.
⌘K (or Ctrl+K) focuses the search box. Esc closes the modal.
All endpoints require the X-Admin-Key header. All responses use the standard UME envelope.
| Method | Path | Purpose |
|---|---|---|
GET | /admin/exchange-assets | List all EAM rows. Optional ?exchange=coinbase filter. Returns id, asset, exchange_name, exchange_symbol, enabled, prewarm, category. |
POST | /admin/exchange-assets/save | Upsert one or more asset rows. Body: {"exchange_name":"gemini","assets":[{"asset":"MON","exchange_symbol":"monusd","enabled":true,"prewarm":true,"category":"Meme Coins"}]} |
POST | /admin/exchange-assets/toggle | Flip enabled and/or prewarm on a single row. Body: {"exchange_name":"okx","asset":"BANANA","prewarm":false}. Only the fields provided are updated. |
DELETE | /admin/exchange-assets | Delete a row. ?exchange=gemini&asset=APE deletes one asset. ?exchange=gemini deletes all assets for that exchange. |
POST | /admin/eam/sync | Sync EAM → application_parameters. Reads all enabled=true, prewarm=true rows, writes per-exchange CSV params and the master prewarm_assets_list. Returns per-key results. |
GET | /admin/force-deploy-params | Push updated application_parameters to all running containers immediately. Used after sync to activate changes without a restart. |
GET | /admin/eam/audit | Start a 4-layer prewarm audit in the background. Returns 202 immediately. The audit runs async (~12–15s) and stores results in Valkey (eam:audit:latest, 24h TTL). If an audit is already running, returns {"status":"already_running"}. |
GET | /admin/eam/audit?poll=1 | Poll for audit results. Returns {"ready":false,"running":true} while in progress, or {"ready":true,"result":{...}} when complete. The panel polls every 3 seconds automatically. |
POST | /admin/eam/discover | Start async discovery of uncovered assets for an exchange, ranked by 24h volume. Body: {"exchange":"okx","limit":18}. Returns 202 immediately. Filters against the full EAM collection across all exchanges. Results cached in Valkey 10 min per exchange (eam:discover:{exchange}). |
GET | /admin/eam/discover?exchange=okx&poll=1 | Poll for discovery results. Returns {"ready":false} while running, or {"ready":true,"result":{"candidates":[...],"uncovered":N,"scored":N}} when complete. Each candidate includes asset, exchange_symbol, price, and volume_24h. |
| Column | Type | Description |
|---|---|---|
id | SERIAL PRIMARY KEY | Auto-increment row ID |
exchange_name | TEXT NOT NULL | Exchange identifier: coinbase, gemini, kraken, gateio, cryptocom, okx |
asset | TEXT NOT NULL | Canonical asset symbol: BTC, ETH, etc. Unique per exchange. |
exchange_symbol | TEXT NOT NULL | The ticker this exchange uses for the asset. Format varies by exchange. |
enabled | BOOLEAN NOT NULL DEFAULT true | Whether the asset appears in /asset-categories (the subscriber demo dropdown). |
prewarm | BOOLEAN NOT NULL DEFAULT false | Whether the asset gets a persistent WebSocket subscription at container startup. |
category | TEXT NOT NULL DEFAULT 'Infrastructure' | Dropdown group. Must match a row in asset_categories. |
(exchange_name, asset) — one row per asset per exchange. The save endpoint uses ON CONFLICT DO UPDATE so upserts are safe to retry.
After a sync, the following keys are written to application_parameters:
| Key | Value |
|---|---|
coinbase_prewarm_assets | Comma-separated list of prewarmed assets on Coinbase |
gemini_prewarm_assets | Comma-separated list of prewarmed assets on Gemini |
kraken_prewarm_assets | …and so on for each exchange |
prewarm_assets_list | Master combined list — all unique prewarmed assets across all exchanges |
The EAM Admin Panel is an admin-only tool. It is not linked from any public page.
| Control | Detail |
|---|---|
| Authentication | All API calls include X-Admin-Key header. The key is embedded in the page source — the page itself is the credential. Do not share the URL with non-admins. |
| WAF Rate Limit | /admin/* paths are rate-limited to 100 requests per 5 minutes at the ALB WAF. |
| Lockdown | If the admin key is compromised, run bash scripts/kcc.sh lockdown-admin $(curl -s ifconfig.me) immediately to restrict /admin/* to your IP only, then rotate the key. |
| URL | https://api.cpmp-site.org/docs/eam-admin.html |