誌 開發日誌 做了什麼 · 試過什麼 · 否決了什麼 · 留下了什麼
已落地 relay 2026-W36

六個階段都打完勾,AI卻說要再查一次

20 commits 7c8a82e..50719a1 ↗

自己架一台訊令 relay,降低柴米帳對公共 Nostr relay 的依賴。六個階段全部做完之後,又讓 AI 回頭做了一次完工稽核,正式站上真的抓出一個會永久鎖死的漏洞。

AbstractSelf-hosted a WebRTC signaling relay to cut dependence on public Nostr relays. After all six build phases were marked done, a self-triggered completion audit caught a real production bug that could permanently lock the service.

那時候在想什麼

柴米帳同步靠 Trystero 借用公共的 Nostr relay 牽線,清單是套件內建的,依 appId 洗牌固定拿五個。我看著那五個名字一直不太放心,其中一台叫 staging.yabu.me,帳本配對押在陌生人的測試機上,總覺得哪天可能就連不上了。

除了這個具體的理由,還有一個更長遠的念頭。我想要有一份自己的基礎設施,降低對外部系統的依賴,以後服務多起來也能自成一個體系,不用每個新專案都重新找一次別人的公共服務。

這東西本身不複雜,我也知道,只是從來沒碰過。憑架構上的直覺,我知道這種訊令 relay 對服務要擴展的時候是有用的。

姑且一試。可是我知道自己做得到,這不是那種試了才知道行不行的東西。用 AI 輔助,我一開始想的是大概一兩個小時。

整個項目我給的是一個戰略方向,剩下的交給 AI 去弄,我負責監督。一開始就決定的是這只給自己用,不做成公共 relay,README 後來也是這樣寫的:作者自用,要用請自己架一份。選 Cloudflare Durable Object 而不是隨便一台 VPS 開 WebSocket,主要是成本考量。

實際做了什麼

docs/plan.md 這兩天被改了十四次,比任何一支程式檔都多。不是需求一直在變,是做法卡住又回頭重寫過。

NIP-11 一開始被判定不用做,理由是 Trystero 自己不讀那份文件。後來又做了,是因為打算把 repo 開源,AI 建議這一段要補全:任何 Nostr 客戶端接上一台陌生的 relay,第一件事就是問它支援什麼,沒有這份宣告就只能靠試錯發現。

六個階段全部標完之後,正式站上線,真的用 Trystero 兩端配對過。接下來 AI 說要做一次完工稽核,不是我要求的。這件事我認同,開發到一個階段,回頭做一次自我審查和項目復盤,是它自己的直覺,這個習慣我贊成。

那次稽核真的抓到東西。正式站上有一道連線上限的檢查沒有回收半開的 socket,滿了就會永久鎖死,只有重新部署救得回來。跟階段四在正式站量到的那次洩漏是同一類問題,那次只修了一半。

也是這次稽核順手驗證了我當初的擔心。柴米帳原本靠的那五台公共 relay,其中三台已經連不上或者拒收事件。哪天可能連不上,沒有等到哪天,relay 架好的當下就已經是真的。

結論

沒有做到一半放棄的東西。整體跟原本的規劃差不多,在預期範圍之內。

這個 relay 現在跑在正式站上,柴米帳已經接上了。