Fees now flow through settlement. The payout and the deduction are posted as separate ledger transactions rather than netted, so a player's history shows the full win and the charge as itemised lines instead of a quietly smaller win. The admin console shows treasury, liability, revenue, every posting, every round, and risk flags. Auth is a constant-time token compare and the surface is not mounted at all unless ARCADE_ADMIN_TOKEN is set, so a default deployment has no admin endpoint to attack. The token lives in browser memory only. It is read-only over game outcomes by design: seeds show only after settlement and nothing can alter a crash point. A control that could would make the fairness proof a lie. The console immediately found a real bug: 343 unresolved rounds, because the reconciler only considered rounds with bets and abandoned empty ones accumulated forever, burying the signal. Now cleared automatically. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
194 lines
6.4 KiB
HTML
194 lines
6.4 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<meta name="robots" content="noindex, nofollow">
|
|
<title>QA :: OPERATIONS</title>
|
|
<link rel="stylesheet" href="/style.css">
|
|
<link rel="stylesheet" href="/admin.css">
|
|
</head>
|
|
<body class="admin">
|
|
|
|
<svg class="filigree" aria-hidden="true">
|
|
<defs>
|
|
<pattern id="orn" width="60" height="52" patternUnits="userSpaceOnUse">
|
|
<g fill="none" stroke="currentColor" stroke-width="0.7">
|
|
<path d="M15 0 L45 0 L60 26 L45 52 L15 52 L0 26 Z"/>
|
|
<path d="M30 26 L60 26 M30 26 L15 0 M30 26 L15 52"/>
|
|
<circle cx="30" cy="26" r="1.6"/>
|
|
</g>
|
|
</pattern>
|
|
</defs>
|
|
<rect width="100%" height="100%" fill="url(#orn)"/>
|
|
</svg>
|
|
|
|
<!-- Gate. The token is held in memory only; it is never written to storage,
|
|
so closing the tab ends the session. -->
|
|
<section class="panel center" id="gate">
|
|
<h1>OPERATIONS</h1>
|
|
<p class="muted small">Restricted. Access is logged.</p>
|
|
<input id="token" type="password" placeholder="operator token" autocomplete="off">
|
|
<button class="primary" id="unlock">Authenticate</button>
|
|
<p class="fineprint" id="gate-msg"></p>
|
|
</section>
|
|
|
|
<div id="console" hidden>
|
|
|
|
<header class="topbar">
|
|
<div class="brand">QUANTUM<span>OPS</span></div>
|
|
<div class="livedot" id="livedot" title="auto-refreshing"></div>
|
|
<div class="balance">
|
|
<span class="label">house pot</span>
|
|
<span class="value" id="hdr-pot">—</span>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Headline position -->
|
|
<section class="wrap">
|
|
<div class="tiles ops">
|
|
<div class="tile">
|
|
<span class="tile-label">house pot</span>
|
|
<span class="tile-value" id="k-pot">—</span>
|
|
<span class="tile-sub">operator funds</span>
|
|
</div>
|
|
<div class="tile">
|
|
<span class="tile-label">owed to players</span>
|
|
<span class="tile-value" id="k-owed">—</span>
|
|
<span class="tile-sub">liability</span>
|
|
</div>
|
|
<div class="tile">
|
|
<span class="tile-label">fees collected</span>
|
|
<span class="tile-value up" id="k-fees">—</span>
|
|
<span class="tile-sub" id="k-fees-24h">—</span>
|
|
</div>
|
|
<div class="tile">
|
|
<span class="tile-label">margin 24h</span>
|
|
<span class="tile-value" id="k-margin">—</span>
|
|
<span class="tile-sub" id="k-volume">—</span>
|
|
</div>
|
|
<div class="tile">
|
|
<span class="tile-label">players</span>
|
|
<span class="tile-value" id="k-players">—</span>
|
|
<span class="tile-sub" id="k-active">—</span>
|
|
</div>
|
|
<div class="tile" id="tile-books">
|
|
<span class="tile-label">books</span>
|
|
<span class="tile-value" id="k-books">—</span>
|
|
<span class="tile-sub" id="k-conservation">—</span>
|
|
</div>
|
|
</div>
|
|
|
|
<nav class="opsnav" id="opsnav">
|
|
<button class="on" data-panel="dash">Dashboard</button>
|
|
<button data-panel="players">Players</button>
|
|
<button data-panel="ledger">Ledger</button>
|
|
<button data-panel="rounds">Rounds</button>
|
|
<button data-panel="risk">Risk</button>
|
|
</nav>
|
|
|
|
<!-- DASHBOARD -->
|
|
<section class="opspanel" id="panel-dash">
|
|
<div class="grid2">
|
|
<div class="card">
|
|
<h2>Revenue, 30 days</h2>
|
|
<div class="chart tall" id="chart-revenue"></div>
|
|
</div>
|
|
<div class="card">
|
|
<h2>Stakes vs payouts</h2>
|
|
<div class="chart tall" id="chart-flow"></div>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<h2>Fee schedule in force</h2>
|
|
<div id="fee-schedule" class="kvgrid"></div>
|
|
<p class="muted small">
|
|
Rendered from the same values the server charges. If this table is
|
|
wrong, the code is wrong — it is not a separate document.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- PLAYERS -->
|
|
<section class="opspanel" id="panel-players" hidden>
|
|
<div class="card">
|
|
<h2>Accounts</h2>
|
|
<input id="player-filter" placeholder="filter by name or key" autocomplete="off">
|
|
<div class="tablewrap">
|
|
<table class="data" id="tbl-players">
|
|
<thead><tr>
|
|
<th>id</th><th>name</th><th>balance</th><th>bets</th>
|
|
<th>wagered</th><th>won</th><th>net</th><th>last seen</th>
|
|
</tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- LEDGER -->
|
|
<section class="opspanel" id="panel-ledger" hidden>
|
|
<div class="card">
|
|
<h2>Every posting</h2>
|
|
<p class="muted small">
|
|
Append-only. Rows are never modified or deleted; corrections appear
|
|
as compensating entries.
|
|
</p>
|
|
<div class="tablewrap">
|
|
<table class="data" id="tbl-ledger">
|
|
<thead><tr>
|
|
<th>id</th><th>kind</th><th>round</th><th>account</th>
|
|
<th>amount</th><th>before</th><th>after</th><th>when</th>
|
|
</tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- ROUNDS -->
|
|
<section class="opspanel" id="panel-rounds" hidden>
|
|
<div class="card">
|
|
<h2>Recent rounds</h2>
|
|
<p class="muted small">
|
|
Seeds appear only after settlement. There is no control here that
|
|
reveals a sealed seed or alters an outcome — that is what makes the
|
|
fairness proof worth anything.
|
|
</p>
|
|
<div class="tablewrap">
|
|
<table class="data" id="tbl-rounds">
|
|
<thead><tr>
|
|
<th>id</th><th>game</th><th>crash</th><th>players</th>
|
|
<th>staked</th><th>paid</th><th>fees</th><th>house</th><th>seed</th>
|
|
</tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- RISK -->
|
|
<section class="opspanel" id="panel-risk" hidden>
|
|
<div class="grid2">
|
|
<div class="card">
|
|
<h2>Attention</h2>
|
|
<div id="risk-flags" class="flags"></div>
|
|
</div>
|
|
<div class="card">
|
|
<h2>Largest net winners</h2>
|
|
<div class="tablewrap">
|
|
<table class="data" id="tbl-winners">
|
|
<thead><tr><th>account</th><th>name</th><th>net</th><th>bets</th></tr></thead>
|
|
<tbody></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</section>
|
|
</div>
|
|
|
|
<script type="module" src="/admin.js"></script>
|
|
</body>
|
|
</html>
|