soft3/lytics/rs/ingest/static/bbg_dash.html

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>bbg โ€” cyberstates</title>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
        <link
            href="https://fonts.googleapis.com/css2?family=Play:wght@400;700&display=swap"
            rel="stylesheet"
        />
        <style>
            :root {
                --cyber-black: #000000;
                --cyber-green: #00ff41;
                --cyber-cyan: #00e5ff;
                --cyber-yellow: #ffd700;
                --cyber-orange: #ff6600;
                --cyber-dim: #1a1a1a;
                --cyber-muted: #333333;
                --cyber-text: #e0e0e0;
            }
            * {
                margin: 0;
                padding: 0;
                box-sizing: border-box;
            }
            html {
                overflow-y: scroll;
                scrollbar-gutter: stable;
            }
            body {
                font-family: "Play", sans-serif;
                background: var(--cyber-black);
                color: var(--cyber-text);
                min-height: 100vh;
                -webkit-font-smoothing: antialiased;
            }
            .page-frame {
                max-width: 1100px;
                margin: 0 auto;
                padding: 20px 24px 80px;
            }
            @media (max-width: 640px) {
                .page-frame { padding: 12px 10px 72px; }
            }
            .header-row1 {
                display: flex;
                align-items: center;
                justify-content: space-between;
                gap: 16px;
                margin-bottom: 8px;
                flex-wrap: wrap;
            }
            .logo {
                font-size: clamp(22px, 4vw, 32px);
                font-weight: 700;
                line-height: 1.1;
                color: #fff;
                text-decoration: none;
            }
            .logo span.mark { color: var(--cyber-orange); }
            .logo-suffix {
                min-height: 14px;
                line-height: 14px;
                font-size: 11px;
                letter-spacing: 3px;
                text-transform: uppercase;
                color: rgba(255, 102, 0, 0.55);
                margin-top: 4px;
            }
            .nav-row { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
            .nav-btn {
                font-family: "Play", sans-serif;
                font-size: 12px;
                font-weight: 700;
                letter-spacing: 2px;
                color: #000;
                text-decoration: none;
                background: var(--btn-c);
                border: 1px solid var(--btn-c);
                padding: 8px 14px;
                border-radius: 2px;
                transition: all 0.15s;
                white-space: nowrap;
            }
            .nav-btn:hover { filter: brightness(1.1); }
            .nav-states { --btn-c: var(--cyber-green); }
            .nav-tokens { --btn-c: var(--cyber-yellow); }
            .nav-lytics { --btn-c: var(--cyber-cyan); }
            .nav-bbg { --btn-c: var(--cyber-orange); }
            .nav-cybergraph { --btn-c: #ff00ff; }
            .nav-btn.nav-here { background: transparent; color: var(--btn-c); box-shadow: none; }
            .meta-line {
                font-size: 12px;
                letter-spacing: 1px;
                color: #555;
                margin: 8px 0 20px;
                line-height: 1.6;
            }
            .section-label {
                font-size: 11px;
                letter-spacing: 3px;
                text-transform: uppercase;
                color: #555;
                margin: 28px 0 12px;
            }
            .stat-grid {
                display: grid;
                grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
                gap: 12px;
            }
            .stat-card {
                background: #050505;
                border: 1px solid #1a1a1a;
                border-radius: 2px;
                padding: 14px 16px;
                min-width: 0;
            }
            .stat-label {
                font-size: 10px;
                letter-spacing: 2px;
                text-transform: uppercase;
                color: #555;
                margin-bottom: 8px;
            }
            .stat-value {
                font-size: clamp(16px, 3vw, 24px);
                font-weight: 700;
                color: var(--cyber-orange);
                font-variant-numeric: tabular-nums;
                line-height: 1.15;
            }
            .stat-hint { font-size: 11px; color: #444; margin-top: 6px; letter-spacing: 0.5px; }
            .hint { font-size: 12px; color: #3a3a3a; margin: 10px 0 0; letter-spacing: 0.5px; line-height: 1.5; }
            .table-wrap { overflow-x: auto; margin-top: 4px; border: 1px solid #111; border-radius: 2px; }
            .cyber-table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 12.5px; }
            .cyber-table th {
                background: #050505;
                border-bottom: 1px solid var(--cyber-muted);
                padding: 10px 14px;
                text-align: left;
                font-size: 10px;
                font-weight: 700;
                letter-spacing: 1.5px;
                text-transform: uppercase;
                color: var(--cyber-orange);
                white-space: nowrap;
            }
            .cyber-table td {
                padding: 9px 14px;
                border-bottom: 1px solid #0e0e0e;
                white-space: nowrap;
            }
            .cyber-table tr:last-child td { border-bottom: none; }
            .cyber-table td.num { text-align: right; font-variant-numeric: tabular-nums; }
            .cyber-table td.hash { font-family: monospace; color: #888; }
            .cyber-table .empty { color: #333; font-style: italic; padding: 16px; text-align: center; }
            .two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
            @media (max-width: 720px) { .two-col { grid-template-columns: 1fr; } }
            .footer { margin-top: 60px; font-size: 11px; color: #333; text-align: center; letter-spacing: 1px; }
            .footer a { color: #555; }
        </style>
    </head>
    <body>
        <div class="page-frame">
            <div class="header-row1">
                <div>
                    <a class="logo" href="/lytics/bbg"><span class="mark">โ—†</span> BBG</a>
                    <div class="logo-suffix" id="logo-suffix">cyberstates ยท committed state</div>
                </div>
                <div class="nav-row">
                    <a class="nav-btn nav-states" href="/">STATES</a>
                    <a class="nav-btn nav-tokens" href="/tokens">TOKENS</a>
                    <a class="nav-btn nav-lytics" href="/lytics/">LYTICS</a>
                    <a class="nav-btn nav-bbg nav-here" href="/lytics/bbg">BBG</a>
                    <a class="nav-btn nav-cybergraph" href="/lytics/cybergraph">CYBERGRAPH</a>
                </div>
            </div>
            <div class="meta-line" id="meta">loadingโ€ฆ</div>

            <div class="section-label">dimensions</div>
            <div class="stat-grid" id="ov"></div>
            <p class="hint">
                every number here is a real inf datalog query
                (<code>inf_source::BbgSource</code>) over the actually committed
                <code>BbgState</code> โ€” not a shadow copy. an empty dimension is
                the honest answer to "is this used yet", not a bug: lytics casts
                cyberlinks (structural visitorโ†’page edges), and that alone
                populates particles/axons/signals โ€” nothing in the current cast
                path mints a neuron record, or writes a location/coin/card/file.
            </p>

            <div class="section-label">estimated size</div>
            <div class="stat-grid" id="size-total"></div>
            <div class="table-wrap" style="margin-top:12px"><table class="cyber-table" id="size-breakdown"></table></div>
            <p class="hint">
                data payload only โ€” <code>key_bytes + size_of::&lt;Record&gt;()</code>
                per entry, summed. <code>BTreeMap</code>/<code>BTreeSet</code> node
                overhead and unfilled capacity are real and not included, so this
                is a floor, not the true resident size. no dimension here holds
                actual content bytes yet โ€” see the hint below <code>files</code>.
            </p>

            <div class="section-label">particles โ€” top by energy</div>
            <div class="table-wrap"><table class="cyber-table" id="particles"></table></div>
            <p class="hint">
                energy = total staked conviction a particle received across every
                cyberlink addressed to it; weight = conviction on the axon
                (fromโ†’to edge) itself. pi_star/s_yes/s_no/meta_score are focus
                and valence fields lytics's cast path never writes โ€” shown as
                they are, not hidden.
            </p>

            <div class="section-label">axons โ€” most-linked particles</div>
            <div class="two-col">
                <div>
                    <div class="stat-label" style="margin-bottom:8px">top out-degree (from)</div>
                    <div class="table-wrap"><table class="cyber-table" id="axons-out"></table></div>
                </div>
                <div>
                    <div class="stat-label" style="margin-bottom:8px">top in-degree (to)</div>
                    <div class="table-wrap"><table class="cyber-table" id="axons-in"></table></div>
                </div>
            </div>

            <div class="section-label">neurons โ€” minted records</div>
            <div class="table-wrap"><table class="cyber-table" id="neurons"></table></div>
            <p class="hint">
                focus/karma/stake. lytics never mints a <code>NeuronRecord</code> for a
                visitor โ€” <code>BbgState::insert</code> only debits an existing one
                (<code>get_mut</code>, never <code>or_insert</code>) โ€” so this is
                genuinely empty today, not a query bug.
            </p>

            <div class="section-label">signals โ€” recent</div>
            <div class="table-wrap"><table class="cyber-table" id="signals"></table></div>

            <div class="section-label">balances โ€” top</div>
            <div class="table-wrap"><table class="cyber-table" id="balances"></table></div>
            <p class="hint">
                keyed by <code>H(owner โ€– token)</code> โ€” a key does not decode back
                to a readable owner/token pair without the preimage.
            </p>

            <div class="section-label">other committed dimensions</div>
            <div class="two-col">
                <div>
                    <div class="stat-label" style="margin-bottom:8px">locations / coins</div>
                    <div class="table-wrap"><table class="cyber-table" id="loc-coins"></table></div>
                </div>
                <div>
                    <div class="stat-label" style="margin-bottom:8px">cards / files</div>
                    <div class="table-wrap"><table class="cyber-table" id="cards-files"></table></div>
                </div>
            </div>
            <p class="hint">
                <code>files</code> is where content bytes would attach to a
                particle by spec โ€” but `FileRecord` (<code>{available,
                chunk_count}</code>) is never constructed anywhere in cyber
                today: no code path writes a row here, so it stays permanently
                empty, not by choice but because the write side was never
                built. the real byte-storage primitives already exist,
                sharing bbg's own hemera hash โ€” <code>radio/cyber-bao</code>
                (Poseidon2-BAO verified streaming, 4096-byte chunks) and
                <code>radio/iroh-blobs</code> (a working content-addressed
                blob store) โ€” just not wired to this dimension yet.
            </p>

            <div class="footer">
                ๐Ÿด a <a href="https://cyberia.to">cyberia</a> project ยท
                <a href="https://cyberstates.net">cyberstates.net</a> ยท raw state, not lytics's product
            </div>
        </div>

        <script>
            // this page is served at `<site-root>/bbg` (e.g. /lytics/bbg
            // behind cyberstates' nginx rewrite) โ€” derive that root from
            // location.pathname so the fetch below hits the same backend
            // this page came from, not the outer site's own /api/.
            const root = location.pathname.split("/").slice(0, -1).join("/") + "/";
            const get = (name, extra = "") =>
                fetch(`${root}api/bbg/report/${name}${extra}`).then((r) => r.json());
            const short = (h) => (h && h.length > 14 ? h.slice(0, 8) + "โ€ฆ" + h.slice(-4) : h || "");

            function table(el, headers, data, cols) {
                if (!data || !data.length) {
                    el.innerHTML = `<tbody><tr><td class="empty" colspan="${headers.length}">โ€” 0 records</td></tr></tbody>`;
                    return;
                }
                el.innerHTML =
                    `<thead><tr>${headers.map((h, i) => `${h}</th>`).join("")}</tr></thead><tbody>` +
                    data
                        .map(
                            (r) =>
                                `<tr>${cols.map(([c, cls]) => `{cls || ""}">${r[c] ?? ""}</td>`).join("")}</tr>`,
                        )
                        .join("") +
                    `</tbody>`;
            }

            function bytes(n) {
                if (n == null) return "โ€”";
                if (n < 1024) return `${n} B`;
                if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
                if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(2)} MB`;
                return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
            }

            function cards(items) {
                return items
                    .map(
                        ([label, value, hint]) =>
                            `<div class="stat-card">
                                <div class="stat-label">${label}</div>
                                <div class="stat-value">${value ?? "โ€”"}</div>
                                <div class="stat-hint">${hint || ""}</div>
                            </div>`,
                    )
                    .join("");
            }

            async function refresh() {
                const o = await get("overview");
                document.getElementById("meta").textContent =
                    `height ${o.height} ยท root ${short(o.root)}`;
                document.getElementById("ov").innerHTML = cards([
                    ["particles", o.particles, "content-addressed nodes"],
                    ["axons", o.axons, "cyberlink edges"],
                    ["neurons", o.neurons, "minted records"],
                    ["signals", o.signals, "committed headers"],
                    ["balances", o.balances, "nonzero H(ownerโ€–token)"],
                    ["locations", o.locations, ""],
                    ["coins", o.coins, ""],
                    ["cards", o.cards, ""],
                    ["files", o.files, ""],
                    ["commitments", o.commitments, "privacy dim"],
                    ["nullifiers", o.nullifiers, "privacy dim"],
                    ["intents", o.intents, "uncommitted"],
                ]);

                const sz = await get("size");
                document.getElementById("size-total").innerHTML = cards([
                    ["estimated total", bytes(sz.total), "data payload โ€” see hint"],
                ]);
                table(
                    document.getElementById("size-breakdown"),
                    ["DIMENSION", "BYTES"],
                    [
                        { d: "particles", b: bytes(sz.particles) },
                        { d: "axons_out", b: bytes(sz.axons_out) },
                        { d: "axons_in", b: bytes(sz.axons_in) },
                        { d: "neurons", b: bytes(sz.neurons) },
                        { d: "signals", b: bytes(sz.signals) },
                        { d: "balances", b: bytes(sz.balances) },
                        { d: "commitments", b: bytes(sz.commitments) },
                        { d: "nullifiers", b: bytes(sz.nullifiers) },
                        { d: "intents", b: bytes(sz.intents) },
                        { d: "axon_edges", b: bytes(sz.axon_edges) },
                        { d: "locations / coins / cards / files", b: bytes(sz.locations + sz.coins + sz.cards + sz.files) },
                    ],
                    [["d", ""], ["b", "num"]],
                );

                const particles = await get("particles", "?limit=20");
                table(
                    document.getElementById("particles"),
                    ["PARTICLE", "ENERGY", "WEIGHT", "PI*", "S_YES", "S_NO", "META"],
                    particles.map((p) => ({ ...p, particle: short(p.particle) })),
                    [["particle", "hash"], ["energy", "num"], ["weight", "num"], ["pi_star", "num"], ["s_yes", "num"], ["s_no", "num"], ["meta_score", "num"]],
                );

                const ax = await get("axons", "?limit=15");
                table(
                    document.getElementById("axons-out"),
                    ["PARTICLE", "DEGREE"],
                    (ax.out_degree || []).map((r) => ({ ...r, particle: short(r.particle) })),
                    [["particle", "hash"], ["degree", "num"]],
                );
                table(
                    document.getElementById("axons-in"),
                    ["PARTICLE", "DEGREE"],
                    (ax.in_degree || []).map((r) => ({ ...r, particle: short(r.particle) })),
                    [["particle", "hash"], ["degree", "num"]],
                );

                const neurons = await get("neurons", "?limit=20");
                table(
                    document.getElementById("neurons"),
                    ["NEURON", "FOCUS", "KARMA", "STAKE"],
                    neurons.map((n) => ({ ...n, neuron: short(n.neuron) })),
                    [["neuron", "hash"], ["focus", "num"], ["karma", "num"], ["stake", "num"]],
                );

                const signals = await get("signals", "?limit=25");
                table(
                    document.getElementById("signals"),
                    ["STEP", "NEURON", "LINKS", "HEIGHT", "PROOF"],
                    signals.map((s) => ({ ...s, neuron: short(s.neuron), proof_hash: short(s.proof_hash) })),
                    [["step", "num"], ["neuron", "hash"], ["link_count", "num"], ["block_height", "num"], ["proof_hash", "hash"]],
                );

                const balances = await get("balances", "?limit=20");
                table(
                    document.getElementById("balances"),
                    ["KEY", "AMOUNT"],
                    balances.map((b) => ({ ...b, key: short(b.key) })),
                    [["key", "hash"], ["amount", "num"]],
                );

                const other = await get("other_dims");
                const rows = (arr) => (arr || []).map((r) => r.map((v) => (typeof v === "string" && v.length > 14 ? short(v) : v)).join(" / ")).join("<br>") || "โ€” none";
                document.getElementById("loc-coins").innerHTML =
                    `<tbody><tr><td class="hash">locations</td><td>${rows(other.locations)}coins${rows(other.coins)}</td></tr></tbody>`;
                document.getElementById("cards-files").innerHTML =
                    `<tbody><tr><td class="hash">cards</td><td>${rows(other.cards)}files${rows(other.files)}</td></tr></tbody>`;
            }

            refresh();
            setInterval(refresh, 5000);
        </script>
    </body>
</html>

Graph