diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 8f0cfe3..07a2534 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -36,3 +36,9 @@ jobs: - name: Lint markdown run: make lint-markdown + + - name: Install hunspell + run: sudo apt-get update && sudo apt-get install -y --no-install-recommends hunspell + + - name: Check for British spelling + run: make lint-spelling diff --git a/Makefile b/Makefile index a668054..71f5fc6 100644 --- a/Makefile +++ b/Makefile @@ -22,4 +22,8 @@ lint-markdown: @echo "🧹 Linting markdown..." @$(YARN) lint:md -.PHONY: run-dev build preview lint lint-markdown +lint-spelling: + @echo "🧹 Checking for British spelling..." + @$(YARN) lint:spelling + +.PHONY: run-dev build preview lint lint-markdown lint-spelling diff --git a/accelerate/readiness.md b/accelerate/readiness.md index 98de9b9..aeb63d8 100644 --- a/accelerate/readiness.md +++ b/accelerate/readiness.md @@ -9,15 +9,15 @@ Before you accept anything, Console states what your history can support. A cutoff recommendation needs 200 measured proposals, across all your validators, not per key. Until the cycle holds that many, the Adjust step says **Not enough proposals yet for a recommendation**. You can still start Accelerate and upload a configuration. Measurement runs from the moment that configuration takes effect. The recommendation appears once the cycle holds enough. -## Modelled annual gain +## Modeled annual gain -The top of step 1 is about the modelled gain: what a later bid cutoff could have captured on blocks you already proposed, held at that rate for a year. It is read from the simulation the terms describe, so before you accept them the screen says **Accept the terms to see your modelled gain**. After you accept, the annual figure shows in that place. +The top of step 1 is about the modeled gain: what a later bid cutoff could have captured on blocks you already proposed, held at that rate for a year. It is read from the simulation the terms describe, so before you accept them the screen says **Accept the terms to see your modeled gain**. After you accept, the annual figure shows in that place. The other answers in that place: | On the screen | What it means | | --- | --- | -| **Reading your modelled gain** | The window is still being read. | +| **Reading your modeled gain** | The window is still being read. | | **Still gathering evidence** | The model needs 30 days of ingested history and 30 MEV blocks. The line says how far along you are. | | **No MEV blocks to model yet** | Nothing you proposed in the window took a relay bid, so there is no bid timing to model. | | **Nothing was left on the table** | No better bid arrived after the one you took, at any measured offset. | @@ -39,4 +39,4 @@ If Console could not read which indices are active, the row stays **Needed** and If indices are on record and the recommendation says **Not enough proposals yet for a recommendation**, check this row first. **Activating** means you are waiting on activation, not on proposal luck. -The report’s own charts measure per MEV block: accepted ETH on the bid that was taken, unrealised ETH on bids that arrived later. A per-proposal average is a different number and is labelled that way on the report. +The report’s own charts measure per MEV block: accepted ETH on the bid that was taken, unrealized ETH on bids that arrived later. A per-proposal average is a different number and is labeled that way on the report. diff --git a/accelerate/recommendation.md b/accelerate/recommendation.md index 11c52af..aeaea25 100644 --- a/accelerate/recommendation.md +++ b/accelerate/recommendation.md @@ -31,8 +31,8 @@ Once the window can support a number, the panel is **Recommended bid cutoff**, i Beside it: -* **Slots that could have improved** — the share of MEV blocks that had a better bid within a stated offset past the bid you took, over the modelling window. -* **Slots modelled** — MEV blocks in that window. +* **Slots that could have improved** — the share of MEV blocks that had a better bid within a stated offset past the bid you took, over the modeling window. +* **Slots modeled** — MEV blocks in that window. * **Relays on file** — how many relays the uploaded config names. Upload your own file if you want this to be your set. The figure is uplift that was available at that offset, on blocks already proposed. It does not price the risk of waiting. **No cutoff change indicated** means the window does not support moving it. diff --git a/accelerate/what-accelerate-does.md b/accelerate/what-accelerate-does.md index 83c1f13..fc419d6 100644 --- a/accelerate/what-accelerate-does.md +++ b/accelerate/what-accelerate-does.md @@ -7,15 +7,15 @@ description: A bid cutoff for the MEV sidecar you already run, from slots you al Under the **Accelerate** heading, the screen reads **Accepting bids closer to your deadline captures more MEV. Accelerate measures what that would have been worth on the blocks you propose, and recommends a cutoff from your own slots.** -Accelerate recommends the bid cutoff for the MEV sidecar you already run, so a proposal can take a better bid without waiting so long that the block is late. The number comes from slots your validators already proposed. You upload the sidecar configuration you run, read the recommendation, and download a config to deploy yourself. Console never writes to your infrastructure. Nothing on this screen changes validator behaviour until you deploy that file. +Accelerate recommends the bid cutoff for the MEV sidecar you already run, so a proposal can take a better bid without waiting so long that the block is late. The number comes from slots your validators already proposed. You upload the sidecar configuration you run, read the recommendation, and download a config to deploy yourself. Console never writes to your infrastructure. Nothing on this screen changes validator behavior until you deploy that file. ## Who can use it -The sidebar shows **Accelerate** when Optimum has enabled it for your organisation and you have at least 200 active validators on mainnet. It is offered to **entity** accounts, which is who reaches that number. Registering as an entity does not enable it. An individual account with at least 200 active validators on mainnet can be enabled the same way. See [Account type](/getting-in/account-type). +The sidebar shows **Accelerate** when Optimum has enabled it for your organization and you have at least 200 active validators on mainnet. It is offered to **entity** accounts, which is who reaches that number. Registering as an entity does not enable it. An individual account with at least 200 active validators on mainnet can be enabled the same way. See [Account type](/getting-in/account-type). A cutoff recommendation, once you are on the screen, still needs 200 measured proposals. [Readiness](/accelerate/readiness). -If **Accelerate** is not in the sidebar, your organisation is not enabled or it has fewer than 200 active validators on mainnet. Below that number the entry stays hidden. Otherwise ask [support](/help/support). There is no other URL to use. +If **Accelerate** is not in the sidebar, your organization is not enabled or it has fewer than 200 active validators on mainnet. Below that number the entry stays hidden. Otherwise ask [support](/help/support). There is no other URL to use. ## The steps @@ -31,10 +31,10 @@ When a measurement cycle finishes and you have not answered it, **Accelerate** i Every figure is measured on slots you already proposed. The figures sit on this screen, under **MEV outcome**. See [Acceleration](/accelerate/where-results-show). -Before you accept the terms, the top of the screen says **Accept the terms to see your modelled gain**. After you accept, that place shows the annual figure: what a later bid cutoff could have captured on the slots you propose, from the simulation the terms describe. See [Readiness](/accelerate/readiness#modelled-annual-gain). +Before you accept the terms, the top of the screen says **Accept the terms to see your modeled gain**. After you accept, that place shows the annual figure: what a later bid cutoff could have captured on the slots you propose, from the simulation the terms describe. See [Readiness](/accelerate/readiness#modeled-annual-gain). ## Older names on some screens -The sidebar entry and the screen heading are **Accelerate**. Results are **MEV outcome** on that screen. There is no **MumBoost** item under **Performance**, and the title **MEV Cutoff Optimisation** is not on this screen. +The sidebar entry and the screen heading are **Accelerate**. Results are **MEV outcome** on that screen. There is no **MumBoost** item under **Performance**, and the title **MEV Cutoff Optimization** is not on this screen. A direct link to the older report can still show two MumBoost messages: **Could not check the MumBoost terms**, and **This operator has not accepted the MumBoost terms**. Both mean the same check on Accelerate. See [Acceleration](/accelerate/where-results-show). diff --git a/accelerate/where-results-show.md b/accelerate/where-results-show.md index 4516254..57f50d8 100644 --- a/accelerate/where-results-show.md +++ b/accelerate/where-results-show.md @@ -14,7 +14,7 @@ Open **Accelerate** in the sidebar. The proposal figures are on that screen. The After a cutoff is on file, the screen can show two sections: * **Proposal CL results since last adjustment** — proposals since the day after you recorded the cutoff, split into MEV, vanilla, and missed, and the average head-vote accuracy on the proposed blocks. Missed includes orphaned blocks. The data does not separate those two. -* **MEV outcome** — total accepted ETH, the average accepted bid per proposed slot, and average unrealised MEV still on the table. **Bid value by slot** stacks the accepted bid with the unrealised remainder of the best bid seen. **Selected bid timing** plots when each accepted bid arrived at the relay. +* **MEV outcome** — total accepted ETH, the average accepted bid per proposed slot, and average unrealized MEV still on the table. **Bid value by slot** stacks the accepted bid with the unrealized remainder of the best bid seen. **Selected bid timing** plots when each accepted bid arrived at the relay. **MEV outcome** is collapsed until you open it. diff --git a/eslint.config.mjs b/eslint.config.mjs index 2db273d..f305c5d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -14,6 +14,7 @@ export default [ ] }, { languageOptions: { globals: globals.browser } }, + { files: ["scripts/**"], languageOptions: { globals: globals.node } }, pluginJs.configs.recommended, ...tseslint.configs.recommended, ...pluginVue.configs["flat/essential"], diff --git a/getting-in/account-type.md b/getting-in/account-type.md index 42720a1..506b3d3 100644 --- a/getting-in/account-type.md +++ b/getting-in/account-type.md @@ -10,10 +10,10 @@ The question is **How do you take part?** | | Individual | Entity | | --- | --- | --- | | Who | You take part under your own name. | A company, fund, or staking provider. | -| Next screen | **Your details**. Contact fields. | **Your organisation**. Entity fields, then the same contact fields. | +| Next screen | **Your details**. Contact fields. | **Your organization**. Entity fields, then the same contact fields. | | What you can use | Signal. | Signal, and Accelerate where Optimum enables it. | -Pick **Entity** if you want Accelerate. Optimum enables it per organisation, and the sidebar also needs at least 200 active validators on mainnet. Picking Entity does not enable it on its own. See [What Accelerate does](/accelerate/what-accelerate-does). +Pick **Entity** if you want Accelerate. Optimum enables it per organization, and the sidebar also needs at least 200 active validators on mainnet. Picking Entity does not enable it on its own. See [What Accelerate does](/accelerate/what-accelerate-does). The stored account type for an individual is an internal id. The label on the screen is **Individual**. diff --git a/getting-in/create-an-account.md b/getting-in/create-an-account.md index 29e9169..51540eb 100644 --- a/getting-in/create-an-account.md +++ b/getting-in/create-an-account.md @@ -12,20 +12,20 @@ Open [Console](https://console.getoptimum.xyz/). The sign-in card has two tabs. | **Create account** | **Create your account** | **Sign up with Google**, **Sign up with Microsoft** | | **Sign in** | **Sign in to Console** | **Continue with Google**, **Continue with Microsoft** | -**Create account** takes a new account into registration. **Sign in** takes you back to the console, or to the page you were opening. If you sign in with an account that has no organisation yet, finish registration from the button on the console home page (see below). +**Create account** takes a new account into registration. **Sign in** takes you back to the console, or to the page you were opening. If you sign in with an account that has no organization yet, finish registration from the button on the console home page (see below). **Trouble signing in?** at the bottom of the card opens support. Create account tab. Sign up with Google and Sign up with Microsoft are marked. -Registration is two screens. The first asks **How do you take part?** The second is **Your organisation**, or **Your details** for an individual, and includes **Do you run validators?** Registration ends when the account exists. Gateway setup, validator indices, and Accelerate are on the console after that. +Registration is two screens. The first asks **How do you take part?** The second is **Your organization**, or **Your details** for an individual, and includes **Do you run validators?** Registration ends when the account exists. Gateway setup, validator indices, and Accelerate are on the console after that. -The last screen is **Your account is ready**. It lists the account type, the organisation or username, the contact, and whether you run validators, then **Go to the console**. +The last screen is **Your account is ready**. It lists the account type, the organization or username, the contact, and whether you run validators, then **Go to the console**. ## When signup is closed -If the card has no **Create account** tab, or [signup](https://console.getoptimum.xyz/signup) shows a 404, self-serve signup is closed. Optimum creates the account for you, and you sign in with the Google or Microsoft account you gave them. Where Signal and Accelerate are enabled for your organisation, you use the same screens. +If the card has no **Create account** tab, or [signup](https://console.getoptimum.xyz/signup) shows a 404, self-serve signup is closed. Optimum creates the account for you, and you sign in with the Google or Microsoft account you gave them. Where Signal and Accelerate are enabled for your organization, you use the same screens. -## No organisation linked +## No organization linked **No operator account linked to your user.** means you signed in but registration never finished. While signup is open, the same screen shows **Finish setting up your account**, which returns you to registration. Without that button, ask [support](/help/support) to link the account. diff --git a/getting-in/register.md b/getting-in/register.md index 007c615..1b3f2d3 100644 --- a/getting-in/register.md +++ b/getting-in/register.md @@ -5,7 +5,7 @@ description: The fields Console asks for, and the terms checkbox on the same scr # Register -The heading is **Your details** or **Your organisation**, depending on the account type. +The heading is **Your details** or **Your organization**, depending on the account type. ## Entity @@ -32,4 +32,4 @@ If you answer yes, Console also asks: There is no separate terms screen. On this screen, tick **I have read and agree to the terms**. The checkbox opens the agreement. **Accept the terms to continue** if you submit without it. -Submitting this screen is what creates the organisation. The next screen is **Your account is ready**, then **Go to the console**. You connect a gateway on [Signal](/signal/what-signal-does), and submit validator indices on [Activate validators](/signal/register-keys), from the console. +Submitting this screen is what creates the organization. The next screen is **Your account is ready**, then **Go to the console**. You connect a gateway on [Signal](/signal/what-signal-does), and submit validator indices on [Activate validators](/signal/register-keys), from the console. diff --git a/help/faq.md b/help/faq.md index 54ed2ff..f2560e2 100644 --- a/help/faq.md +++ b/help/faq.md @@ -19,7 +19,7 @@ The identity directories were not mounted. [When the check fails](/signal/when-t ## The second gateway was refused -`OPT_GATEWAY_ID` is the name from **1a**, and it is unique per organisation. [Before you begin](/start/before-you-begin). +`OPT_GATEWAY_ID` is the name from **1a**, and it is unique per organization. [Before you begin](/start/before-you-begin). ## I lost the enrollment key @@ -35,7 +35,7 @@ Signal is switched on by Optimum. Until it is, the sidebar has no **Signal** ent ## I do not see Accelerate -The sidebar shows Accelerate when Optimum has enabled it for your organisation and you have at least 200 active validators on mainnet. Registering as an entity does not enable it. Below 200 active validators the entry stays hidden. Otherwise ask [support](/help/support). [What Accelerate does](/accelerate/what-accelerate-does). +The sidebar shows Accelerate when Optimum has enabled it for your organization and you have at least 200 active validators on mainnet. Registering as an entity does not enable it. Below 200 active validators the entry stays hidden. Otherwise ask [support](/help/support). [What Accelerate does](/accelerate/what-accelerate-does). ## I do not see a performance report diff --git a/help/troubleshoot.md b/help/troubleshoot.md index 79826cd..75dbdea 100644 --- a/help/troubleshoot.md +++ b/help/troubleshoot.md @@ -11,9 +11,9 @@ Start with the failure you can see. For the four failures Console names under Ve **Signup shows a 404, or there is no Create account tab.** Self-serve signup is closed. [Create an account](/getting-in/create-an-account#when-signup-is-closed). -**No operator account linked to your user.** Registration did not finish. Use **Finish setting up your account** if it is there, or ask [support](/help/support). [No organisation linked](/getting-in/create-an-account#no-organisation-linked). +**No operator account linked to your user.** Registration did not finish. Use **Finish setting up your account** if it is there, or ask [support](/help/support). [No organization linked](/getting-in/create-an-account#no-organization-linked). -**No Signal entry in the sidebar.** Signal is switched on by Optimum. Until it is, opening Signal returns you to your organisation's home, and there is no URL that gets around it. Ask [support](/help/support). [When Signal is not on your sidebar](/signal/what-signal-does#when-signal-is-not-on-your-sidebar). +**No Signal entry in the sidebar.** Signal is switched on by Optimum. Until it is, opening Signal returns you to your organization's home, and there is no URL that gets around it. Ask [support](/help/support). [When Signal is not on your sidebar](/signal/what-signal-does#when-signal-is-not-on-your-sidebar). ## Starting the container @@ -33,7 +33,7 @@ This is safe. The peer id and the enrollment credential live in `$HOME/optimum-g **Enrollment fails with `401`.** The key is unknown, expired, used up, or revoked. Optimum returns the same `401` for all four on purpose, so it reads like a typo. Check the key on **Enrollment keys**: the card shows the expiry date and **enrollments used**, and a revoked key is no longer listed. A key from Signal lasts one day. During signup, the same case shows **This key can no longer be used**. Mint a new key and run the command again. [Which key lifetime](/signal/what-signal-does#which-key-lifetime). -**A second host is refused with `409`.** Another gateway in your organisation already enrolled under that name. Give this gateway a new name in **1a**. [Onboard in bulk](/signal/onboard-in-bulk). +**A second host is refused with `409`.** Another gateway in your organization already enrolled under that name. Give this gateway a new name in **1a**. [Onboard in bulk](/signal/onboard-in-bulk). **A rebuilt host is refused with `409`.** The host lost `$HOME/optimum-gateway`, so it generated a new identity, and the old credential under its name is still live. Restore the directory if you have it. Otherwise revoke the old gateway key under **Gateways & keys** → **Gateway** (self-serve accounts: ask [support](/help/support)), then start it again. The same name is the right one on that host once the old credential is gone. @@ -49,7 +49,7 @@ This is safe. The peer id and the enrollment credential live in `$HOME/optimum-g **`curl` to port `48123` is refused from another machine.** Telemetry is published on the gateway host's loopback only. Run it on that host. -**Verify passes, and you are not sure it is the new gateway.** Verify checks every gateway in your organisation together. It shows counts, such as **Yes, 3** enrolled, not names. If another gateway is already healthy, the rows can pass before the new one reports. Confirm the new host with `curl http://localhost:48123/health` on that host. [When the check fails](/signal/when-the-check-fails#verify). +**Verify passes, and you are not sure it is the new gateway.** Verify checks every gateway in your organization together. It shows counts, such as **Yes, 3** enrolled, not names. If another gateway is already healthy, the rows can pass before the new one reports. Confirm the new host with `curl http://localhost:48123/health` on that host. [When the check fails](/signal/when-the-check-fails#verify). **The peer id changed after a restart.** The identity directories were not mounted. [The peer id changed](/signal/when-the-check-fails#the-peer-id-changed). @@ -61,7 +61,7 @@ This is safe. The peer id and the enrollment credential live in `$HOME/optimum-g ## Accelerate -**No Accelerate entry.** The sidebar shows it when Optimum has enabled it for your organisation and you have at least 200 active validators on mainnet. Registering as an entity does not add the entry. Below 200 active validators the entry stays hidden. Otherwise ask [support](/help/support). [Who can use it](/accelerate/what-accelerate-does#who-can-use-it). +**No Accelerate entry.** The sidebar shows it when Optimum has enabled it for your organization and you have at least 200 active validators on mainnet. Registering as an entity does not add the entry. Below 200 active validators the entry stays hidden. Otherwise ask [support](/help/support). [Who can use it](/accelerate/what-accelerate-does#who-can-use-it). **Validator keys registered says Activating.** Your indices are on record and none is active on chain yet. Pending validators are not assigned proposals, so nothing is measured until they activate. [Readiness](/accelerate/readiness). diff --git a/index.md b/index.md index bb15af7..dd02e7f 100644 --- a/index.md +++ b/index.md @@ -7,7 +7,7 @@ description: Optimum is a gateway network that delivers Ethereum blocks to your Optimum is a network of gateways you run beside your beacon node. The gateway delivers Ethereum blocks sooner, so your validators can attest to the head that was actually proposed. Where Optimum enables it, it also recommends a bid cutoff for the MEV sidecar you already run, so a proposal can take a better bid without waiting so long that the block is late. -These pages are the runbook for that setup. They are for staking providers and for people who run their own validators. [Start here](/start/what-optimum-does) if you want the path before the detail. [Open Console](https://console.getoptimum.xyz/) when you are ready to enrol a gateway. +These pages are the runbook for that setup. They are for staking providers and for people who run their own validators. [Start here](/start/what-optimum-does) if you want the path before the detail. [Open Console](https://console.getoptimum.xyz/) when you are ready to enroll a gateway. Your validators can only attest to a block after it reaches your beacon node. When the block arrives late, attestations miss or vote on the wrong head. When you propose, the bid cutoff sets how long the sidecar waits for a better bid. If you wait too long, your block is late. If you cut off too early, you leave value behind. @@ -56,14 +56,14 @@ The gateway is a peer of your consensus client. It passes blocks to the client a | | What it does | Where you see it | | --- | --- | --- | | **Signal** | The gateway hands your beacon node blocks from the mesh, so your validators receive blocks sooner. That improves attestation performance. | The Signal home in Console, then **Network** under **Performance** | -| **Accelerate** | Recommends a bid cutoff from slots your validators already proposed, so you can take a better bid without missing the slot. You download the config and deploy it on your own MEV sidecar. Offered to entity accounts. Shown when Optimum enables the organisation and it has at least 200 active validators on mainnet. | Accelerate in Console, under **MEV outcome**. See [Acceleration](/accelerate/where-results-show). | +| **Accelerate** | Recommends a bid cutoff from slots your validators already proposed, so you can take a better bid without missing the slot. You download the config and deploy it on your own MEV sidecar. Offered to entity accounts. Shown when Optimum enables the organization and it has at least 200 active validators on mainnet. | Accelerate in Console, under **MEV outcome**. See [Acceleration](/accelerate/where-results-show). | Console never changes your infrastructure. Optimum does not host gateways. You run the gateway and apply any configuration yourself. ## Who it is for * **Staking providers and other entities.** Run a gateway beside each beacon node, register your validator indices so Console can measure the result, then use Accelerate on your proposals. -* **Individuals who run validators.** Signal works the same way for you. Accelerate is offered to entity accounts, and the sidebar shows it once Optimum enables the organisation and it has at least 200 active validators on mainnet. [Account type](/getting-in/account-type). +* **Individuals who run validators.** Signal works the same way for you. Accelerate is offered to entity accounts, and the sidebar shows it once Optimum enables the organization and it has at least 200 active validators on mainnet. [Account type](/getting-in/account-type). * **Participants without validators.** Run a stream-only gateway to receive blocks from the mesh and serve them to your own consumers. This is a supported account. ## The path @@ -71,9 +71,9 @@ Console never changes your infrastructure. Optimum does not host gateways. You r Start here. This is the operator setup. 1. **[What Optimum does](/start/what-optimum-does).** How the pieces fit, which path is yours, and what the host needs. -2. **[Create an account](/getting-in/create-an-account).** Register your organisation in Console. +2. **[Create an account](/getting-in/create-an-account).** Register your organization in Console. 3. **[Signal](/signal/what-signal-does).** Name the gateway, start it, peer your consensus client, verify it, and register your validator indices. -4. **[Accelerate](/accelerate/what-accelerate-does).** Shown once Optimum enables your organisation and you have at least 200 active validators on mainnet. Then, once enough proposals are measured, read the cutoff recommendation and set it on your MEV sidecar. +4. **[Accelerate](/accelerate/what-accelerate-does).** Shown once Optimum enables your organization and you have at least 200 active validators on mainnet. Then, once enough proposals are measured, read the cutoff recommendation and set it on your MEV sidecar. [Open Console](https://console.getoptimum.xyz/) diff --git a/operate/run-the-gateway.md b/operate/run-the-gateway.md index afba30a..4b6f878 100644 --- a/operate/run-the-gateway.md +++ b/operate/run-the-gateway.md @@ -1,6 +1,6 @@ --- title: Run the gateway -description: Where the install, enrolment, and configuration references live. +description: Where the install, enrollment, and configuration references live. --- # Run the gateway diff --git a/package.json b/package.json index b8d36c4..0d9e2b3 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "docs:preview": "vitepress preview", "lint": "eslint --fix", "lint:md": "markdownlint '**/*.md'", + "lint:spelling": "sh scripts/fetch-dictionaries.sh && node scripts/check-us-spelling.mjs", "format": "prettier --write '**/*.{js,jsx,ts,tsx,json,css,scss}'" }, "devDependencies": { diff --git a/scripts/check-us-spelling.mjs b/scripts/check-us-spelling.mjs new file mode 100644 index 0000000..ad84803 --- /dev/null +++ b/scripts/check-us-spelling.mjs @@ -0,0 +1,89 @@ +// Fails when the docs use a British spelling: a word the US dictionary rejects, the matched +// British one accepts, and that has a close US spelling the British dictionary rejects +// (organisation/organization). Names and jargon are in neither dictionary, so they pass. +import { execFileSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; + +const dictDir = process.env.SPELLING_DICT_DIR ?? ".cache/dictionaries"; +const US = join(dictDir, "en_US"); +const GB = join(dictDir, "en_GB-ise"); +const IGNORE = "spelling-ignore-next-line"; +const WORD = /\p{L}+(?:['’]\p{L}+)*/gu; + +const blank = (s) => s.replace(/[^\n]/g, " "); + +// Code, link targets, URLs and tag attributes are not prose; blank them so line numbers hold. +function prose(markdown) { + return markdown + .replace(/^(```|~~~)[^\n]*\n[\s\S]*?^\1[^\n]*$/gm, blank) + .replace(/`[^`\n]*`/g, blank) + .replace(/\]\([^)\n]*\)/g, blank) + .replace(/https?:\/\/\S+/g, blank) + .replace(/<[^>]*>/g, blank); +} + +function hunspell(args, words) { + return execFileSync("hunspell", args, { input: words.join("\n") + "\n", encoding: "utf8" }) + .split("\n") + .filter(Boolean); +} + +const files = execFileSync("git", ["ls-files", "*.md", ".vitepress/config.mts"], { encoding: "utf8" }) + .split("\n") + .filter(Boolean); + +const occurrences = []; +for (const file of files) { + const raw = readFileSync(file, "utf8"); + const lines = (file.endsWith(".md") ? prose(raw) : raw).split("\n"); + const rawLines = raw.split("\n"); + lines.forEach((line, i) => { + if (i > 0 && rawLines[i - 1].includes(IGNORE)) return; + for (const [word] of line.matchAll(WORD)) occurrences.push({ file, line: i + 1, word }); + }); +} + +function distance(a, b) { + const row = Array.from({ length: b.length + 1 }, (_, j) => j); + for (let i = 1; i <= a.length; i++) { + let prev = row[0]; + row[0] = i; + for (let j = 1; j <= b.length; j++) { + const next = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1] === b[j - 1] ? 0 : 1)); + prev = row[j]; + row[j] = next; + } + } + return row[b.length]; +} + +const unique = [...new Set(occurrences.map((o) => o.word))]; +const rejectedByUs = hunspell(["-d", US, "-l"], unique); +const candidates = rejectedByUs.length ? hunspell(["-d", GB, "-G"], rejectedByUs) : []; + +// The dictionaries also differ on a few non-variant words (prev, coastguard), so require a +// close US form, same first three letters, that British English rejects. +// `hunspell -a` answers "& word n offset: s1, s2" for each misspelling. +const isClose = (a, b) => a.slice(0, 3) === b.slice(0, 3) && distance(a, b) <= 2; +const closeUs = new Map(); +for (const row of candidates.length ? hunspell(["-d", US, "-a"], candidates) : []) { + const m = row.match(/^& (\S+) \d+ \d+: (.+)$/); + if (!m) continue; + const near = m[2].split(", ").filter((s) => isClose(s.toLowerCase(), m[1].toLowerCase())); + if (near.length) closeUs.set(m[1], near); +} +const rejectedByGb = new Set(closeUs.size ? hunspell(["-d", GB, "-l"], [...new Set([...closeUs.values()].flat())]) : []); +const suggestion = new Map(); +for (const [word, near] of closeUs) { + const us = near.find((s) => rejectedByGb.has(s)); + if (us) suggestion.set(word, us); +} + +if (suggestion.size === 0) process.exit(0); + +for (const { file, line, word } of occurrences.filter((o) => suggestion.has(o.word))) { + console.log(`${file}:${line}: '${word}' is British spelling; US: ${suggestion.get(word)}`); +} +console.log(`\nUse American spelling. For a deliberate exception, put '${IGNORE}' in a comment on the line above.`); +process.exit(1); diff --git a/scripts/fetch-dictionaries.sh b/scripts/fetch-dictionaries.sh new file mode 100755 index 0000000..ea2d67a --- /dev/null +++ b/scripts/fetch-dictionaries.sh @@ -0,0 +1,34 @@ +#!/bin/sh +# Fetches the US and British Hunspell dictionaries that scripts/check-us-spelling.mjs compares. +# Both come from the same SCOWL release and size, so they differ only in variant spellings; +# mixing in a distro en_GB (a different, larger build) would flag ordinary jargon as British. +set -eu + +RELEASE=rel-2026.02.25 +DIR=${SPELLING_DICT_DIR:-.cache/dictionaries} +BASE=https://github.com/en-wl/wordlist/releases/download/$RELEASE + +sha256() { + if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1 + else shasum -a 256 "$1" | cut -d' ' -f1 + fi +} + +fetch() { + name=$1 zip=$2 want=$3 + [ -f "$DIR/$name.dic" ] && [ -f "$DIR/$name.aff" ] && return 0 + tmp=$(mktemp) + curl -fsSL -o "$tmp" "$BASE/$zip" + got=$(sha256 "$tmp") + if [ "$got" != "$want" ]; then + echo "checksum mismatch for $zip: got $got, want $want" >&2 + rm -f "$tmp" + exit 1 + fi + unzip -oq "$tmp" "$name.dic" "$name.aff" -d "$DIR" + rm -f "$tmp" +} + +mkdir -p "$DIR" +fetch en_US hunspell-en_US-2026.02.25.zip ac8e73310e951d88c52c2cf2ba54ceaca34f8486a81630ac8a75dc5f931179f9 +fetch en_GB-ise hunspell-en_GB-ise-2026.02.25.zip d6fbb91ae7824c52fb02f74d7bc2cd9092f130faec60f42326a59437fa7247a3 diff --git a/signal/connect-your-gateway.md b/signal/connect-your-gateway.md index 64b0973..2e12c34 100644 --- a/signal/connect-your-gateway.md +++ b/signal/connect-your-gateway.md @@ -9,7 +9,7 @@ This page covers **1b Start the gateway** and **1c Point your client at the gate ![Signal, Connect. 1a names the gateway. 1b starts it, 1c points the client at it, 1d verifies it.](/console/04-signal-connect.png) -**1a** is the name. Console writes it into the command as `OPT_GATEWAY_ID`. The blocks below use `fra-gateway-1` so you can see where that name sits. Copy the command from Console rather than retyping the name. Leaving the name unset uses the placeholder `dev-gateway`, which enrols with no label. If you just created the key, it is already in the command. Otherwise the command reads `$OPT_JOIN_KEY`, so export the key first. +**1a** is the name. Console writes it into the command as `OPT_GATEWAY_ID`. The blocks below use `fra-gateway-1` so you can see where that name sits. Copy the command from Console rather than retyping the name. Leaving the name unset uses the placeholder `dev-gateway`, which enrolls with no label. If you just created the key, it is already in the command. Otherwise the command reads `$OPT_JOIN_KEY`, so export the key first. `OPT_GATEWAY_CLUSTER_ID` has no default. The process exits if it is empty. Console sets it to the cluster the key was minted for, and the block below shows the mainnet value so you can read the shape. Copy the value Console prints. A different value authenticates and then fails every mesh handshake. @@ -17,7 +17,7 @@ The image Console hands out for production is `docker.io/getoptimum/gateway:late Do not add `OPT_CHAIN_ID`. Console does not put it in the command. The chain is the **Network** selected in the header when the key was minted. The one-day key on Signal is for Ethereum. A Hoodi gateway needs its own key from **Gateways & keys** → **Enrollment keys**. -Auth and bootstrap are built in: `https://auth.getoptimum.io` and `https://bootstrap.getoptimum.io`. They are not in the command. Set `OPT_REMOTE_AUTH_URL` only when Optimum gives you another environment, and set `OPT_REMOTE_BOOTSTRAP_URL` to the bootstrap for that same environment. An auth URL pointed elsewhere, with the default bootstrap, enrols in one place and peers in another. +Auth and bootstrap are built in: `https://auth.getoptimum.io` and `https://bootstrap.getoptimum.io`. They are not in the command. Set `OPT_REMOTE_AUTH_URL` only when Optimum gives you another environment, and set `OPT_REMOTE_BOOTSTRAP_URL` to the bootstrap for that same environment. An auth URL pointed elsewhere, with the default bootstrap, enrolls in one place and peers in another. Both telemetry variables are required. `OPT_ENABLE_TELEMETRY` without `OPT_REMOTE_PUSH_ENABLE` builds a local registry and does not push. `OPT_REMOTE_PUSH_ENABLE` without telemetry is refused at startup. `/health` answers either way, but two of its checks stay empty until telemetry is enabled. @@ -154,4 +154,4 @@ For Lighthouse this variable is the connection, not only the restart case: nothi Lighthouse v8 needs extra PeerDAS flags. Those are in the [gateway quick start](https://getoptimum.github.io/optimum-gateway/versions/latest/quick-start), not on this page. -`--libp2p-addresses` is deprecated in favour of `--boot-nodes`, and `--trusted-peers` takes a peer id. Console does not use that pair. Use `OPT_DIRECT_CL_PEERS` as above. +`--libp2p-addresses` is deprecated in favor of `--boot-nodes`, and `--trusted-peers` takes a peer id. Console does not use that pair. Use `OPT_DIRECT_CL_PEERS` as above. diff --git a/signal/onboard-in-bulk.md b/signal/onboard-in-bulk.md index 29ae5df..4ccd0db 100644 --- a/signal/onboard-in-bulk.md +++ b/signal/onboard-in-bulk.md @@ -7,19 +7,19 @@ description: Many gateways from one enrollment key, and many validators at once. ## Many gateways -You do not need one key per host. One enrollment key enrols every host on the network it was minted for. Set **Network** in the header first. Ethereum and Hoodi are two keys. See [Why enrollment keys](/signal/what-signal-does#why-enrollment-keys). +You do not need one key per host. One enrollment key enrolls every host on the network it was minted for. Set **Network** in the header first. Ethereum and Hoodi are two keys. See [Why enrollment keys](/signal/what-signal-does#why-enrollment-keys). 1. Set **Network** in the header, then mint a key that outlasts the rollout: **Gateways & keys** → **Enrollment keys** → **Generate enrollment key**. Set **Valid for (days)** to cover the time until the last host is started. The secret is shown once. Store it where your deployment tooling reads secrets. 2. On Signal, open **Set up another gateway**. In **1a**, set the name for this host, then copy the start command. It reads the key from `$OPT_JOIN_KEY` unless you had Console put a key in it. The name from **1a** is already in the command. 3. On that host, export `OPT_JOIN_KEY` and run the command. For the next host, change the name in **1a** and copy the command again. 4. Peer each gateway with the beacon node beside it. Every gateway has its own peer id. [Connect your gateway](/signal/connect-your-gateway). -5. Open Signal and **Run check**. Verify covers every gateway in your organisation together: **Gateway enrolled** shows how many, such as **Yes, 3**. It does not name them, so check each new host with `curl http://localhost:48123/health` on that host. +5. Open Signal and **Run check**. Verify covers every gateway in your organization together: **Gateway enrolled** shows how many, such as **Yes, 3**. It does not name them, so check each new host with `curl http://localhost:48123/health` on that host. Names must differ. The command sets `OPT_GATEWAY_ID` to the name from **1a**, and a second gateway under a name that is already enrolled is refused with HTTP 409. -Keep each host's `$HOME/optimum-gateway` directories. A gateway that keeps them re-uses its credential and spends nothing on restart. One that loses them enrols again, spends another of the key's enrollments, and is refused under the same name until the old credential is revoked. +Keep each host's `$HOME/optimum-gateway` directories. A gateway that keeps them re-uses its credential and spends nothing on restart. One that loses them enrolls again, spends another of the key's enrollments, and is refused under the same name until the old credential is revoked. -On Kubernetes, store the key in a Secret, inject it as `OPT_JOIN_KEY`, and give each pod a unique `OPT_GATEWAY_ID`, such as the pod name. Keep the identity PVC: an `emptyDir` is lost whenever the pod is replaced, and every replacement enrols again. The chart's own values still describe the older API key path. See [Fleet enrollment (join key)](https://getoptimum.github.io/optimum-gateway/versions/latest/kubernetes#fleet-enrollment-join-key) in the gateway Helm guide. +On Kubernetes, store the key in a Secret, inject it as `OPT_JOIN_KEY`, and give each pod a unique `OPT_GATEWAY_ID`, such as the pod name. Keep the identity PVC: an `emptyDir` is lost whenever the pod is replaced, and every replacement enrolls again. The chart's own values still describe the older API key path. See [Fleet enrollment (join key)](https://getoptimum.github.io/optimum-gateway/versions/latest/kubernetes#fleet-enrollment-join-key) in the gateway Helm guide. ## Many validators diff --git a/signal/what-signal-does.md b/signal/what-signal-does.md index a3a5aac..26d5439 100644 --- a/signal/what-signal-does.md +++ b/signal/what-signal-does.md @@ -9,7 +9,7 @@ Signal is the gateway beside your beacon node. It receives blocks from the Optim ## When Signal is not on your sidebar -Signal is switched on by Optimum. Until it is, the sidebar has no **Signal** entry: the screens below are not reachable, `/dashboard/operators//signal` returns you to your organisation's home, and **Verify the connection** answers **This check is not available right now.** +Signal is switched on by Optimum. Until it is, the sidebar has no **Signal** entry: the screens below are not reachable, `/dashboard/operators//signal` returns you to your organization's home, and **Verify the connection** answers **This check is not available right now.** That switch covers every account. It is not a fault in yours, and no URL gets around it. Ask [support](/help/support). Everything on these pages describes the screens as they appear once Signal is on. @@ -45,16 +45,16 @@ The pill while you are on these steps is the check: **Not checked**, **Checking* ## Why enrollment keys -A gateway does not need a secret of its own handed to it. You give every host the same enrollment key (`OPT_JOIN_KEY`, `ojk_…`). On first start, each gateway creates its own keypair, enrols with Optimum, and keeps the credential it gets back in `$HOME/optimum-gateway/mump2p`. From then on it uses that credential, not the enrollment key. +A gateway does not need a secret of its own handed to it. You give every host the same enrollment key (`OPT_JOIN_KEY`, `ojk_…`). On first start, each gateway creates its own keypair, enrolls with Optimum, and keeps the credential it gets back in `$HOME/optimum-gateway/mump2p`. From then on it uses that credential, not the enrollment key. That is what makes a fleet manageable: * **One key for many hosts.** Each host gets its own name in **1a**, and that name is in the command, so you change **1a** before you copy the command for the next host. A repeat of a name is refused with HTTP 409. * **Nothing to rotate on the hosts.** The key only lets new gateways in. When it expires or you revoke it, gateways that already enrolled keep running. -* **A short window if it leaks.** The key's lifetime is how long a copied command can still add gateways to your organisation. +* **A short window if it leaks.** The key's lifetime is how long a copied command can still add gateways to your organization. * **The network and cluster come from the key.** Set **Network** in the header to **Ethereum** or **Hoodi** before you open **Generate enrollment key**. The dialog shows that network. A gateway cannot pick its own. The one-day key on Signal is for Ethereum. A Hoodi gateway needs its own key. -Each key admits up to 1,000 gateways, and your organisation is capped at 1,000 live gateway credentials. A gateway that keeps its credential across restarts spends nothing. Losing the identity directory spends another enrollment. +Each key admits up to 1,000 gateways, and your organization is capped at 1,000 live gateway credentials. A gateway that keeps its credential across restarts spends nothing. Losing the identity directory spends another enrollment. ## Which key lifetime diff --git a/signal/when-the-check-fails.md b/signal/when-the-check-fails.md index c3331d4..69c2d8e 100644 --- a/signal/when-the-check-fails.md +++ b/signal/when-the-check-fails.md @@ -9,7 +9,7 @@ description: What Verify reports, what it cannot see, and the four failures Cons **Verify the connection** on Signal runs by itself when you open the screen. It keeps checking while a gateway is enrolled but has not reported in. **Run check** runs it again. -It reads three things from outside your host, across every gateway in your organisation. A row passes if any of them passes it. **Gateway enrolled** reads **Yes**, or **Yes, 3** for three gateways. **Blocks arriving** reads, for example, **4 of 5 slots**. +It reads three things from outside your host, across every gateway in your organization. A row passes if any of them passes it. **Gateway enrolled** reads **Yes**, or **Yes, 3** for three gateways. **Blocks arriving** reads, for example, **4 of 5 slots**. While you are on **1d**, Console also looks up the name from **1a**. **Not enrolled yet** means nothing has started under that name. **Enrolled, and seen in the last ten minutes** means that gateway reported in. **Enrolled, but its credential is suspended** means the gateway key was revoked. diff --git a/start/before-you-begin.md b/start/before-you-begin.md index e0ae1a5..6e4592e 100644 --- a/start/before-you-begin.md +++ b/start/before-you-begin.md @@ -18,4 +18,4 @@ Do not start the container with `--network host`. That publishes telemetry, incl On Fedora, RHEL, Rocky, and Alma, SELinux is enforcing by default. The identity mounts then fail with `write identity data: permission denied` after the log has already loaded JWKS, which looks like a rejected key. Console has **Add an SELinux relabel to the identity mounts** above the start command. Turn it on before you copy the command. See [When the check fails](/signal/when-the-check-fails). -One enrollment key covers every gateway you run on that network. Set **Network** in the header to **Ethereum** or **Hoodi** before you mint. Each needs its own key. Each gateway enrols under the name you give it in **1a** (`OPT_GATEWAY_ID`). Two gateways that share a name: the second enrolment is refused with HTTP 409. For many hosts, see [Onboard in bulk](/signal/onboard-in-bulk). +One enrollment key covers every gateway you run on that network. Set **Network** in the header to **Ethereum** or **Hoodi** before you mint. Each needs its own key. Each gateway enrolls under the name you give it in **1a** (`OPT_GATEWAY_ID`). Two gateways that share a name: the second enrollment is refused with HTTP 409. For many hosts, see [Onboard in bulk](/signal/onboard-in-bulk). diff --git a/start/choose-a-path.md b/start/choose-a-path.md index 9ad7339..25d41c6 100644 --- a/start/choose-a-path.md +++ b/start/choose-a-path.md @@ -13,7 +13,7 @@ Optimum does not host gateways. Every path below is a container you run on your The gateway runs beside the beacon node. You peer the consensus client, then register validator indices. Signal is the sidebar entry for that. -Accelerate is a later step, offered to entity accounts. The sidebar shows it once Optimum enables your organisation and you have at least 200 active validators on mainnet. It is not part of connecting the gateway. See [Account type](/getting-in/account-type). +Accelerate is a later step, offered to entity accounts. The sidebar shows it once Optimum enables your organization and you have at least 200 active validators on mainnet. It is not part of connecting the gateway. See [Account type](/getting-in/account-type). ## You do not run validators diff --git a/start/what-optimum-does.md b/start/what-optimum-does.md index e7fde00..847200c 100644 --- a/start/what-optimum-does.md +++ b/start/what-optimum-does.md @@ -5,7 +5,7 @@ description: The gateway, Signal, and Accelerate, in the words Console uses. # What Optimum does -You run one container, the Optimum gateway, on a host that can reach your beacon node. The gateway enrols with an enrollment key from Console, joins the mesh, and your consensus client peers it. That peering is **Signal**. +You run one container, the Optimum gateway, on a host that can reach your beacon node. The gateway enrolls with an enrollment key from Console, joins the mesh, and your consensus client peers it. That peering is **Signal**. **Accelerate** is separate. It reads slots your validators already proposed and recommends a bid cutoff, so a proposal can take a better bid without waiting so long that the block is late. You apply that cutoff on the MEV sidecar you already run, MEV-Boost or Commit-Boost. Console does not change your infrastructure.