diff --git a/whitepaper/README.md b/whitepaper/README.md new file mode 100644 index 0000000..e429f0c --- /dev/null +++ b/whitepaper/README.md @@ -0,0 +1,64 @@ +# Theseus Navigator whitepaper + +Published at `theseus.x/whitepaper/` (Sia bucket `bns/theseus/whitepaper/`), mirrored at +`https://silentmode.st/theseus-x/whitepaper/` and reachable through the gateway at +`https://navigate.st/bns/theseus.x/whitepaper/`. Licence: CC BY 4.0 (see `LICENSES.md` at the repo root). + +## Files + +| File | Role | +| --- | --- | +| `whitepaper.md` | The text. Source of truth in the repo; one paragraph per line, `##` sections. | +| `index.html` | Generated. Never edit by hand; rebuild it. | +| `build/build.mjs` | Markdown → page. `node build/build.mjs` writes `index.html`; `--check` verifies it is current. | +| `build/template.html` | Page chrome (nav, styles, footer, TOC script) with `{{title}}`, `{{byline}}`, `{{toc}}`, `{{article}}`. | +| `build/figure-*.html` | The diagrams, hand-written SVG. Placed where the text has `[embedded content: · …]`. | + +The text may also be edited in a shared document outside the repo and exported as Markdown over +`whitepaper.md`; the export keeps the `[embedded content: …]` markers the build needs. Either way, +after a change: + +``` +node WebsiteDev/site-theseus-x/whitepaper/build/build.mjs +``` + +Open `index.html` locally to review, then publish both surfaces (the ICANN mirror does not sync +from the Sia bucket): + +``` +node Argus/src/lib/sia-upload.js WebsiteDev/site-theseus-x/whitepaper/index.html bns/theseus/whitepaper +scp WebsiteDev/site-theseus-x/whitepaper/index.html silentmode:/opt/silent-mode/site/theseus-x/whitepaper/index.html +``` + +Check `https://silentmode.st/theseus-x/whitepaper/` and `https://navigate.st/bns/theseus.x/whitepaper/` +afterwards (compare the SHA-256 with the local file; the gateway may cache a folder for a minute or two). +Commit `whitepaper.md`, `index.html` and any figure change together. + +When the whole theseus.x site is republished anyway, `scripts/mirror-bcnr-site.sh` (usage in its header) +carries the whitepaper folder along with everything else. + +## Writing rules + +These are decisions already taken for this paper; keep them. + +- Names are "digital certificate records" on Bitcoin Cash. Do not describe them as NFTs or + CashTokens; those words are fine for chats and forums, not for this paper. +- A name is neither "owned" nor "rented" by definition. The point is that the record lives on the + chain and the intermediaries between owner and record are gone. A TLD's certificate holder sets its + terms (a yearly fee, a longer term, or none). +- No list of current TLDs, no prices, no network status, no release version numbers or dates. + The paper describes how the system works and its security model, not the state of the network. +- No personal names, nicknames or contact details. The author is Silent Mode. No references to the + tools the text was written with. +- The licensing paragraph names MPL-2.0 for the clients, Apache-2.0 for Argus, CC BY 4.0 for the + documents, and points at the brand pages for the trademark terms. +- Every factual claim is checked against the source tree and the live surfaces; the closing + paragraph states the date of that check. Update that date and the byline (line 3) when a + revised text is published. + +## Diagrams + +Each figure is one `
…
` fragment with an inline SVG sized to a +760-unit-wide viewBox, using the page colours (`#e7eaf1` ink, `#8b98a9` muted, `#5e6678` dim, +`#d6ff3d` accent) and a `
`. To add one, write `build/figure-.html` and put a line +`[embedded content: ]` in `whitepaper.md` where it belongs. diff --git a/whitepaper/build/build.mjs b/whitepaper/build/build.mjs new file mode 100644 index 0000000..6960da0 --- /dev/null +++ b/whitepaper/build/build.mjs @@ -0,0 +1,95 @@ +#!/usr/bin/env node +// Builds ../index.html (the theseus.x whitepaper page) from ../whitepaper.md. +// +// node build/build.mjs # write ../index.html +// node build/build.mjs --check # exit 1 if ../index.html is not what the build produces +// +// whitepaper.md is the Markdown export of the editable document (see ../README.md). +// The page chrome lives in template.html; the two diagrams are the figure-*.html +// fragments and are placed where the export left its "[embedded content: · …]" +// marker. Markdown handled: #/## headings, paragraphs, "- " and "1. " lists, pipe +// tables, **bold**, `code`, [text](url), backslash escapes. Each paragraph is one line. +import fs from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const here = path.dirname(fileURLToPath(import.meta.url)); +const root = path.join(here, ".."); +const md = fs.readFileSync(path.join(root, "whitepaper.md"), "utf8").replace(/\r\n/g, "\n"); +const template = fs.readFileSync(path.join(here, "template.html"), "utf8"); +const figure = (name) => { + const f = path.join(here, `figure-${name}.html`); + if (!fs.existsSync(f)) throw new Error(`no figure fragment for "${name}" (expected ${f})`); + return fs.readFileSync(f, "utf8").replace(/\n$/, ""); +}; + +const esc = (s) => s.replace(/&/g, "&").replace(//g, ">"); +const unescapeMd = (s) => s.replace(/\\([\\`*_{}\[\]()#+\-.!|>~"'])/g, "$1"); + +// Inline Markdown → HTML. Code spans are protected first so nothing inside them is touched. +function inline(src) { + const codes = []; + let s = src.replace(/`([^`]+)`/g, (_, c) => { codes.push(`${esc(c)}`); return `\u0000${codes.length - 1}\u0000`; }); + s = esc(unescapeMd(s)); + s = s.replace(/\*\*(.+?)\*\*/g, "$1"); + s = s.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_, t, u) => `${t}`); + return s.replace(/\u0000(\d+)\u0000/g, (_, i) => codes[+i]); +} + +const slug = (t) => t.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, ""); +const cells = (row) => row.replace(/^\s*\|/, "").replace(/\|\s*$/, "").split(/(? c.trim()); + +const lines = md.split("\n"); +let title = "", byline = ""; +const toc = [], out = []; +for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (!line.trim()) continue; + if (line.startsWith("# ")) { title = line.slice(2).trim(); continue; } + if (line.startsWith("## ")) { + const text = line.slice(3).trim(), id = slug(text); + toc.push(` ${inline(text)}`); + out.push(`

