igneum/docs/community/discord-hooks.md
igneum-labs 13fafe3338 Home page restored and polished for the miner: the step scene with the shards, the glow and the final line brought up to date; live network strip; measured card picker; Discord; no calendar month
The owner, 6 October 2026: "Im not sure about the site can we revert it but polish it? minimal, simple but everything needed
for the dopamine and emotional triggers of the gpu miner", then "cant we have a dag animation like we had before? with the
shards making up the block, then the final line and the other bits that looked really cool but brought upto date?"

Restored (a new commit, no history rewrite): site/index.html as it stood at 14da2b8, the step-view page with its hero, facts,
scene, three things to check, downloads, build, wallet, journey, economics and the ledger band; nothing from tonight's other
work is undone (the litepaper, /live, the roadmap and benchmark wording, ledger X31 to X33, the scroll-zoom fix). Where the
page named a month it now carries tonight's sentences: the proofs card reads "Live rows arrive with the public testnet. The
public testnet is weeks away: three seed nodes and the public RPC are up, and it opens when the go checklist closes."; the
journey lead reads "Six phases, four public gates ... No calendar dates: each phase closes at its gate." and its inlined
phases are the gate-worded ones. No "August 2027", no month anywhere on the page.

The scene (site/live-steps.js): the original bits, every one driven by the live feed. A real block arrives from the right
with a glow and its chain number under it; its outline is grey while pending and ember once included; as its shards are
verified or paid, particles fly in from the edges and its quarter cells fill molten; when every shard is proven the block
turns ember and the caption says how many provers were paid; a locked checkpoint takes a ring and a ripple and the dashed
"final" line sweeps in from the right to it, drawn only while finality is active (the legend no longer says "final to its
left"; the wallet card's state word "final, checkpoint 5" is the wallet's own vocabulary and stays). A proof or a lock that
lands on a block already off screen re-enters from the right as its own event, so every step is seen when it happens. The
counters gain blocks per second. The caption follows the newest block that changed step.

The miner's triggers, each one element with live or measured numbers, none invented: a network strip under the facts
(hash rate, blocks in the last ten minutes, the latest block's age ticking every second with its chain number and word,
shards proven and paid with the last ten minutes), each value flashing molten when it changes; a card picker from the bench
table with the time a card alone takes to find a block at the live hash rate (RTX 5090 127.7 MH/s at 227 W and RTX 4070
28.8 MH/s at 76 W from the 6 October Ember Tune on the three-card Windows rig, RX 9070 XT 17.8 MH/s from the 5 October eGPU
entry, Apple M5 Max 26.7 MH/s from the first live swap; the RTX 4090 and RX 7900 XTX shown as not yet measured with the
bench table linked) with the pool note; the fairness line (graphics cards only, a new program every hour, your card proves
the blocks, 80% to the miner and 20% to the provers, nothing to anyone else); the download buttons with sizes; a Discord join
line (the standing invite, recorded in docs/community/discord-hooks.md so nobody asks again); the testnet sentence.

docs/fud-ledger.md: G4, C2 and X8 each gain a status line saying the restored home page carries their sentence again; the
ledger-text check guards them on the home page again (55 sentences, 0 missing).

Measured headless over 40 s against the live feed: 19 captioned transitions, the strip reading 2.3 GH/s, 562 blocks in ten
minutes, 8 s ago, 2,970 shards paid, the picker's times 18 s (5090), 79 s (4070), 2 min (9070 XT), 86 s (M5 Max) at that
hash rate, 0.92 blocks/s; no console errors at 1440 or 390 in either theme; no horizontal overflow. Captures in
docs/plans/site-ui-3-shots/after-v2/: home-{1440,390}-{dark,light}-fold.jpg, home-{1440,390}-{dark,light}.jpg and
home-steps-1440-dark.webm (ten seconds of the scene). Checks: identity grep 0 hits on served files, link check 934 links
across 16 pages 0 broken, ledger text 55 of 55, contrast 32 pairs 0 under 4.5:1, api tests 5 pass. Not deployed: the owner
sees it first.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 21:37:38 +00:00

86 lines
7.1 KiB
Markdown

# Discord webhooks: the bot's posts to #numbers, #announcements and #incidents
6 October 2026. Three channel webhooks named "Igneum" (the Bot role in the server kit, docs/community/reddit/discord.md)
carry the project's numbers to the Discord server. One script, `tools/community/discord-hooks.mjs` (Node 22, standard
library only), shapes every post as a single embed: ember accent (#F2541B), the site icon as thumbnail, plain words, at most
eight fields, UK time in the footer with UTC in brackets, never a mention, never a reply. Dry run is the default.
## The posts
| Post | Channel | When (UK) | Content |
|---|---|---|---|
| Network pulse | #numbers | 03:00, 09:00, 15:00, 21:00 | chain block and blocks/s over the 6 h window, hash rate and active vote keys ("a card runs several"), difficulty and its 6 h change, last lock (index, share of weight, age, voters), shards paid in the window and IGN paid to provers in the last 5 min, median proof lag and provers, reward and ramp, miner version share when /api/live exposes engines, Devnet 2 in one line (boxes, height, last gate PASS or FAIL) |
| Daily digest | #numbers | 09:00 | a small monospace table, yesterday against now, with the 24 h deltas |
| Weekly numbers | #numbers | Monday 09:00 | the reddit kit's template (docs/community/reddit/posts/02-weekly-numbers-template.md) as six fields with the last-week column, a link to the ledger |
| Release | #announcements | on a publish, by the shipper | version, three download links with full sha256, node commit, consensus digest, up to five "what changed" lines read from the release plan |
| Incident open / resolve | #incidents | by hand, or by the watcher | UTC time, what happened, who is affected per tier, what is being done; then cause, the rule or fix, duration |
Every number comes from the public API (`/api/stats`, `/api/live?window=300`, `/api/supply`; docs/api/public-stats.md). The
Devnet 2 line reads the fleet file and uses its numbers and the gate word only; the seed address and the state text never pass.
## The watcher
`watch` (and every `tick`) opens one incident per condition and resolves it when the condition has been clear for 2 minutes.
One open incident per condition at a time; the state file carries since / clear_since / open.
| Condition | Opens when | Text |
|---|---|---|
| finality_paused | `finality.active` false, or the locked index unchanged, for over 5 min | finality paused; miners and provers paid as usual; nothing final until weight returns |
| proof_lag | `proving.median_proof_lag_s` over 900 s | proving behind; blocks final as usual, proofs late |
| observer_silent | `stale` with `age_s` over 180 s, or the API unreachable for over 3 min | the live numbers are stale; the chain is unaffected |
The watcher's texts are fixed sentences in the script, each a stated rule, and are the only incidents nobody typed.
## Commands
```
node tools/community/discord-hooks.mjs pulse | digest | weekly [--live] [--force]
node tools/community/discord-hooks.mjs release 0.3.14 --windows <url> --windows-sha <hex> --mac <url> --mac-sha <hex> --hive <url> --hive-sha <hex> \
--node-commit 4c6b129d --digest "b18ed271 (thirteen fields, unchanged)" --plan docs/plans/release-0.3.14.md --section "1. Why" [--changed "line"]... [--live]
node tools/community/discord-hooks.mjs incident open --what "..." --affected "..." --doing "..." [--at 2026-10-06T15:44:00Z] [--id inc-...] [--live]
node tools/community/discord-hooks.mjs incident resolve --id inc-... --cause "..." --fix "..." [--at ...] [--live]
node tools/community/discord-hooks.mjs watch [--live] one watcher pass
node tools/community/discord-hooks.mjs tick [--live] one scheduler pass (what the London clock says is due, then watch)
node tools/community/discord-hooks.mjs preview rebuild tools/community/out/preview.html
node tools/community/discord-hooks.mjs check which webhooks are configured (names, never values)
node --test tools/community/discord-hooks.test.mjs
```
Dry run writes `tools/community/out/<key>.json` (git-ignored) and renders `out/preview.html`, a Discord-like dark page of
every payload in the folder. The release subcommand takes the "what changed" lines from `--changed` flags or from the named
section of the release plan: bullet lines as they are, table rows by their first cell, cut at the first semicolon, a long
parenthetical dropped, 160 characters at most; a line that trips the guard is dropped with a note on stderr.
## Guard rails
| Rail | How |
|---|---|
| Forbidden strings | before every post: the founder's name and logins, hosting providers, internal host names, machine ids, internal paths, IP addresses, a standalone 32-hex token (a sha256 passes), any dl.igneum.network path outside /public/, a webhook URL, any mention; the post is refused with the pattern named |
| Limits | title 256, description 4,096, field value 1,024, eight fields, 6,000 in total, content 2,000; refused, never cut silently |
| Idempotency | a post id file (`posts` in the state) keyed `pulse:<date>:<hour>`, `digest:<date>`, `weekly:<date>`, `release:<version>`, `incident:<id>:open|resolve`; a rerun is skipped, `--force` posts again |
| 429 | exponential backoff from 1 s, doubling to 60 s, `retry_after` honoured, six tries, then exit 1 |
| State | written only after a successful live post; a dry run keeps its own state in out/state.json |
| Mentions | `allowed_mentions: {parse: []}` on every payload and a guard that refuses @everyone, @here and user mentions in the text |
## Credentials and deployment
`~/.config/igneum/discord`, mode 600, KEY=VALUE lines: `DISCORD_WEBHOOK_NUMBERS`, `DISCORD_WEBHOOK_ANNOUNCEMENTS`,
`DISCORD_WEBHOOK_INCIDENTS`. Never in the repository, never printed; `check` prints which keys are set.
The scheduler runs on igneum-build-1 because the Mac sleeps: `infra/build-server/discord-hooks/{igneum-discord-hooks.service,
igneum-discord-hooks.timer, install.sh}`, a tick every minute. `install.sh` copies the script to `/srv/discord-hooks/bin`, the
credentials file to `/srv/discord-hooks/env` (mode 600, owner build, over ssh stdin) and enables the timer. The repository rule
says the box never holds a secret; a webhook URL is one, so the script refuses unless `IGNEUM_SECRET_ON_BOX_OK=1` is set,
which records the coordinator's ruling. A leaked webhook is deleted in Discord and the file replaced.
## Tested, 6 October 2026
Dry run against the live API at 18:51 UTC rendered all six posts (pulse 641 characters, digest 477, weekly 1,699, release
1,501, incident open 716 and resolve 659). The guard refused the fleet file's seed address and a line with an internal path
from the release plan. Finality was paused at render time (`finality.active` false, last lock 6842 at about 18:42Z, the newest
checkpoints at 55% of total weight): the watcher's first condition, seen in dry run. Not yet run: a live post (no credentials
file yet), the install on the box.
## The server invite
The standing invite is https://discord.gg/beCy8G5ZR (created 6 October 2026, main; the vanity names discord.gg/igneum and discord.gg/igneumnetwork were free on 3 October and are not yet claimed). The home page's join line and any public text use this URL; nobody asks for it again.