Skip to content

feat: 单文件——每个房间文件都能离线生成新房间,同时是网站和中继服务器 - #7

Merged
Freecode100Year merged 8 commits into
mainfrom
feat/single-file
Oct 30, 2026
Merged

Freecode100Year merged 8 commits into
mainfrom
feat/single-file

Conversation

@Freecode100Year

@Freecode100Year Freecode100Year commented Oct 29, 2026 •

Copy link
Copy Markdown
Owner

概要

  • 每个房间文件都能离线生成下一代房间:新的钥匙,可设新口令。
  • 生成房间不再依赖任何网站:整个网站就是一个 index.html。

⚠️ 本分支建立在 #6(以及 #5)之上,包含它们的全部提交。直接合并本 PR,或把 feat/single-file 作为新 main,就能一次得到全部改动;#5、#6 随之不再需要单独合并。

改动

  • 每个房间文件可以创建新房间:房间文件里新增折叠的「创建一个新房间」。
    • 新文件由页面正在运行的脚本和样式字节加上新配置拼出(htmlFor),不联网,与 render(config) 逐字节一致,可用 verify-room-file.mjs 核验。
    • 默认沿用当前房间的中继,也可以改成其他中继或去中心化中继。
  • 空白文件就是创建器:配置为 {} 或 {"relays":[…]} 的文件打开即是创建页面,负责创建房间、转换旧文件;托管在网站上时还可「下载离线版」。
  • 网站 = 这一个文件:
    • dist/hr.html 和 Pages 的 index.html 都是这个创建器文件,也负责打开 #hr1= 房间链接。
    • 删除 html-room/web.js、hr.js 和 hr.css。
    • dist/_headers 由构建生成,HTTP CSP 固定与文件相同的脚本和样式哈希。
  • 链接策略改变:空白文件的 CSP 为 wss: ws://localhost:* ws://127.0.0.1:*,网站可以打开任意中继的房间链接,不再限于站点白名单。安全上的取舍已写进 security-model.md。

测试

  • 单元测试:63 项通过;build:check 通过;Worker 探针在 wrangler dev 下 10 项通过。
  • 新增 browser-tests/generations.mjs:
    • 在断网状态下:空白创建器生成房间 A;A 生成房间 B(新钥匙、新口令,旧口令解不开);B 生成去中心化房间 C。全程没有任何网络请求,每代输出都与官方构建一致。
    • 联网后两名成员在 B 里聊天。
    • 网站可以把自己下载为离线创建器,并记住本站的中继。
  • 原有浏览器测试和 Pages 测试:全部通过。有两处断言随新的链接策略做了调整:网站现在可以打开指向任意中继的链接,也会显示自定义中继输入框。

追加:房间文件同时是服务端(node index.html)

  • 每个文件在 <!doctype html> 前的 HTML 注释里内嵌中继(html-room/serve.js,只用 Node 标准库)。浏览器跳过它;Node 把页面部分当作块注释,所以 node index.html 直接运行中继,提供 /hr/(与 Node 中继共用规则)、/bc/、/healthz,支持 --tunnel 和 --tls-cert。
  • 空白创建器运行时还会提供自身网页,一个文件就是完整网站;房间文件只当中继,不公开自己,以免泄露钥匙。
  • 浏览器生成的新文件会带上服务端部分,与官方构建逐字节一致。
  • 测试:
    • 与 Node/Worker 中继相同的 /hr/、/bc/ 探针在 node index.html 上全部通过。
    • 浏览器测试:由文件提供网站、通过它聊天,并把下载的房间文件也当中继运行。
  • 单元测试共 75 项通过;9 个浏览器测试和 Pages 测试全部通过。

https://claude.ai/code/session_01PCUwNSXQdqfDSugmFo4qmM


Generated by Claude Code