${inline(text)}

`); + continue; + } + if (!toc.length && !byline) { byline = inline(line.trim()); continue; } + const embed = line.match(/^(?:\[|[)embedded content:\s*([a-z0-9-]+)\s*(?:·.*)?(?:\]|\\\]|])\s*$/i); + if (embed) { out.push(figure(embed[1])); continue; } + if (/^[-*] /.test(line) || /^\d+\. /.test(line)) { + const ordered = /^\d+\. /.test(line); + const items = []; + while (i < lines.length && (ordered ? /^\d+\. / : /^[-*] /).test(lines[i])) items.push(lines[i++].replace(/^(?:[-*]|\d+\.) /, "")); + i--; + out.push(`<${ordered ? "ol" : "ul"}>${items.map((t) => `
  • ${inline(t)}
  • `).join("")}`); + continue; + } + if (line.trimStart().startsWith("|")) { + const rows = []; + while (i < lines.length && lines[i].trimStart().startsWith("|")) rows.push(lines[i++]); + i--; + const head = cells(rows[0]), body = rows.slice(2).filter((r) => !/^\s*\|?\s*(:?-+:?\s*\|)+/.test(r) || true); + const tr = (cs, tag) => `${cs.map((c) => `<${tag}>${inline(c)}`).join("")}`; + out.push(`${tr(head, "th")}${rows.slice(2).map((r) => tr(cells(r), "td")).join("")}
    `); + continue; + } + out.push(`

    ${inline(line.trim())}

    `); +} +if (!title || !byline) throw new Error("whitepaper.md needs a '# title' line and a byline paragraph before the first '## ' heading"); + +// "Theseus Navigator Whitepaper" → last word in the accent colour, like the other theseus.x pages. +const titleHtml = esc(title).replace(/\s(\S+)$/, ' $1'); +const html = template + .replace("{{title}}", titleHtml) + .replace("{{byline}}", byline) + .replace("{{toc}}", toc.join("\n")) + .replace("{{article}}", out.join("\n")); + +const target = path.join(root, "index.html"); +if (process.argv.includes("--check")) { + const cur = fs.existsSync(target) ? fs.readFileSync(target, "utf8") : ""; + if (cur !== html) { console.error("index.html differs from the build of whitepaper.md"); process.exit(1); } + console.log("index.html is up to date"); +} else { + fs.writeFileSync(target, html); + console.log(`wrote ${path.relative(process.cwd(), target)} (${html.length} bytes, ${toc.length} sections)`); +} diff --git a/whitepaper/build/figure-architecture.html b/whitepaper/build/figure-architecture.html new file mode 100644 index 0000000..ae9d5fb --- /dev/null +++ b/whitepaper/build/figure-architecture.html @@ -0,0 +1,9 @@ +
    +Every client reads the chain itself; the gateway only serves bytes + +same chain +Theseus Navigatordesktop browserAriadne's ThreadWindows resolverAriadne for Androidphone browserSiriusregistrar and studio +Bitcoin Cash chainread through Electrum servers, reduced locally +Gateway, navigate.stserves Sia sites, verifies signed records +Sia storagesites, signed records, extensions +
    Architecture: four clients, one chain, one gateway.
    diff --git a/whitepaper/build/figure-resolution.html b/whitepaper/build/figure-resolution.html new file mode 100644 index 0000000..215242b --- /dev/null +++ b/whitepaper/build/figure-resolution.html @@ -0,0 +1,7 @@ +
    +Four sources build one local index; the record picks the source + +Bundled snapshotships in the buildProfile snapshotsaved after every pollElectrum delta pollbeacon history, every 30 sSia name lista further floor, rarely needed +Local name indexfirst valid REG winsUPD only from the holder +Sia site (s3)through the gateway mountOwner's server (ip)TLS fingerprint pinnedProxy and inlinep and h recordsRedirect (u)to any URL +
    Resolution: four sources, one index, four ways to serve.
    diff --git a/whitepaper/build/template.html b/whitepaper/build/template.html new file mode 100644 index 0000000..dd7988b --- /dev/null +++ b/whitepaper/build/template.html @@ -0,0 +1,146 @@ + + + + + +Whitepaper — Theseus.X + + + + + + + + + + + + +
    +
    + +
    +
    +

    {{title}}

    + +
    +
    +{{article}} +
    +
    +
    +
    + + + + +