Hyperuplink:像 1998 年那樣討論
介紹 Hyperuplink,一套現代化的 HTML5/CSS3、100% 無需 JavaScript 的網路佈告欄軟體,以單一執行檔的形式提供,支援多數主流平台,無需任何執行期依賴,並採用 PostgreSQL。

在 2400 鮑數據機刺耳的交握聲、處於 Turbo 模式的電腦穩定嗡鳴,以及凌晨三點 CRT 螢幕蒼白的閃爍之間,網際網路似乎曾擁有某種東西,而在過去二十多年裡,它已徹底失去:那就是靈魂。由真實的人們組成的社群圍繞著只需一次請求就能載入的佈告欄聚集,在那裡,一般的硬體就能為你呈現整個世界,而不必為了顯示一份主題清單就占用你一半的 CPU。如果你恰好經歷過歷史上那段短暫而奇特的時刻,你大概至今仍對那段日子懷有好感,還記得你曾發現的網路上那些古怪的小角落、在其中流連的時光,或許還有沿途結識的朋友。
Hyperuplink 就是我試圖將那些回憶與附著其上的情感裝瓶,並注入到在 2026 年依然合理的東西中的嘗試。它是一個現代化的網路佈告欄,不需要 Telnet,直接在瀏覽器中運作,在伺服器端渲染正統、現代的 HTML5 與 CSS,100% 無需 JavaScript,並以單一靜態連結的執行檔形式發布,沒有外部執行環境、沒有直譯器、沒有 FastCGI、沒有 /var/www、沒有 node_modules,也不會在你的硬碟上散落任何零散檔案。它可以與 PostgreSQL 伺服器或整個叢集溝通,能運用任何 Redis 相容的快取,並內建一整套懷舊與現代兼具的主題。
更重要的是,Hyperuplink 很有趣,它不會把自己看得太嚴肅,是為所有已經受夠了與 phpBB 的執行環境搏鬥、或是 Discourse 那破爛 JavaScript 介面的人準備的論壇軟體。沒錯,Hyperuplink 也真的會狠狠鞭打駱駝的屁股。
===========================================================================
NOW DIALING ... :: CARRIER DETECTED
===========================================================================
█ █ █ █ ████ █████ ████ █ █ ████ █ █████ █ █ █ █
█ █ █ █ █ █ █ █ █ █ █ █ █ █ █ ██ █ █ █
█████ ███ ████ ███ ████ █ █ ████ █ █ █ █ █ ███
█ █ █ █ █ █ █ █ █ █ █ █ █ ██ █ █
█ █ █ █ █████ █ █ ███ █ █████ █████ █ █ █ █
::: A SUPER HIGH SPEED INTERNET BULLETIN BOARD AS SINGLE BINARY :::
===========================================================================
但是……為什麼?
簡短來說,對於那些一直掛在社群頻道上的人,或是已經在稍早的狀態更新中讀過背景故事的人,當時我想要為使用我所打造的任何工具、程式與服務的人建立一個社群討論論壇,卻找不到任何一套我真正願意忍受的軟體。
我在尋找一個像是當年那種美好舊時光 BBS 系統的網路論壇,但又能讓人們舒舒服服地用現代瀏覽器使用。我還希望它能……
- 能使用現有的資料庫資料表來驗證使用者,及/或……
- 支援簡單的註冊,最好能用 XMPP JID 取代電子郵件地址
- 支援透過電子郵件、最好也能透過 XMPP 進行通知與回覆
- 輕量,不會拖著一堆執行期依賴
- 不要求使用者啟用 JavaScript
- 不會用一堆我大概永遠不會用到的管理功能把我埋起來
- 佈景主題相對容易自訂,更重要的是,長期維護也相對輕鬆
phpBB 是最顯而易見的第一站,因為它已經存在數十年,而且不像 Discourse 和 Lemmy 那樣強迫訪客吞下 JavaScript。但 phpBB 是個攜帶過多功能的怪獸,安裝與設定都要花時間,一旦把它的擴充套件和執行期依賴算進去,就會要求一種週期性的維護儀式,而老實說我根本沒那個時間。Discourse 和 Lemmy 則是從一開始就不在考慮範圍,因為它們在未啟用 JavaScript 的情況下根本無法運作。我看過的其他東西要不是缺少我需要的功能,就是會帶來類似的執行期麻煩,不然就是得讓我去 fork 再永遠維護那個 fork,才能得到幾個我需要的功能。所以我做了那個理性的、心理健全的決定,在去年年底開始自己寫一套佈告欄軟體。
技術雜談
來用 Go 吧!
在寫下任何一行程式碼之前,我先坐下來衡量了那些常見的選項:PHP 配 Laravel、Python 配 Django、Elixir 配 Phoenix、Go 配 Fiber,以及 Zig 配 Jetzig。我連一秒鐘都沒考慮過伺服器端的 TypeScript,因為 Node.js 和 NPM 生態系根本是個充滿惡意軟體的垃圾場,我連為了像 Hyperuplink 這種刻意搞得有點荒謬的東西都不想選它。
這些直譯式技術堆疊讓網頁開發變得愉快,它們把繁瑣的 HTTP、Session 和表單處理都抽象掉了,讓你可以專注在你正在打造的東西上,但每一個都會在背後拖著執行環境與維護負擔。我對 Hyperuplink 的唯一目標,就是讓業餘的管理員也能在不需要全天候照顧整個技術堆疊的情況下經營一個佈告欄。從管理的角度來看,我希望只要偶爾更新一兩個執行檔就好,而不必去訂閱什麼 PHP 安全公告、它的郵件清單、GitHub 上的安全通報,還有 NVD,只為了確保自己沒有漏掉那成千上萬個依賴套件中的某個重大 CVE。
Go 恰好落在甜蜜點上,介於像 C、C++ 和 Zig 這類以開發速度換取效能的低階編譯語言,與像 PHP 和 Python 這類讓資料結構操作很愉快但執行成本高昂的直譯語言之間。決定性的因素是 Go 能編譯成單一靜態連結的執行檔,你只要複製到任何 VPS 上就能直接執行。唯一的缺點是 Go 並不完全算是「原生網頁」語言,也沒有像 Django 或 Laravel 那樣能加速處理無聊細節的東西,所以我在Fiber v3 框架之上自己打造了一個小型的網頁應用程式框架,然後就從那裡開始了。
引擎蓋底下
Hyperuplink 是單一靜態執行檔,以停用 CGO 的方式編譯,並交叉編譯至 Linux、macOS、FreeBSD、NetBSD、OpenBSD 以及一長串的架構,因此部署不過就是把那個執行檔複製到定位而已。它原生支援 PostgreSQL、對叢集友善,並使用具體化檢視來最佳化讀取效能。結構描述遷移已內嵌其中,會在啟動時自動執行,這表示沒有外部的遷移檔案,升級應該就跟直接啟動新版本一樣簡單。
此外,會使用 Redis 相容的服務來處理快取、Session 與非同步工作佇列。大頭貼、附件與自訂素材可以上傳到本機硬碟,或是任何 S3 相容的物件儲存空間(例如 MinIO),這在水平擴展服務時很有用。
Hyperuplink 完全不執行任何客戶端 JavaScript,這表示每個頁面都是由伺服器渲染的 HTML5 與 CSS,也沒有任何東西會記錄你的游標如何又飄回那篇爭論鳳梨到底該不該放在披薩上的文章,只為了收集你帳號的資料。
說到這個,帳號可以透過本地密碼註冊/登入,並可選用 TOTP 兩步驟驗證,但 Hyperuplink 也支援透過 OAuth 供應商登入,方便你把想從其他平台拉攏過來的朋友拐過來。而對於覺得電子郵件太老人的人,註冊與通知也可以透過 XMPP 完成。至於授權,帳號可以被指派到具備各分類權限的群組,讓好東西只對對的人開放。
Hyperuplink 內建一系列佈景主題,其中一些美麗的復古美學要歸功於 classic-stylesheets 專案。也有稍微現代一點的外觀可供選擇,而且每個主題的配色都可以互換,所以一個帶著 Gruvbox 色調的 Mac OS 9 風格佈告欄是完全可行的。
這個佈告欄在文章中支援 Markdown,提供大頭貼與附件上傳,為管理員提供檢舉與審核功能,而且介面支援 i18n。
Hyperuplink 還內建了 REST API,我認為它比 Lemmy 或 Discourse 所提供的更友善好用,甚至還有自己的 TUI 客戶端,透過官方的 Hyperuplink 整合至 Neon Modem Overdrive。
開始使用
Hyperuplink 在 tty.fail 上開發,並鏡像至 GitHub,而鏡像站正是預先建置好的執行檔與容器映像檔建置與託管的地方(感謝免費的 CPU 運算資源!)。無論你決定如何執行佈告欄,都需要一個可連線的 PostgreSQL 與 Redis 相容伺服器,另外,如果你想讓上傳檔案不要放在本機硬碟,也可以選擇準備一個 S3 相容的儲存空間。
原生執行
官方儲存庫包含了讓你盡快上手所需的所有文件與設定,但基本概念就是你可以直接從發行頁面抓取適合你平台的執行檔,放到你想放的任何地方,然後執行:
$ ./hyperuplink -c "file:///etc/hyperuplink.toml"
Docker / Podman
有提供完整的 Docker 與 Podman(無 root!)設定,如果你想一次把整個技術堆疊都架起來。儲存庫內附了包含 PostgreSQL 與 Valkey 以及可選 MinIO 配置的 docker-compose.yml/podman-compose.yml:
$ docker compose up -d
Quadlets
Podman 的設定能做到 Docker 設定能做的一切,只是無需 root 權限,而且除了 podman-compose.yml 之外,如果你偏好使用 systemd,還有一組 Quadlet 單元可供使用。
K8s
Kubernetes 也能用,一個帶有幾個副本、並透過 Secret 傳入設定的最小化 Deployment 就差不多是你所需要的一切。由於上傳可以使用 S3,Pod 可以保持無狀態。
Gentoo
儲存庫中提供了 Ebuild,讓你可以在自己的 Gentoo……伺服器上編譯……我想是吧。
NixOS / nixpkg
我試著把 Nix 所需的一切都放進去,但老實說,我在任何地方都沒有實際在使用它,所以請把它當成概念驗證,而不是有在積極維護的東西。如果你有意願積極維護 Nix 相關的部分,歡迎與我聯繫。
FreeBSD / OpenBSD / OpenRC / …
儲存庫中也包含了適用於 FreeBSD、OpenBSD、OpenRC 甚至 systemd 的 Service 定義所需的 init 指令稿。
從醬汁建置
如果你想自行建置 Hyperuplink,你需要 Go,做法同樣簡單:
$ git clone https://tty.fail/mrus/hyperuplink.git
$ cd hyperuplink
$ make build
自給自足的執行檔會落在 ./build/hyperuplink,隨時可以移到你想放的任何地方。
注意:好吧,好吧,我說謊了,被你抓到了。沒有執行期依賴並不 100% 正確,你現在大概正盯著一個拒絕讓使用者上傳大頭貼的論壇。原因在於 Hyperuplink 有一個執行期依賴,那就是 ImageMagick 的 convert 指令。服務必須能在其 $PATH 中找到該執行檔,大頭貼功能才能正常運作。
至於為什麼,說來話長,但長話短說就是影像處理很困難,而且沒有多少人,包括我在內,會想為了比方說 WebP 壓縮演算法,去打造一個原生的 Go 實作來重新發明輪子。因為我明確不想為了保留 Go 輕鬆交叉編譯的特性而使用 CGO,所以我決定呼叫 convert 執行檔是最合理的做法。畢竟,如果你曾在系統上架設過任何與網頁相關的東西,你很可能本來就有 ImageMagick。
反向代理
無論你最終如何執行這個處理程序,請將它放在一個會終止 TLS 的反向代理之後,因為在 Mode = "production" 模式下,Session Cookie 僅限 HTTPS,而你絕對不會想在 development 模式下執行論壇。此外,如果你想讓服務受到監管,儲存庫中已有前述適用於 systemd、Gentoo 與 Alpine 上的 OpenRC,以及 FreeBSD 與 OpenBSD 上 rc.d 的服務檔案在等著你。
EOF
Hyperuplink 以 SEGV 授權開源,程式碼可在 tty.fail 上取得,鏡像與預先建置的執行檔則在 GitHub 上,而你想閱讀的關於它的其他一切資訊,不是可以在 hyperup.link 上找到,就是在其內嵌的手冊中,位於 Help -> Manual 底下。
如果這一切聽起來正是你的菜,歡迎來聊天室打聲招呼,架好佈告欄後來炫耀一下,如果你想在開發或測試上幫把手,也歡迎與我聯繫,因為最棒的社群,永遠都是由真實的人們投入真實的心力所打造的。
隨機一篇部落格
留言
登入後參與討論