docs/plans/miner-ui-5.md (what is built, gate results, owed, the RPC fields the node does not expose yet, what the
site lane takes); docs/plans/miner-ui-5-shots/ (light and dark at 1440 and 390, the saved block card PNG);
docs/plans/miner-ui-5-first-share-runbook.md; docs/community/discord/ladder.md.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
(cherry picked from commit f508274fe2)
5.1 KiB
Discord: the ladder's rung announcements (message shapes, not posted)
7 October 2026, miner-ui-5 (mission item 6, docs/analysis/mission/mission.md 2.6). The shapes the bot would post for
each rung of the miner's ladder, so the words in Discord match the app and the site rung for rung. Nothing here is
posted by this lane; the Discord lane wires them into tools/community/discord-hooks.mjs behind its guard rails
(docs/community/discord-hooks.md: the forbidden-string guard, the limits, idempotency keys, never a mention). Every
number is a chain fact from the public API (/api/live, /api/stats), which is the observer's copy of the node's
getFinalityWeights and getFinalityCheckpoints; a post names nothing the chain does not say.
The rules every rung post obeys
| Rule | How |
|---|---|
| The miner's object, never the project's | a post carries the key id, the block link, the rank and the day; never the network hashrate, the block count or a milestone of the project |
| A chain fact only | the trigger is a field of getFinalityWeights (keys[].blocks, keys[].voter, keys[].participation, voters) or getFinalityCheckpoints (latestLockedIndex), read through /api/live finality.weights |
| Opt-in for anything beyond the key id | the card model and the "(you)" link to the address page appear only when the miner switched "Make my page public" on in the app (settings.profile_public); without it the post is the key id and the block link |
| Short key id | the first 8 hex of the vote key hash, the same id the app, /live and the explorer show; never a payout address, never a machine name |
| One post per key per rung | idempotency key rung:<rung>:<keyid>; a rung is posted once for a key, ever (a key that drops and climbs back is not posted again) |
| Copy law | no em dashes, no two-beat antithesis, no aphorisms; the numbers in the line, nothing decorative |
| Volume | #first-blocks takes rungs 0 and 1 (one line each); #numbers takes the daily ladder summary; rungs 2 to 5 and P are never posted singly (one a minute at scale), they are counted in the summary |
Channel: #first-blocks
Rung 0, first block (trigger: a key's first blue block in the window; keys[].blocks goes 0 to 1 in finality.weights, and the block's miner is that key in /api/live blocks[])
First block: key 6ad0e117
Block 1,284,117 · igneum.network/block/9f3a…c21e
RTX 4070 (the miner chose to show the card)
Without the opt-in the third line is absent. Embed: ember accent, title "First block", one field "Key" = 6ad0e117, one field "Block" = the explorer link; footer the UK time with UTC in brackets (the hooks script's footer rule).
Rung 1, the vote (trigger: keys[].voter goes false to true, that is blocks reaches params.dust)
Your key has a vote: 6ad0e117
100 blocks in the window (the line is 100) · the key now signs every checkpoint
On the devnet the line reads 5 blocks in the window (the line is 5): the number is params.dust, never a constant.
Channel: #numbers, the daily ladder summary (09:00 UK, beside the daily digest)
The ladder, yesterday
First blocks: 14 keys found their first block
Votes: 9 keys crossed the dust line (now 1,213 voters)
Signatures: 1,201 keys signed a locked checkpoint (latest lock 184,220)
Full window: 406 keys at 30 of 30 days
Rank 1 by weight: 6e80f3ef with 2,592 blocks in the window
Signing streak, longest: 41 days unbroken (presence 100%)
Shards: 388 paid to 57 keys
Fields and sources, one per line:
| Line | Field |
|---|---|
| First blocks | keys whose blocks went from 0 to at least 1 between the two daily reads of finality.weights.keys[] |
| Votes | keys whose voter went false to true; voters for the total |
| Signatures | keys with participation above 0 at the day's last read; latest_locked_index for the lock |
| Full window | keys whose blocks span the whole params.weightWindow (owed from the node: keys[].daysMined; until then the line is left out, never estimated) |
| Rank 1 | keys[] sorted by blocks, the top row |
| Signing streak | owed from the node (keys[].streakDays); until then the line is left out |
| Shards | /api/live proving.shards_paid_10m summed over the day's reads, provers from live_proofs.prover |
A line whose field the node does not expose yet is left out of the post rather than estimated (the "no number a company server has to be trusted for" rule, reinvent.md section 6).
Roles (the Discord lane, from the same fields)
| Role | Granted when | Revoked when |
|---|---|---|
| Voter | a message signed with the vote key names a key whose voter is true in getFinalityWeights |
voter false at a daily read (a key under dust, or stripped: strippedUntilDaa above the DAA score) |
| Window | the key's blocks span the full params.weightWindow (keys[].daysMined owed; until then by hand from the ladder summary) |
the key misses a day |
| Prover | a paid shard record names the key (igneum_getProofRecords(block).paid[shard]) |
never (a paid record is a chain fact) |
The signed message is the proof of key ownership; the app does not sign it yet (owed: a "prove my key" button that signs a nonce with the vote key and copies it).