Room files gain a 更换中继 panel that re-emits the same room (master or
wrapped key untouched, no password needed) with a new relay list and exact
CSP connect-src. The hosted maker converts existing files, and the CLI gains
a retarget command. All three paths produce byte-identical output that
passes verify-room-file.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01PCUwNSXQdqfDSugmFo4qmM
1. Relay-agnostic transport (version 3 rooms)
   - Relay entries bc+wss://, nostr+wss://, mqtt+wss:// select version 3;
     classic wss:// rooms stay version 2, unchanged. Retarget converts
     between them with the same key, password and roomId.
   - html-room/mesh.js: members admit (AES-GCM frames under a master-derived
     key), number (announce, claim, earliest claim wins), list and attribute
     each other; relays only forward. Every frame goes to every relay and is
     de-duplicated by IV, so one surviving relay keeps the room together.
   - Node server and Cloudflare Worker serve /bc/<topic> plain broadcast.

2. Easier relays
   - relay/go: dependency-free Go broadcast relay, one ~6 MB binary for
     Windows, macOS, Linux, BSD, Android and OpenWrt routers; -tunnel prints
     a public bc+wss:// address via a Cloudflare quick tunnel (no domain,
     open port or root).
   - scripts/relay-quickstart.sh downloads it (SHA-256 enforced) and
     cloudflared; Relay binaries workflow publishes 13 platform builds.

3. External protocols
   - Nostr: ephemeral kind 20111 events tagged with the topic, signed with
     a per-session BIP-340 key (@noble/curves).
   - MQTT 3.1.1 over WebSocket, QoS 0, topic htmlroom/<topic>.

Tests: unit (parsing, sealing, packets, membership clashes), real relays
(Node, Go, signature-checking Nostr relay, aedes MQTT, Worker /bc/ under
wrangler dev), browser end-to-end over four relays with relay loss.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01PCUwNSXQdqfDSugmFo4qmM
Rewrites every user-facing string in the room creator and the generated
index.html in everyday words, moves relay settings into a collapsed
section, highlights the main buttons, and links the repository
(https://github.com/Freecode100Year/htmlroom.git) from both pages.
Tests updated for the new wording.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01PCUwNSXQdqfDSugmFo4qmM
Files carry an optional custom: true (on by default in the creation page
and CLI room; untick or --strict for exact relays only). Such files allow
connect-src wss: plus loopback ws, and show a relay box on the entry
screen; typed relays apply to that visit only and pick classic or
decentralized mode, with the same key, password and roomId. Web links
never show the box. Converter and CLI retarget keep or change the setting.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01PCUwNSXQdqfDSugmFo4qmM
- Room files fold a creator: a new room with a new key and optional new
  password, built offline from the page's own script and style bytes
  (htmlFor), byte-identical to a release build.
- A blank file ({} or {relays}) is the creator: the offline generator and
  the hosted site. It also opens #hr1= links (CSP wss: + loopback ws) and
  converts old files; hosted, it offers itself as an offline download.
- The separate web entry (web.js, hr.js, hr.css) is gone: hr.html and the
  Pages index.html are the creator file; _headers is generated with the
  same script and style hashes.

Tests: offline chain blank -> A -> B (new password) -> C with no network
requests and release-identical output, chat in B, site self-download;
existing suites updated for links on the site reaching any relay.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01PCUwNSXQdqfDSugmFo4qmM
Every file carries html-room/serve.js in an HTML comment before the
doctype; to Node the page is a block comment, so `node index.html` runs a
zero-dependency relay (/hr/ with the shared rules, /bc/, /healthz,
--tunnel, --tls-cert). A blank creator also serves itself, making one file
a complete site; a room file never serves itself (its key). New files
created in the browser carry the server part from document.firstChild.

Tests: the shared /hr/ and /bc/ relay probes run against node index.html;
browser test serves the site from the file, chats through it, and runs the
downloaded room file as a relay.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01PCUwNSXQdqfDSugmFo4qmM
@Freecode100Year Freecode100Year changed the title feat: 每个房间文件都能离线生成新房间,整个网站只有一个文件 feat: 单文件——每个房间文件都能离线生成新房间,同时是网站和中继服务器 Oct 29, 2026
@Freecode100Year
Freecode100Year merged commit 1f07e02 into main Oct 30, 2026
12 of 16 checks passed
@Freecode100Year

Copy link
Copy Markdown
Owner Author

已随 #8 并入 main

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants