🔥 去中心化、無中央資料庫的端對端加密即時通訊軟體,結合 Nostr 與 WebRTC,重現 MSN 經典體驗(震動、音樂狀態、離線留言) | Decentralized, end-to-end encrypted messenger with no central database, built on Nostr + WebRTC and reviving the classic MSN Messenger experience (nudges, music status, offline messages).
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
Life is short, connect buddies. 去中心化、無中央資料庫的即時通訊軟體。融合 Nostr 協議 與 WebRTC,高度還原早期 MSN Messenger 的經典互動體驗(震動、音樂狀態、離線留言),並以「零伺服器維護成本」為營運目標。 🔥 官方網站: | 🌐 在瀏覽器開啟(網頁版): | 💻 下載桌面版:GitHub Releases Cinderous 透過「狀態信令」與「巨量資料傳輸」雙軌混合網路,達成極致隱私與低延遲:查看完整 README ↗
🌐 English · English version
Life is short, connect buddies.
去中心化、無中央資料庫的即時通訊軟體。融合 Nostr 協議 與 WebRTC,高度還原早期桌面即時通訊軟體的經典互動體驗(震動、音樂狀態、離線留言),並以「零伺服器維護成本」為營運目標。
🔥 官方網站:https://vaalrl.github.io/Cinderous/ | 🌐 在瀏覽器開啟(網頁版):https://cinderous.cinderous1.workers.dev | 💻 下載桌面版:GitHub Releases
Cinderous 透過「狀態信令」與「巨量資料傳輸」雙軌混合網路,達成極致隱私與低延遲:
npub)即為全網唯一 ID,無帳號密碼。同一裝置可並存多個身分(工作/個人)。docs/SECURITY.md 殘餘)。雙軌混合網路(依連線狀態動態切換):
| 引擎 | 技術 | 定位 |
|---|---|---|
| 引擎 A:狀態與信令 | Nostr 協議(Cloudflare Workers;SQLite-backed Durable Objects) | 離線留言暫存、線上狀態廣播、WebRTC 初始 SDP 信令交換 |
| 引擎 B:巨量資料傳輸 | WebRTC 直連(P2P) | 即時互動:震動(Nudge)、動畫快遞、大檔案傳輸,繞過中繼站達成毫秒級延遲 |
資料生命週期靠 NIP-40(7 天過期)與 Ephemeral Events(Kind 20000-29999 純記憶體轉發、不寫資料庫),確保 Cloudflare 永久免費額度不被耗盡。
RelayChatBackend engine、功能高度對齊);Android 已有測試版 APK(見 Releases,Capacitor 殼,自 v0.0.14 起);🔴 它目前以 debug 憑證簽署且可被偵錯——能接上 USB 偵錯的人可讀取行程記憶體,而 session 期間私鑰必然在裡面,請勿用它處理你真正在意的對話(ADR-0335,詳見 docs/SECURITY.md)。iOS 尚未推出(另需 macOS 建置)。原生通知與背景推播待接。完整技術規格見 PRD.md,模組邊界與資料流見 ARCHITECTURE.md。
.
├── apps/
│ ├── desktop/ # Tauri + React + TypeScript(第一優先平台)
│ │ ├── src/ # React/TS 前端 UI 與瀏覽器 demo(demo.html / webrtc.html)
│ │ └── src-tauri/ # Rust:SQLite、Nostr WebSocket、WebRTC、金鑰管理
│ └── mobile/ # React Native + SQLite(輔助平台,預留)
├── packages/
│ └── core/ # 共用 TS:Nostr 事件、secp256k1 簽章、NIP-44/17/59、型別、Kind 常數
├── relay/ # Cloudflare Worker(Nostr 中繼站;Durable Objects 扇出,離線留言預留 DO SQLite)
└── docs/adr/ # 架構決策紀錄(ADR)
從核心協定到桌面/行動客戶端與企業模式皆已實作並通過測試(pnpm monorepo、TypeScript strict、全程 TDD)。摘要如下(詳見 docs/adr/ 逾 330 份決策紀錄):
核心通訊
RTCPeerConnection/ICE/TURN)語音/視訊通話、資料通道分塊檔案傳輸、雙軌降級與自動改道即時通體驗(懷舊即時通風)
企業模式(公司帳號)
組織名冊(管理者簽章)、邀請碼入職、公司帳號金鑰託管+離職接管
公司設定(歡迎詞/上下班時間、下班自動靜音)、訊息保留天數、公司儲存槽(檔案存放)
企業成員鎖公司座;桌面與行動端皆可管理(唯「企業主收儲存槽落盤」限桌面=需原生檔案系統)
測試:core 639/engine 570/desktop 714/mobile 441/relay 197/website 68/theme 37/cli 30/i18n 16/brand 4(TS 共 2,716)+ Rust,含 relay↔client 端到端整合測試與行動端 jsdom 互動測試(ADR-0328)。
可眼見:桌面客戶端可打包成安裝檔(見上「終端平台」),亦可直接在瀏覽器跑(/);另有端到端 demo.html/真實 WebRTC webrtc.html。
完整施工計畫(現況、各平台待辦、M6–M9 進階功能、相依順序):見 docs/ROADMAP.md。
使用者教學(給一般使用者:身分與鑰匙、加好友、本地密碼、換裝置三條路、搬家、常見問題):見 docs/使用手冊_User-Guide.md。
前端開發指南(給社群開發者:三層架構、重用 core/i18n、消費 ChatBackend 接自己的 UI、AGPL):見 docs/前端開發指南_Frontend-Guide.md(ADR-0074)。
需求:Node 22+、pnpm 10+(Rust 測試另需 stable toolchain)。
# 1. 取得原始碼並安裝 workspace 相依
git clone https://github.com/VaalRL/Cinderous.git && cd Cinderous
pnpm install
# 2. 驗證環境(全綠代表就緒)
pnpm -r test # 全部 TS 測試(core / engine / i18n / relay / desktop / mobile)
pnpm -r typecheck # 全部型別檢查
# 3. 起本機真實 relay,再跑桌面前端(瀏覽器開發、不需 Tauri)
pnpm --filter @cinderous/relay build:dev && pnpm --filter @cinderous/relay dev # ws://localhost:8787
pnpm --filter @cinderous/desktop dev # 前端;開 /?relay=ws://localhost:8787
想接自己的前端、加語言、做擴充或貢獻核心? 開發者入口見
.github/CONTRIBUTING.md(三層架構、環境、參與方式、規範),接前端的完整教學見docs/前端開發指南_Frontend-Guide.md。
| 任務 | 指令 |
|---|---|
| 桌面前端(懷舊即時通風格) | pnpm --filter @cinderous/desktop dev,開啟 /(另有端到端 demo /demo.html、真實 WebRTC /webrtc.html) |
| 前端建置 | pnpm --filter @cinderous/desktop build |
| 共用核心測試 | pnpm --filter @cinderous/core test |
| 中繼站測試 | pnpm --filter @cinderous/relay test |
| 本機真實 relay(開發用) | pnpm --filter @cinderous/relay build:dev && pnpm --filter @cinderous/relay dev(起 ws://localhost:8787;前端開 /?relay=ws://localhost:8787 即連真實 relay) |
| Rust 測試 | cargo test(於 apps/desktop/src-tauri/) |
| 桌面端開發(需 Tauri 工具鏈) | pnpm --filter @cinderous/desktop tauri dev |
| 中繼站本地開發 / 部署(需 wrangler) | wrangler dev / wrangler deploy(於 relay/) |
中繼站(relay/)是一個自建的最小 Nostr relay,跑在 Cloudflare Workers 上,以
Durable Object 持有所有 WebSocket 連線並做記憶體扇出。它的職責是:
message-store 邏輯已實作並測試——每收件人配額、以 #p 為索引。持久化:Cloudflare 版用 SQLite-backed Durable Object(new_sqlite_classes)、Node 自架版用內建 SQL)。WebRTC 一旦 P2P 打通,震動、檔案傳輸等資料完全走點對點,永遠不經過 relay。 Ephemeral 事件只在記憶體轉發、不落地;relay 看不到訊息明文,也看不到(經 Gift Wrap 隱藏的)社交圖譜。
# 1. 取得原始碼並安裝相依(會建立 workspace 連結,wrangler 打包時需要)
git clone <your-fork-url> cinder && cd cinder
pnpm install
# 2. 登入 Cloudflare(會開瀏覽器授權)
cd relay
npx --yes wrangler@4 login
# 3. 本地開發:在 ws://127.0.0.1:8787 起一個本機 relay
npx --yes wrangler@4 dev
# 4. 部署到 Cloudflare,取得 wss://cinder-relay.<你的帳號>.workers.dev
pnpm run deploy
⚠ 用
npx而不是pnpm dlx:pnpm 10 的建置腳本需明示同意,pnpm dlx wrangler會以ERR_PNPM_IGNORED_BUILDS中止(--allow-build在 dlx 下無效)。
部署設定在 relay/wrangler.toml:Worker 名稱、進入點 src/worker.ts、
以及 Durable Object 綁定(RELAY_ROOM → RelayRoom 類別與 migration)。wrangler 會直接打包
TypeScript,無需額外建置步驟。可在 wrangler.toml 修改 name 換成你自己的 Worker 名稱。
🔴 推上 GitHub 不會部署中繼站。 這個專案沒有任何自動部署流程——.github/workflows/
裡沒有 wrangler、也沒有 Cloudflare 憑證。中繼站永遠是由人在自己的機器上跑 pnpm run deploy
推上去的。
這表示兩件事,自架者要知道:
git status)。version 欄位(見下方「部署完怎麼確認」)。① 有多個 Cloudflare 帳號時,wrangler 會挑錯
它可能打向你另一個帳號,回一個看起來像憑證壞掉的錯誤:
Authentication error [code: 10000]
憑證沒壞,是帳號選錯了。明示帳號 ID 就好(在 Cloudflare 後台首頁右側或網址列看得到):
CLOUDFLARE_ACCOUNT_ID=<你的帳號 ID> pnpm run deploy
② 用 API token 部署時,權限要兩項不是一項
不想每次都開瀏覽器登入(或要放進自己的自動化)的話,用 API token:
CLOUDFLARE_API_TOKEN=<token> CLOUDFLARE_ACCOUNT_ID=<帳號 ID> pnpm run deploy
token 的權限要勾兩項,缺第二項會得到和上面一模一樣的 10000 錯誤:
| 權限 | 為什麼需要 |
|---|---|
| Workers Scripts: Edit | 上傳 Worker |
| User → Memberships: Read | wrangler 會查你在該帳號的角色 |
部署指令回報成功不等於站真的活著。實測兩件事:
curl https://cinder-relay.<你的子網域>.workers.dev/healthz
# 預期:ok
curl -H "Accept: application/nostr+json" https://cinder-relay.<你的子網域>.workers.dev
# 預期:一份 JSON,其中 "version" 是你剛部署的版號
/healthz 回純文字 ok。不要用首頁判斷——合體模式下首頁本來就會回網頁版,
看它回不回 HTML 是判斷不出中繼站死活的。
NIP-11 的 version 欄位是確認「線上跑的是不是我剛推的那一版」的唯一方式。
想要「一個網域、一次部署」的話,多打一個指令就好:
pnpm run deploy:unified # 在 relay/ 目錄下
它會先建置網頁前端(apps/desktop,mode=unified)再連同中繼站一起部署。兩者的差別:
| 指令 | 結果 | 適合誰 |
|---|---|---|
pnpm run deploy | 只有中繼站(預設) | 想跑一座公共錨點的人 |
pnpm run deploy:unified | 中繼站 + 網頁前端 | 自架給自己和朋友用的人 |
🔴 合體部署必須設 run_worker_first = true([env.unified.assets] 裡已經設好了)。
Cloudflare Static Assets 預設是資產優先:請求先比對資產清單,命中就直接回檔案,
Worker 的程式碼根本不會執行——而中繼站的入口 / 正好命中 index.html。少了這一行,
/ 會回 200 與一段 HTML,客戶端不會看到錯誤,只會看到握不了手的 WebSocket。
同樣的理由,合體部署後請用 GET /healthz(純文字 ok)判斷中繼站死活,不要再看 /。
relay 講標準 Nostr 的 REQ / EVENT / CLOSE 協定,可用 @cinderous/core 的 RelayClient:
import { RelayClient, createHeartbeat, generateSecretKey } from "@cinderous/core";
const ws = new WebSocket("wss://cinder-relay.<你的帳號>.workers.dev");
const sk = generateSecretKey();
const client = new RelayClient(
{ send: (data) => ws.send(data) },
{ onEvent: (subId, event) => console.log("收到", subId, event) },
);
ws.addEventListener("message", (m) => client.receive(m.data));
ws.addEventListener("open", () => {
client.subscribe("presence", [{ kinds: [20000] }]); // 訂閱上線心跳
client.publish(createHeartbeat(sk)); // 廣播自己的心跳
});
桌面/前端可把這個 wss:// 位址設定為連線端點(見 apps/desktop/src/relay-source.ts)。
Ephemeral 心跳會隨上線人數扇出,請參考 docs/adr/0006
的容量估算(免費層約可支撐數十位並行使用者,並列出心跳間隔/合併/抖動等可調旋鈕)。
離線留言需要持久化。DO 已是 SQLite-backed(wrangler.toml 的 new_sqlite_classes);於 worker.ts 把
RelayCore 接上以 DO SQLite 為後備的 MessageStore(行為已由 SDK @cinderous/client/relay 的 message-store
定義並測試:NIP-40 過期、每收件人配額、以 #p 為索引)。詳見
docs/adr/0005。
本專案的開發硬規則如下:
PRD.md,模組與資料流看 ARCHITECTURE.md。v2、new_*、*_enhanced 平行路徑。歡迎參與!送 PR 前請留意:
PRD.md(產品規格)、ARCHITECTURE.md(模組與資料流)與 docs/adr/(既有決策)。pnpm -r typecheck 與 pnpm -r test 全綠(CI 也會檢查)。docs/adr/0000-template.md)。提交即表示你同意以本專案授權(AGPL-3.0)釋出你的貢獻。
這是一套處理端到端加密與私鑰的軟體,目前仍在開發階段、尚未經過安全稽核,請勿用於高風險場景。
發現安全問題請以私下管道回報,不要開公開 issue。完整的安全政策、加密盤點、威脅模型逐項盤點與已知限制清單見 docs/SECURITY.md。
本專案採用 GNU Affero General Public License v3.0(AGPL-3.0)——詳見 LICENSE。
這代表你可以自由使用、修改與散布;但若你散布修改版,或將修改版作為網路服務(例如自架本中繼站)提供給他人, 就必須以相同授權公開你的原始碼(AGPL 第 13 條)。此選擇是為了確保所有衍生版本與自架的 relay 都對使用者保持透明、可審計,貫徹本專案的隱私與去中心化精神。
Copyright (C) 2026 Cinderous contributors.
官網使用的 Manrope 字型以 SIL Open Font License 1.1 授權,授權全文見 apps/website/src/fonts/OFL.txt。
Cinderous 是獨立的開源專案,與 Microsoft 或其他即時通訊軟體的廠商沒有任何關聯,也未獲其背書。文中提及的產品名稱與商標屬於各自的所有人。