diff --git a/docs/SOCIAL.md b/docs/SOCIAL.md index 4bdf117..df39e10 100644 --- a/docs/SOCIAL.md +++ b/docs/SOCIAL.md @@ -29,7 +29,7 @@ Record every account's credential recovery in the owner's password manager |---|---|---|---| | 1 | **X/Twitter** | Dev + fintech crowd (EN); baseline discovery; where OSS lives publicly | 2–4 posts/wk | | 1 | **Telegram channel** | The Arabic crypto/Islamic-finance audience's water cooler; broadcast-only mirrors announcements | auto (RSS → channel) | -| 2 | **YouTube** | The Arabic SERP research showed YouTube scholars dominate this topic — the core AR audience consumes video; TUI walkthroughs and methodology explainers fit | 1–2 videos/mo | +| 2 | **YouTube** | The Arabic SERP research showed YouTube scholars dominate this topic — the core AR audience consumes video; web-console walkthroughs and methodology explainers fit | 1–2 videos/mo | | 3 | Instagram / TikTok | Mass AR/FR audience; 60-second "what is qabd" style explainers | weekly, repurposed | | 3 | LinkedIn | Islamic-finance professionals, if enterprise ever matters | monthly | @@ -47,7 +47,7 @@ Record every account's credential recovery in the owner's password manager 5. Never answer "is X halal?" — the fatwa boundary is the product's edge. Redirect to the compliance methodology and fiqh-basis. 6. Venue names nominatively only; never imply endorsement. -7. Screenshots of the TUI (public/get-started/) and numbers from the +7. Screenshots of the web console (public/get-started/) and numbers from the experiment record are the visual language. No stock chart imagery, no rockets, no lambos — a keel, a terminal, a table of honest numbers. @@ -63,11 +63,11 @@ The project already produces content weekly — social mostly re-shapes it: - Release notes → version post with the install card - Glossary terms → definition-of-the-week (EN + AR) — definition-shaped content is what AI engines and fatwa-fatigued readers both want -- Get Started screenshots → TUI walkthrough threads +- Get Started screenshots → web-console walkthrough threads **Created (phase-gated):** -- YouTube: "Run keel in five minutes" (screen recording of the actual TUI), - "What attestation means", AR-language methodology explainers +- YouTube: "Run keel in five minutes" (screen recording of the actual web + console), "What attestation means", AR-language methodology explainers - Instagram/TikTok: 60-second explainers from the glossary, AR-first ## 5. Wiring into the site (after accounts exist) diff --git a/public/get-started/tui-compliance.png b/public/get-started/tui-compliance.png deleted file mode 100644 index 491a892..0000000 Binary files a/public/get-started/tui-compliance.png and /dev/null differ diff --git a/public/get-started/tui-data.png b/public/get-started/tui-data.png deleted file mode 100644 index a8e3709..0000000 Binary files a/public/get-started/tui-data.png and /dev/null differ diff --git a/public/get-started/tui-help.png b/public/get-started/tui-help.png deleted file mode 100644 index 51c7d20..0000000 Binary files a/public/get-started/tui-help.png and /dev/null differ diff --git a/public/get-started/tui-helpmenu.png b/public/get-started/tui-helpmenu.png deleted file mode 100644 index 2ab8cae..0000000 Binary files a/public/get-started/tui-helpmenu.png and /dev/null differ diff --git a/public/get-started/tui-insights.png b/public/get-started/tui-insights.png deleted file mode 100644 index d219a3a..0000000 Binary files a/public/get-started/tui-insights.png and /dev/null differ diff --git a/public/get-started/tui-main.png b/public/get-started/tui-main.png deleted file mode 100644 index f63b589..0000000 Binary files a/public/get-started/tui-main.png and /dev/null differ diff --git a/public/get-started/tui-menu.png b/public/get-started/tui-menu.png deleted file mode 100644 index a52f1e4..0000000 Binary files a/public/get-started/tui-menu.png and /dev/null differ diff --git a/public/get-started/tui-rules.png b/public/get-started/tui-rules.png deleted file mode 100644 index a05a7e6..0000000 Binary files a/public/get-started/tui-rules.png and /dev/null differ diff --git a/public/get-started/webui-fetch.png b/public/get-started/webui-fetch.png new file mode 100644 index 0000000..9ef9fe4 Binary files /dev/null and b/public/get-started/webui-fetch.png differ diff --git a/public/get-started/webui-gates.png b/public/get-started/webui-gates.png new file mode 100644 index 0000000..8ef849d Binary files /dev/null and b/public/get-started/webui-gates.png differ diff --git a/public/get-started/webui-insights.png b/public/get-started/webui-insights.png new file mode 100644 index 0000000..dd56cd0 Binary files /dev/null and b/public/get-started/webui-insights.png differ diff --git a/public/get-started/webui-rules.png b/public/get-started/webui-rules.png new file mode 100644 index 0000000..6a58081 Binary files /dev/null and b/public/get-started/webui-rules.png differ diff --git a/public/get-started/webui-setup.png b/public/get-started/webui-setup.png new file mode 100644 index 0000000..9666d17 Binary files /dev/null and b/public/get-started/webui-setup.png differ diff --git a/public/get-started/webui-status.png b/public/get-started/webui-status.png new file mode 100644 index 0000000..fd700a3 Binary files /dev/null and b/public/get-started/webui-status.png differ diff --git a/scripts/render-tui-shots.mjs b/scripts/render-tui-shots.mjs deleted file mode 100644 index 7759228..0000000 --- a/scripts/render-tui-shots.mjs +++ /dev/null @@ -1,143 +0,0 @@ -#!/usr/bin/env node -/** - * Render captured TUI screens (tmux capture-pane -e ANSI output) into PNG - * "terminal screenshots" for the Get Started guides: ANSI → HTML → PNG via - * the system Chrome (puppeteer-core), styled as a dark terminal window. - * - * node scripts/render-tui-shots.mjs [title] - */ -import { readFile, writeFile, mkdir } from "node:fs/promises"; -import { dirname } from "node:path"; -import puppeteer from "puppeteer-core"; - -const [input, output, title = "keel tui"] = process.argv.slice(2); -if (!input || !output) { - console.error("usage: render-tui-shots.mjs [title]"); - process.exit(1); -} - -/** Minimal ANSI SGR → converter (colors, bold, dim, reverse). */ -const COLORS = { - 30: "#5c6370", 31: "#e06c75", 32: "#98c379", 33: "#e5c07b", - 34: "#61afef", 35: "#c678dd", 36: "#56b6c2", 37: "#abb2bf", - 90: "#5c6370", 91: "#e06c75", 92: "#98c379", 93: "#e5c07b", - 94: "#61afef", 95: "#c678dd", 96: "#56b6c2", 97: "#ffffff", -}; -const BG = { - 40: "#282c34", 41: "#e06c75", 42: "#98c379", 43: "#e5c07b", - 44: "#61afef", 45: "#c678dd", 46: "#56b6c2", 47: "#abb2bf", - 100: "#5c6370", 101: "#e06c75", 102: "#98c379", 103: "#e5c07b", - 104: "#61afef", 105: "#c678dd", 106: "#56b6c2", 107: "#ffffff", -}; - -function ansiToHtml(ansi) { - let fg = null; - let bg = null; - let bold = false; - let dim = false; - let reverse = false; - let html = ""; - const openSpan = () => { - const styles = []; - if (bold) styles.push("font-weight:700"); - if (dim) styles.push("opacity:.6"); - let color = fg ?? "#abb2bf"; - let background = bg ?? "transparent"; - if (reverse) [color, background] = [background === "transparent" ? "#282c34" : background, color === "#abb2bf" ? "#abb2bf" : color]; - styles.push(`color:${color}`); - if (background !== "transparent") styles.push(`background:${background}`); - html += ``; - }; - const closeSpan = () => { html += ""; }; - - let i = 0; - let spanOpen = false; - const text = ansi - .replaceAll("\u001b[?25l", "") - .replaceAll("\u001b[?25h", "") - .replaceAll("\u001b[2J", "") - .replaceAll("\r", ""); - while (i < text.length) { - if (text.startsWith("\u001b[", i)) { - const m = /^\u001b\[([0-9;]*)m/.exec(text.slice(i)); - if (m) { - if (spanOpen) { closeSpan(); spanOpen = false; } - const params = m[1] === "" ? ["0"] : m[1].split(";"); - for (const p of params) { - const n = Number(p); - if (n === 0) { fg = null; bg = null; bold = dim = reverse = false; } - else if (n === 1) bold = true; - else if (n === 2) dim = true; - else if (n === 7) reverse = true; - else if (n === 22) { bold = dim = false; } - else if (n === 27) reverse = false; - else if (COLORS[n]) fg = COLORS[n]; - else if (BG[n]) bg = BG[n]; - else if (n === 39) fg = null; - else if (n === 49) bg = null; - } - i += m[0].length; - continue; - } - // other escapes: skip to final byte letter - const m2 = /^\u001b\[[0-9;?]*[A-Za-z]/.exec(text.slice(i)); - if (m2) { i += m2[0].length; continue; } - } - // open a span lazily before any text - if (!spanOpen) { openSpan(); spanOpen = true; } - const next = text.indexOf("\u001b", i); - const stop = next === -1 ? text.length : next; - html += text.slice(i, stop).replace(/&/g, "&").replace(//g, ">"); - i = stop; - if (spanOpen) { closeSpan(); spanOpen = false; } - } - if (spanOpen) closeSpan(); - return html; -} - -const ansi = await readFile(input, "utf8"); -const body = ansiToHtml(ansi) - .split("\n") - .map((line) => `
${line || " "}
`) - .join("\n"); - -const pageHtml = ` -
-
${title} — zsh
-
${body}
-
-`; - -const browser = await puppeteer.launch({ - executablePath: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", - headless: "new", -}); -const page = await browser.newPage(); -await page.setContent(pageHtml, { waitUntil: "domcontentloaded" }); -await page.setViewport({ width: 1400, height: 1600, deviceScaleFactor: 2 }); -await new Promise((r) => setTimeout(r, 150)); -const el = await page.$("#shot"); -await mkdir(dirname(output), { recursive: true }); -await el.screenshot({ path: output }); -await browser.close(); -console.log(`rendered ${output}`); diff --git a/scripts/render-webui-shots.mjs b/scripts/render-webui-shots.mjs new file mode 100644 index 0000000..b75abda --- /dev/null +++ b/scripts/render-webui-shots.mjs @@ -0,0 +1,172 @@ +#!/usr/bin/env node +/** + * Capture the Get Started guides' screenshots from keel's REAL web console. + * + * The browser counterpart of render-tui-shots.mjs: where that pipeline ran the + * engine's `keel tui` under tmux and photographed the terminal, this one runs + * `keel serve` (the local web UI the engine ships since the curses TUI was + * deleted, keel#541) and photographs the pages in the system Chrome via + * puppeteer-core. Every PNG it writes is a real screen of a real deployment — + * a fresh paper working directory with the seeded 32 candidate rules, exactly + * the state guide 1 leaves you in. No mocks, no composed imagery. + * + * What it does, end to end: + * + * 1. scaffolds a throwaway deployment in a temp dir — `keel init-config` + * followed by `keel rules seed`, which is exactly what `keel init` runs + * (see keel/cli.py: init = init-config + rules seed). The two commands + * are invoked separately because init's seed step resolves the database + * through the deployment-root detector, which cannot see a folder that + * has no config.yaml yet — run as one command in a fresh dir the seed + * lands on the machine's state root instead of the folder. + * 2. launches `keel serve --no-open` on a loopback port and reads the + * one-time session-token URL it prints, + * 3. drives Chrome to each guide view and screenshots the viewport. + * + * Re-run against a new engine release by pointing --keel-dir at a checkout of + * the release tag (with `uv sync` done) — the guide copy is then updated to + * match whatever the new screens actually show, never before. + * + * node scripts/render-webui-shots.mjs \ + * --keel-dir /tmp/keel-engine-r123 --port 8911 + * + * Needs the system Chrome at the path below (the same executable + * render-tui-shots.mjs uses). + */ +import { spawn, spawnSync } from "node:child_process"; +import { mkdir, rm } from "node:fs/promises"; +import { mkdtempSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { tmpdir } from "node:os"; +import puppeteer from "puppeteer-core"; + +const CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"; + +// -- arguments ----------------------------------------------------------------------------------- + +const args = process.argv.slice(2); +const arg = (name, fallback) => { + const i = args.indexOf(`--${name}`); + return i !== -1 && args[i + 1] ? args[i + 1] : fallback; +}; +const KEEL_DIR = resolve(arg("keel-dir", join(tmpdir(), "keel-engine-shots"))); +const PORT = Number(arg("port", "8911")); +const OUT_DIR = resolve(arg("out", "public/get-started")); +const KEEP = args.includes("--keep"); // keep the scratch deployment for inspection + +const keel = join(KEEL_DIR, ".venv", "bin", "keel"); + +/** Run one keel command in `cwd`, failing loudly — a screenshot pipeline that + * silently continues past a failed scaffold would photograph nothing honestly. */ +function run(cwd, ...argv) { + const r = spawnSync(keel, argv, { cwd, encoding: "utf8" }); + if (r.status !== 0) { + console.error(`keel ${argv.join(" ")} failed (${r.status}):\n${r.stderr || r.stdout}`); + process.exit(1); + } + return r.stdout; +} + +// 1. scaffold a fresh paper deployment ----------------------------------------------------------- + +// mkdtemp, not a fixed name under /tmp: a predictable path in a +// world-writable directory is a symlink-attack surface (SonarCloud S5443), +// even for a local capture script. The Setup screenshot shows the path of +// the run that produced it — illustrative, not contractural. +const scratch = mkdtempSync(join(tmpdir(), "keel-paper-")); +console.log(`scaffolding deployment in ${scratch}`); +run(scratch, "init-config"); // writes config.yaml — the dev/paper template +run(scratch, "rules", "seed"); // the 32 candidate rules, nothing live + +// 2. launch the web console ---------------------------------------------------------------------- + +console.log(`serving the web console on 127.0.0.1:${PORT}`); +const server = spawn(keel, ["serve", "--no-open", "--port", String(PORT)], { + cwd: scratch, + stdio: ["ignore", "pipe", "pipe"], +}); + +/** Resolve once `keel serve` has printed its one-time token URL. */ +const tokenUrl = await new Promise((res, rej) => { + const timeout = setTimeout(() => rej(new Error("keel serve did not print its URL")), 30_000); + const onChunk = (buf) => { + const m = /https?:\/\/\S+\?token=\S+/.exec(buf.toString()); + if (m) { + clearTimeout(timeout); + res(m[0]); + } + }; + server.stdout.on("data", onChunk); + server.stderr.on("data", onChunk); + server.on("exit", (code) => rej(new Error(`keel serve exited early (${code})`))); +}); +console.log(`console up: ${tokenUrl.replace(/token=.*/, "token=…")}`); + +// 3. capture ------------------------------------------------------------------------------------- + +/** + * The six guide shots. `scroll` optionally names a `.card.step` by the strong + * text on its head line, for the cards that sit below the fold on /setup — + * the fetch shot targets the credentials card so the market-data card below + * it (with the Fetch market data button) lands in the same viewport. + * @type {Array<{ name: string; route: string; h1: string; scroll?: string }>} + */ +const SHOTS = [ + { name: "webui-setup", route: "/setup", h1: "Setup" }, + { name: "webui-status", route: "/status", h1: "Status" }, + { name: "webui-fetch", route: "/setup", h1: "Setup", scroll: "A market-data credential" }, + { name: "webui-insights", route: "/insights", h1: "Insights" }, + { name: "webui-rules", route: "/rules", h1: "Rules" }, + { name: "webui-gates", route: "/gates", h1: "Gates" }, +]; + +const browser = await puppeteer.launch({ executablePath: CHROME, headless: "new" }); +const page = await browser.newPage(); +// The engine's light "paper" theme, pinned rather than left to this machine's +// OS preference, so re-runs produce comparable shots. Same storage key the +// console's own theme toggle writes (js/theme.js). +await page.evaluateOnNewDocument(() => { + localStorage.setItem("keel-theme", "light"); +}); +await page.setViewport({ width: 1440, height: 900, deviceScaleFactor: 2 }); + +// The token URL exchanges the one-time token for the session cookie (303 → /). +// `networkidle0` never fires on this app: the console keeps a live event-stream +// connection open on every page, so "load" is the honest event to wait for. +await page.goto(tokenUrl, { waitUntil: "domcontentloaded" }); + +await mkdir(OUT_DIR, { recursive: true }); +for (const shot of SHOTS) { + await page.goto(`http://127.0.0.1:${PORT}${shot.route}`, { waitUntil: "domcontentloaded" }); + // The client fills #content from /api/* after load; h1 is the view's own title. + await page.waitForSelector(`#content h1`); + await page.waitForFunction( + (title) => document.querySelector("#content h1")?.textContent === title, + { timeout: 15_000 }, + shot.h1, + ); + if (shot.scroll) { + // Step cards carry no ids; find the one whose head line names the step. + await page.evaluate((label) => { + const card = [...document.querySelectorAll(".card.step")].find((c) => + c.querySelector("strong")?.textContent?.includes(label), + ); + card?.scrollIntoView({ block: "start" }); + }, shot.scroll); + } + await new Promise((r) => setTimeout(r, 250)); // settle after scroll/paint + const path = join(OUT_DIR, `${shot.name}.png`); + await page.screenshot({ path }); + console.log(`captured ${path}`); +} + +// 4. clean up ------------------------------------------------------------------------------------ + +await browser.close(); +server.kill("SIGTERM"); +if (!KEEP) { + await rm(scratch, { recursive: true, force: true }); + console.log(`removed ${scratch}`); +} else { + console.log(`kept deployment: ${scratch} (serve was: keel serve --port ${PORT})`); +} diff --git a/src/components/pages/GuidePage.astro b/src/components/pages/GuidePage.astro index 5f69e93..2e6c943 100644 --- a/src/components/pages/GuidePage.astro +++ b/src/components/pages/GuidePage.astro @@ -8,7 +8,8 @@ import { SITE } from "../../i18n/config"; /** * A Get Started guide page: numbered step sections, optional command blocks, - * and real TUI screenshots (public/get-started/). Editorial English — like + * and real web-console screenshots (public/get-started/, captured by + * scripts/render-webui-shots.mjs). Editorial English — like * the engine documents — surfaced through the docs sidebar. Prev/next walks * the whole guides lane, so the last Get Started guide continues into the * deep explainers. diff --git a/src/i18n/guides/content.ts b/src/i18n/guides/content.ts index edfb573..4d4dfde 100644 --- a/src/i18n/guides/content.ts +++ b/src/i18n/guides/content.ts @@ -1,9 +1,11 @@ /** * Get Started guides — editorial, site-authored newbie walkthroughs (EN for - * now, like the engine documents themselves). Every command and screen below - * was verified against keel v0.10.0: the TUI screenshots in - * public/get-started/ were captured from a real `keel tui` session on a - * fresh `keel init` working directory. + * now, like the engine documents themselves). Every command, page and button + * below was verified against keel v0.12.2: the screenshots in + * public/get-started/ were captured by scripts/render-webui-shots.mjs from a + * real `keel serve` console on a fresh paper working directory — the curses + * TUI these guides used to teach was deleted from the engine before v0.12.2 + * (keel#541), and the web console replaced it. */ export interface GuideStep { title: string; @@ -26,9 +28,9 @@ export const guides: Guide[] = [ slug: "first-run", title: "1 · Install and first run", description: - "From a fresh download to the operator console on your screen: scaffold a working directory and take the tour.", + "From a fresh download to the web console in your browser: scaffold a working directory and take the tour.", intro: - "This guide starts from nothing and ends with the keel console running on your machine. Everything here is read-only and paper-side — no funds, no keys, nothing can trade. Ten minutes, four commands.", + "This guide starts from nothing and ends with the keel console open in your browser. Everything here is read-only and paper-side — no funds, no keys, nothing can trade. Ten minutes, four commands.", steps: [ { title: "Step 1 — Install keel", @@ -41,42 +43,42 @@ export const guides: Guide[] = [ { title: "Step 2 — Scaffold a working directory", body: [ - "keel keeps everything — config, database, logs — in one working directory. Create a fresh folder and scaffold it:", + "keel keeps everything — config, database, logs — in one working directory. Create a fresh folder and write the two pieces with the commands that name exactly what each one writes. (keel init runs these same two back to back, but its seeding step resolves the database through keel's state-root rules, which cannot see a folder that has no config.yaml yet — as two commands in this order, the second sees the config the first wrote, and the database lands in your folder.)", ], - code: "mkdir keel-paper && cd keel-paper\nkeel init", - shot: "/get-started/tui-main.png", + code: "mkdir keel-paper && cd keel-paper\nkeel init-config\nkeel rules seed", + shot: "/get-started/webui-setup.png", shotCaption: - "The dashboard after a fresh keel init: paper mode, kill-switch ENGAGED by default, 32 rule candidates seeded, no data yet. Exactly what a safe starting point looks like.", + "The console's Setup page after the scaffold (yours will show your folder's paths): config file, database and the 32-rule library done; a market-data credential and everything judgement-shaped still outstanding. Paper places nothing.", }, { - title: "What keel init created", + title: "What the scaffold created", body: [ "config.yaml — the deployment's settings, written in the dev/paper profile. Review the allowlist, caps, and auto_trade sections before anything else.", "keel.db — the local SQLite database (rules, trials, orders). logs/ — what the engine did and why.", - "keel init also seeds the rule registry: 32 rule-product candidates (four rule families across the default allowlist), all in candidate status. Nothing is live; nothing trades.", + "keel rules seed also populates the rule registry: 32 rule-product candidates — four rule families (pullback_continuation, rsi_meanrev, dca, turtle_breakout) across the default eight-product allowlist — all in candidate status. Nothing is live; nothing trades.", ], }, { - title: "Step 3 — Open the operator console", + title: "Step 3 — Open the web console", body: [ - "The console (we call it the TUI) is a live, full-screen dashboard. Start it with:", + "The console is a small web page the engine itself serves, bound to your machine's loopback address only:", ], - code: "keel tui", + code: "keel serve", }, { - title: "Step 4 — Take the tour: nine menus", + title: "Step 4 — Take the tour: seven views", body: [ - "Press m to open the menu. Everything keel does lives behind these nine entries — the guides that follow walk through the ones you'll use first: Rules, Compliance, and Data.", + "keel serve prints a URL carrying a one-time token for this run, opens your browser, and from there everything lives behind the header's seven views. Status (the page you land on) answers \"is it alive\"; Setup is the checklist of what this deployment still needs; Activity, Insights, Rules and Venues report what keel did and found; Gates lists every capability-increasing action and what gates it. The eighth header entry, Docs, links out to the documentation you are reading.", ], - shot: "/get-started/tui-menu.png", + shot: "/get-started/webui-status.png", shotCaption: - "The console menu (m): Dashboard, Profile, Trading, Rules, Compliance, Data, Research, Account, Help. Number keys jump; q or Esc returns to the dashboard.", + "The console's Status view with the seven-view header: Status, Setup, Activity, Insights, Rules, Venues, Gates. The paper badge (top right) is the deployment's mode; the footer says it is served from this machine only.", }, { title: "Step 5 — Read the dashboard", body: [ - "Back on the dashboard (Esc), read it top to bottom: the deployment profile and mode, the kill-switch state, autonomy, drawdown ceilings, rail 17's attestation state, cash, positions, rules, and data freshness for every allowlisted product.", - "The footer is your keyboard map: h help, i insights, r refresh, a autonomy, f fetch — and s/p/d/v/m switch overlays. Press ? anywhere for the current screen's help.", + "Read the Status page top to bottom: the mode and kill-switch state (ENGAGED on a fresh install), autonomy, rail 11's drawdown breaker; the equity card — high-water mark, paper cash, drawdown against its ceilings; rail 17's withdrawal attestation; the market session. Below the cards, three tables: open positions, rule counts (candidate=32), and data freshness — \"No market data yet\", which the next guide fixes.", + "The page re-reads itself every fifteen seconds, so the figures move without you reloading. The theme toggle in the header is yours; the mode badge is the config's, and no page in this console can change it — that is a terminal action by design.", ], }, { @@ -99,17 +101,19 @@ export const guides: Guide[] = [ title: "Step 1 — Get a read-only market-data key", body: [ "Candle history is fetched through Coinbase's authenticated client, so keel fetch needs a free Coinbase Developer Platform (CDP) key with read-only permissions — market data only, no trading scope.", - "Put the key and secret in .env in your working directory (copy .env.example if you came from the source path). Without a key, keel fetch fails with an AuthenticationError — that is by design, not a bug.", + "Give it to keel from the terminal — it prompts with the echo off and stores the pair in your operating system's keychain, not in a file, and never on the command line where shell history could keep it:", ], + code: "keel credentials set CDP_API_KEY", + shot: "/get-started/webui-fetch.png", + shotCaption: + "The Setup page's paper-stage checklist, scrolled to the two steps this guide touches: the credential step's Save a market-data credential form (key and secret, straight into the OS keychain), and the Market data step below it.", }, { title: "Step 2 — Warm the candle cache", body: [ - "From the console, press f (or open the Data menu, entry 6) and run fetch — or from the terminal:", + "On the console's Setup page, the Market data step has a Fetch market data button — it runs the fetch as a background job and the page shows its progress. Or run it from the terminal:", ], code: "keel fetch", - shot: "/get-started/tui-data.png", - shotCaption: "The Data menu: fetch warms the candle cache for every allowlisted product; freshness, gap repair, and db import live here too.", }, { title: "Step 3 — Run the simulation", @@ -128,10 +132,11 @@ export const guides: Guide[] = [ { title: "Step 5 — Watch the distance to the gate", body: [ - "The console's insights view (i) reports read-only promotion-gate distance: how far each rule is from the floors it must clear. Use it to see whether a rule is weeks or years from eligibility.", + "The console's Insights view reports read-only promotion-gate distance: how far each rule is from the floors it must clear. On a fresh deployment its gate-distance table says so plainly — no rule has the 20 trades the sample floor needs — which is exactly the report's TRAIN-MORE, one click away, re-read every fifteen seconds.", ], - shot: "/get-started/tui-insights.png", - shotCaption: "Insights (i): promotion-gate distance and reporting, read-only.", + shot: "/get-started/webui-insights.png", + shotCaption: + "The Insights view: the account card with its drawdown ceilings, promotion-gate distance, and the journal — read-only reporting.", }, { title: "Next", @@ -152,18 +157,18 @@ export const guides: Guide[] = [ { title: "Step 1 — Understand profiles", body: [ - "A profile is a config.yaml + database pair. keel init gave you the dev/paper profile. The console's Profile menu (2) switches between discovered profiles — daily paper, live, paper-hourly — and LIVE always asks first. Profiles share nothing: separate databases, separate accounts.", + "A profile is a config.yaml + database pair, one folder, sharing nothing: separate databases, separate accounts. The folder you are in is the deployment keel operates — the console's Setup page names the config and database files it resolved, and the mode badge in the header says what that config declares. Switching profiles is changing folder, not clicking: mode is a config edit plus a terminal action, and LIVE always asks first. The console displays the mode; it cannot change it.", ], }, { title: "Step 2 — Start the agent in paper mode", body: [ - "From the terminal:", + "From the terminal, in the deployment folder:", ], code: "keel agent", - shot: "/get-started/tui-rules.png", + shot: "/get-started/webui-rules.png", shotCaption: - "The Rules console (4): the lifecycle ledger — every rule with the stage it has reached. candidate → paper → live is the only road to live trading.", + "The console's Rules view — the lifecycle ledger. While the agent runs, every rule is still a candidate and the live-rules table is empty: candidate → paper → live is the only road to live trading.", }, { title: "Step 3 — Confirm every order", @@ -174,7 +179,7 @@ export const guides: Guide[] = [ { title: "Step 4 — The kill-switch fails closed", body: [ - "To stop everything: keel kill — from the terminal or the console's Trading menu. It halts trading immediately and stays engaged until you explicitly resume (keel resume asks first, deliberately). A brand-new install starts with the kill-switch ENGAGED; you saw it on the dashboard.", + "To stop everything: keel kill — always allowed, from any terminal. It halts trading immediately and stays engaged until you explicitly resume (keel resume asks first, deliberately). A brand-new install starts with the kill-switch ENGAGED — you saw it on the Status page, and your agent's first cycle on a fresh deployment says it too, logging skipped: kill_switch. The console's Gates view names keel resume for what it is — a capability-increasing action behind the terminal gate.", ], }, { @@ -189,50 +194,45 @@ export const guides: Guide[] = [ slug: "compliance-attest", title: "4 · Attestations before anything is live", description: - "The compliance menu in practice: screening, asset attestations, the venue subscription, and withdrawal capability.", + "The Setup checklist's live stage in practice: screening, asset attestations, the venue subscription, and withdrawal capability.", intro: - "keel never derives a Shariah classification from market data — you record rulings, with sources, and the engine enforces them. This guide walks the Compliance menu: the work that makes a live profile possible.", + "keel never derives a Shariah classification from market data — you record rulings, with sources, and the engine enforces them. This guide walks the \"To go live\" stage of the console's Setup checklist and the Gates view behind it: the work that makes a live profile possible.", steps: [ { - title: "Step 1 — Open the Compliance menu", + title: "Step 1 — Where the compliance work lives", body: [ - "In the console, press m and choose Compliance (5). Everything compliance-shaped lives here — and all of it links back to a source you supply.", + "The console's Setup page ends in a stage headed To go live — every attestation the rails refuse to trade without, each with its state and the command that records it. The Gates view is the other half of the picture: every action that increases what keel can do without asking again, and the interactive-terminal gate each one passes through. The view states it plainly: it cannot perform any of them.", ], - shot: "/get-started/tui-compliance.png", + shot: "/get-started/webui-gates.png", shotCaption: - "The Compliance menu: screen, propose, attest, exemptions, subscription, purification.", - }, - { - title: "Step 2 — Screen the allowlist", - body: [ - "screen shows every allowlisted product's admission verdict. Market facts are computed; Shariah classifications are attested, never inferred — an asset without an attestation is rejected, not passed by default.", - ], + "The Gates view: keel autonomy on, keel resume, keel withdrawals attest --enabled and friends — each with what it increases, and the terminal gate (a typed yes at an interactive TTY) it must pass.", }, { - title: "Step 3 — Record an asset attestation", + title: "Step 2 — Screen and attest the allowlist", body: [ - "From the terminal (or the menu's attest entry), record a classification with a source and your name:", + "The Setup checklist's step \"Every allowlisted asset screened and attested\" is judgement-shaped, and the page says what that means: keel can record your ruling; it must never choose it for you. Market facts are computed; Shariah classifications are attested, never inferred — an asset without an attestation is unknown, not fine, and the step's detail reads \"unattested: ADA, BTC, …\" until you clear it.", + "The step's own form — Record this attestation — asks for exactly the ruling: asset code, sector, backing ('ayn, dayn, or native), whether holding it earns a return, a source, and your name. Nothing is pre-filled or defaulted; an attestation without a cited source is refused like a missing one. The same action at the terminal:", ], - code: "keel assets attest", + code: "keel assets attest --asset X --sector ... --backing ... --source ...", }, { - title: "Step 4 — Attest the venue subscription (rail 14)", + title: "Step 3 — Attest the venue subscription (rail 14)", body: [ - "Rail 14 refuses live BUYs until the operator attests the venue's subscription — the monthly allowance actually spent. keel subscription shows and records it.", + "Rail 14 refuses live BUYs until the operator attests the venue's subscription — the monthly allowance actually spent. What you pay the venue is a fact only you have; keel cannot read your billing. This one stays at the terminal — the checklist itself prints the command — and keel subscription show reads it back:", ], - code: "keel subscription", + code: "keel subscription attest --venue ... --tier ...", }, { - title: "Step 5 — Attest withdrawal capability (rail 17)", + title: "Step 4 — Attest withdrawal capability (rail 17)", body: [ - "Rail 17 encodes qabd (§65.4): an asset that cannot be withdrawn may not have been validly possessed. keel withdrawals records withdrawal-capability attestations per product.", + "Rail 17 encodes qabd (§65.4): an asset that cannot be withdrawn may not have been validly possessed. keel withdrawals attest records withdrawal-capability attestations per product — the attestation carries a 7-day TTL and needs an interactive terminal, as both the checklist and the Gates view say outright.", ], - code: "keel withdrawals", + code: "keel withdrawals attest --enabled", }, { - title: "Step 6 — Purification, honestly", + title: "Step 5 — Purification, honestly", body: [ - "keel purification reports non-compliant income owed to charity (KB §65.9) from what actually ran through the engine. Account-level duties no rail can see — disabling USDC rewards on idle balances, chiefly — stay on the operator's checklist, in the operator runbook.", + "keel purification reports non-compliant income owed to charity (KB §65.9) from what actually ran through the engine. Account-level duties no rail can see — disabling USDC rewards on idle balances, chiefly — appear in the checklist as the venue-interest step, and keel will never show that one as done: the venue's API does not expose enrolment, and a green check that verifies nothing turns an open risk into a false assurance. It stays on the operator's checklist, in the operator runbook.", ], }, { diff --git a/src/i18n/pages/install.ts b/src/i18n/pages/install.ts index ac0e533..6d61b38 100644 --- a/src/i18n/pages/install.ts +++ b/src/i18n/pages/install.ts @@ -190,7 +190,7 @@ export const install: LocalizedPage = { paperFirstBody: "For the cautious first step: the paper profile is free and educational, with simulated fills and no real orders. It is built for learning the workflow before any live decision. It needs no funded venue account and no trading credentials; the only key it asks for is a free, read-only market-data key, used to fetch candle history. The live profile is deliberately harder to reach — attestations, the promotion gauntlet and typed human confirmations all stand in the way.", getStarted: { title: "New here? Start with the Get Started guide", - body: "A step-by-step walkthrough — first simulation, the paper profile, the operator console — with screenshots of every screen.", + body: "A step-by-step walkthrough — first simulation, the paper profile, the web console — with screenshots of every screen.", link: "Open Get Started", }, browserTitle: "Work from the browser — the desktop app", @@ -438,7 +438,7 @@ export const install: LocalizedPage = { paperFirstBody: "Pour un premier pas prudent : le profil papier est gratuit et pédagogique — exécutions simulées, aucun ordre réel — conçu pour apprendre le fonctionnement avant toute décision en réel. Il ne réclame ni compte approvisionné sur une plateforme, ni identifiants de trading ; la seule clé demandée est une clé de données de marché gratuite, en lecture seule, pour récupérer l'historique des bougies. Le profil réel, lui, se mérite délibérément : attestations, parcours de promotion et confirmations tapées à la main se dressent sur la route.", getStarted: { title: "Nouveau ici ? Commencez par le guide Premiers pas", - body: "Un parcours pas à pas — première simulation, profil papier, console de l'opérateur — avec une capture de chaque écran.", + body: "Un parcours pas à pas — première simulation, profil papier, console web — avec une capture de chaque écran.", link: "Ouvrir Premiers pas", }, browserTitle: "Travailler depuis le navigateur — l'application de bureau",