使用者指南

一份實用的 Town OS 上手指南——從寫入 USB 隨身碟一路講到回報錯誤。 無論你是第一次使用,或是正在開發套件的開發者, 這份指南都涵蓋了你入門所需的一切。

快速上手

幾分鐘內就能讓 Town OS 在一台閒置電腦上跑起來。你只需要一支 USB-C 隨身碟 (4 GB 以上)和一台用來寫入的 Linux 電腦。

1. 寫入 USB 隨身碟

在任何一台裝有 podmancurlbzip2ddlsblktar 的 Linux 機器上執行安裝指令稿—— Podman 是必要的,因為指令稿要用它來拉取安裝程式映像檔 (而且必須真的能運作,光是裝好還不夠)。它會下載最新的 Town OS 映像檔, 並直接寫入隨身碟。

curl -sSLO https://town-os.github.io/install.sh && bash install.sh

指令稿會掃描已連接的 USB 裝置,列出裝置名稱與容量讓你挑選。確認之後, 它會一次把壓縮映像檔串流解開並寫入該裝置。整個過程依你的網路速度而定, 大約需要幾分鐘。

Raspberry Pi(4 / 400 / CM4、5 / CM5):傳入 RPI=1, 即可把原生開機的 Raspberry Pi 映像檔寫入 SD 卡、隨身碟或 NVMe,而不是標準的 PC 映像檔:

curl -sSLO https://town-os.github.io/install.sh && RPI=1 bash install.sh

Pi 映像檔一律為 64 位元 Arm,且不透過 UEFI 開機,因此就算你是在 x86_64 機器上 執行安裝程式也沒問題。Raspberry Pi 會直接從插入的卡片或磁碟開機,沒有開機選單; 若要以 Pi 5 的 NVMe 開機,請確認開機載入器 EEPROM 的開機順序包含 NVMe (用 rpi-eeprom-config 設定)。

資料銷毀警告

Town OS 會格式化並使用所有偵測到的本機儲存裝置 (NVMe、SATA、SAS、SD 卡)。在一台機器上開機執行 Town OS,將會 永久銷毀每一顆內接硬碟上的所有既有資料

請只在沒有任何你想保留資料的專用機器上啟動 Town OS;或者先依 虛擬機器說明安全地試用——包括 make qemu-usb,它會在 QEMU 中以唯讀方式 開機你剛寫好的隨身碟,完全不會碰到任何真實硬碟。

2. 從 USB 開機

把隨身碟插進目標電腦並從它開機。你可能需要在開機時按某個按鍵才能進入開機選單—— 常見的按鍵有 F12F2EscDel,視你的硬體而定。

3. 首次開機:使用 sledgehammer

如果目標機器先前有其他用途,請在第一次開機時從開機選單 選擇 sledgehammer 選項。sledgehammer 會抹除所有偵測到的儲存裝置, 確保 Town OS 從一塊完全乾淨的白板開始。這能避免前一個作業系統留下的 分割表、檔案系統或 RAID 中繼資料造成問題。

sledgehammer 完成後,機器會自動重新開機,Town OS 接著進行全新的儲存設定。 完整說明請見 Sledgehammer 開機選項

4. 開機時會發生什麼

Town OS 會從隨身碟完整載入記憶體,接著啟動 ttyforce—— 一個互動式文字介面安裝程式,直接在這台機器的主控台上引導你完成設定。

  • 網路設定——ttyforce 會偵測可用的網路介面。若有已接上的有線網路,它會自動繼續;否則會列出 WiFi 網路供你選擇,顯示訊號強度、加密方式,並可輸入 WPA2/WPA3 密碼。
  • 磁碟佈建——ttyforce 依類型與容量將偵測到的磁碟分組,再自動選擇合適的 RAID 等級:1 顆用單碟模式、2 顆用 RAID1(鏡像)、3 顆以上用 RAID5(含同位元檢查的分散)。所有儲存都使用 btrfs。
  • 匯入 SSH 金鑰——你可以輸入 GitHub 使用者名稱來匯入公開金鑰,以便安全地遠端存取。

佈建完成後系統會重新開機,ttyforce 切換到 getty 模式——在主控台上即時顯示服務健康狀況、 系統指標與 journal 輸出。你可以從這裡登入、重新設定網路,或觸發一次 sledgehammer 抹除。

5. 建立帳號,開始使用

在同一個網路上的任何裝置開啟瀏覽器,前往 http://town-os.local。如果打不開,請到路由器的 DHCP 用戶端清單中 找到名為「town-os」的裝置,直接使用它的 IP 位址。系統會提示你建立一個管理者帳號。 登入之後,你就能在儀表板上安裝套件、管理儲存並設定服務。

6. 把路由器的 DNS 指向 Town OS

Town OS 內附 rolodex, 這個 DNS 伺服器負責解析套件的主機名稱,其餘查詢一律轉送上游。 要讓網路上的每台裝置都自動使用它,請給 Town OS 機器一個固定 IP (或 DHCP 保留位址),並把路由器的主要 DNS 伺服器設為該位址。 各廠牌的操作方式與驗證步驟請見 在路由器上設定 DNS

從原始碼建置 USB 映像檔

Town OS 的 USB 映像檔由 install 儲存庫建置。建置流程會產生一個可開機的映像檔,根檔案系統為 squashfs,分割配置為 GPT。

先決條件

你需要一台 Linux 主機和一支隨身碟(4 GB 以上)。建置使用 Arch Linux 的工具 (pacstrapmkinitcpioarch-chroot), 但你不需要執行 Arch——在其他發行版上,make image 會自動在同架構的 Arch 容器裡執行建置。請用對應你發行版的目標安裝主機相依套件:

  • make deps——Arch(及其衍生版)或 Fedora/RHEL
  • make deps-debian——Debian 或 Ubuntu

它們會裝上建置與虛擬機器工具所需的一切——makepodmanarch-install-scriptssquashfs-toolspartede2fsprogsdosfstoolsqemulibvirt

複製與建置

git clone https://gitea.com/town-os/install.git
cd install
make image

執行 makemake image 即可建置完整的 USB 映像檔。 這個流程會下載基礎系統、安裝 Town OS 元件、把所有內容壓縮成 squashfs 檔案系統, 並組出帶 GPT 分割表的最終磁碟映像檔。

分割與 squashfs

產生的映像檔採用 GPT 配置,包含:

  • 一個 BIOS 開機分割區(1 MiB),供傳統 BIOS 開機使用
  • 一個 EFI 系統分割區(64 MiB,FAT32),供 UEFI 開機使用
  • 一個資料分割區(ext4),存放 squashfs 根檔案系統與 GRUB

開機時,Town OS 會把 squashfs 掛載為唯讀的下層,並在其上疊一層 tmpfs。 這表示作業系統完全在記憶體中執行——隨身碟本身只在開機時被讀取。 所有執行期的變更都發生在記憶體裡,重新開機後即被丟棄,每次都給你一塊乾淨的白板。

寫入映像檔

最簡單的方式是 make flash:映像檔過期時會自動重建,然後寫入 USB 裝置。 若要手動處理,請注意建置產物帶有日期與架構後綴(例如 town-os-2026-07-25-x86_64.img)——用 dd 寫入即可:

# 找出你的 USB 裝置(例如 /dev/sdb)
lsblk

# 寫入映像檔(把檔名與 /dev/sdX 換成你自己的裝置)
sudo dd if=town-os-YYYY-MM-DD-x86_64.img of=/dev/sdX bs=4M status=progress conv=fsync

請再三確認目標裝置——dd 會毫不詢問地覆寫你所指定的任何裝置。

開機流程
USB 隨身碟
GPT 分割表
EFI 系統分割區 squashfs 根
開機
執行中的系統
tmpfs 疊加層 可讀寫
squashfs 唯讀
記憶體
作業系統完全在記憶體中執行

Sledgehammer 開機選項

Town OS 的隨身碟包含一個 sledgehammer 開機選項,它會抹除所有偵測到的 儲存裝置,把系統重設為乾淨狀態。當你想重頭來過時很好用——例如測試完之後、 重新部署到新硬體之前,或是儲存已經毀損的時候。

它會做什麼

當你在開機時選擇 sledgehammer 選項,Town OS 會:

  • 偵測所有本機儲存裝置(NVMe、SATA/SAS、SD 卡)——與正常開機時相同的偵測邏輯
  • 抹除每一個偵測到的裝置上的分割表與檔案系統
  • 重新開機,接著像第一次開機那樣重新完成儲存設定

sledgehammer 會銷毀所有偵測到的儲存裝置上的全部資料。隨身碟本身不受影響—— 只影響內接磁碟。使用這個選項前,請確認你需要的東西都已備份。

如何使用

  1. 把 Town OS 隨身碟插入目標機器並從它開機
  2. 在開機選單中選擇 sledgehammer 項目,而不是預設的開機選項
  3. 系統會抹除所有偵測到的儲存並自動重新開機
  4. 下次開機時,Town OS 會依你的 town-os.yaml 設定從頭建立儲存

什麼時候該用

  • 回復原廠狀態——讓系統回到剛部署時的乾淨狀態
  • 更換儲存後端——從 btrfs 換成 ZFS(或反過來)必須先清空既有儲存
  • 儲存毀損——若檔案系統已損壞到無法修復,sledgehammer 能給你一個乾淨的起點
  • 重新部署——把硬體改用於另一套 Town OS 設定
Sledgehammer 重設流程
開機選單
選擇 sledgehammer
sledgehammer
抹除
清空儲存
抹除所有偵測到的磁碟
NVMe SATA SD
重新開機
全新設定
從頭設定儲存
town-os.yaml

在路由器上設定 DNS

Town OS 內附 rolodex, 這個 DNS 伺服器為你的套件管理權威區域,並轉送上游查詢。若要把它當成整個網路的 DNS 伺服器,你需要讓路由器把 Town OS 機器的 IP 位址當作 DNS 伺服器發送給網路上的裝置。 這樣每台裝置都會自動用 rolodex 解析 DNS——不必逐台設定。

找出 Town OS 的 IP 位址

你需要知道 Town OS 機器的區域網路 IP 位址。可以這樣找到:

  • 首次開機設定完成後,登入 http://town-os.local 的儀表板,取用其中顯示的內部 IP——這就是要拿來當 DNS 伺服器的位址
  • 若你用的是虛擬機器,執行 make vm-ip
  • 到路由器的 DHCP 用戶端清單中尋找名為「town-os」的裝置
Town OS 儀表板顯示外部 IP 與內部 IP,並附有複製按鈕

為了讓 DNS 穩定運作,Town OS 機器應該要有固定 IP 位址,或在路由器上設定 DHCP 保留。 萬一 IP 改變,整個網路的 DNS 都會壞掉。

指派固定 IP 或 DHCP 保留

大多數路由器都允許你依 MAC 位址替特定裝置保留 IP。這項設定通常在 LAN 設定DHCP位址保留 之下。 在用戶端清單中找到 Town OS 機器,把它目前的 IP 保留下來即可。

如果你的路由器不支援 DHCP 保留,也可以在 town-os.yaml 中加上設定, 直接在 Town OS 機器上設定固定 IP。

在路由器中變更 DNS 伺服器

各廠牌路由器的步驟略有不同,但大方向都一樣:

  1. 登入路由器的管理介面(通常是 192.168.1.1192.168.0.1
  2. 找到 DHCP 設定LAN 設定DNS 設定 區塊
  3. 主要 DNS 伺服器改成 Town OS 機器的 IP 位址
  4. 可以再設一個次要 DNS 伺服器作為備援(例如 1.1.1.18.8.8.8)——當 Town OS 無法連線時會改用它
  5. 儲存並套用設定

儲存之後,網路上的裝置會在下次更新 DHCP 租約時取得新的 DNS 伺服器。 你也可以中斷再重新連線,或重新啟動裝置來立即生效。

常見路由器介面

路由器廠牌DNS 設定所在位置
ASUS(華碩)內部網路 → DHCP 伺服器 → DNS 伺服器
TP-LinkDHCP → DHCP 設定 → 主要 DNS
NetgearInternet → 網域名稱伺服器(DNS)位址
Linksys連線 → 本地網路 → DHCP 伺服器 → 靜態 DNS
UniFi設定 → 網路 →(你的網路)→ DHCP Name Server
pfSense / OPNsenseServices → DHCP Server → DNS Servers
OpenWrt網路 → 介面 → LAN → DHCP 伺服器 → 進階設定 → DHCP-Options:6,<town-os-ip>

關閉瀏覽器內建的 DNS

大多數瀏覽器都自帶 DNS 解析器,會完全繞過你的路由器。 Firefox 的 DNS over HTTPS(DoH),以及 Chrome 與 Edge 中對應的「安全 DNS」功能, 都會把查詢直接送到 Cloudflare、NextDNS 之類的公共供應商,因此 Town OS 根本看不到 這些查詢,你的套件名稱就會顯示「找不到伺服器」,即使網路上其他部分都解析正常。 請在每一台需要以名稱存取你服務的裝置上關掉它:

  • Firefox——設定 → 隱私權與安全性 → DNS over HTTPS,選擇關閉。(預設保護也可能悄悄啟用 DoH,所以請明確選「關閉」,別留在預設值。)
  • Chrome——設定 → 隱私權和安全性 → 安全性,關閉使用安全 DNS
  • Edge——設定 → 隱私權、搜尋和服務 → 安全性,關閉使用安全 DNS 指定如何查閱網路位址
  • Brave、Vivaldi、Opera——與 Chrome 相同,位於隱私權和安全性 → 安全性之下。
  • Safari——沒有內建 DoH,使用系統解析器。請到系統設定 → 一般 → VPN 與裝置管理確認沒有安裝 DNS 設定描述檔。

你可能設定過的任何系統層級加密 DNS 也一樣——Android 的私人 DNS、 iOS/macOS 的 DNS 描述檔,或本機的 systemd-resolved/dnscrypt 設定。這些都會蓋過路由器發出的 DNS 伺服器。如果你想要加密的上游 DNS,交給 Town OS 就好: rolodex 本身就能對上游使用 DoH/DoT,同時仍然回答你的本機名稱 (見下方選擇解析模式)。

驗證是否生效

當裝置取得新的 DNS 設定之後,驗證一下 rolodex 是否真的在處理你的 DNS 查詢:

# 檢查你的機器正在使用哪個 DNS 伺服器
nslookup example.com

# 或直接向 Town OS 查詢
dig @<town-os-ip> example.com

# 查詢套件網域(前提是你已安裝套件)。
# 名稱格式為 <name>.<repo>.<tld> —— .home 是預設網路的 TLD
dig @<town-os-ip> gitea.default.home

如果 example.com 能正確解析,就表示 rolodex 已經運作, 並在為你的網路處理 DNS。

選擇解析模式

不屬於你任何一個網路的名稱,會依解析模式處理; 該模式在儀表板的 設定 → DNS 解析 中設定。共有三種模式:

  • 自動(建議)——預設值。先嘗試從根伺服器開始逐層解析,失敗後依序退回 DoH/DoT、本機轉送器,最後是公共解析器,並沿用最近一次成功的層級。這讓網路條件允許時保有遞迴解析的隱私,在會過濾對外 DNS 的網路上也能優雅降級。
  • 僅遞迴——一律從根伺服器逐層解析,不做任何退回。可保證查詢絕不交給第三方解析器,但在封鎖或劫持對外 53 埠的網路(旅館、需登入的公共網路、部分 ISP)中,所有外部名稱都會失敗。
  • 轉送——一律把未比對到的查詢送往上游解析器(預設為 Google Public DNS)。這就是傳統的轉送行為。

請注意,這項設定講的是本機如何連上網際網路。而另一個方向的加密 DNS—— 也就是你自己的裝置用來連到本機的方式——會在下面說明。

從你的裝置使用加密 DNS

Town OS 不只自己使用加密 DNS,也把它提供給你自己的裝置。 共有三個可用端點,它們解析的名稱與 53 埠上的一般 DNS 完全相同:

  • DNS over HTTPS(DoH)——https://dns.<tld>/dns-query。它經由 ingress 走標準 HTTPS 埠,因此在限制嚴格的網路中最有機會可用。
  • DNS over TLS(DoT)——dns.<tld>853 埠。
  • DNS over QUIC(DoQ)——dns.<tld>853 埠,走 UDP。

這三者出示的憑證都由本機自己的憑證授權單位簽發,所用的名稱與位址和系統其餘部分一致。 如果你已經在裝置上安裝了根 CA(參見 信任憑證授權單位),它們不需要任何額外設定即可通過驗證。

從未信任過你的 CA 的裝置一樣可以驗證這些端點。 Town OS 會在它具權威的區域中發布 DANE 記錄來釘選該憑證,分別位於 _853._tcp.dns.<tld>_853._udp.dns.<tld>—— 每種傳輸各一筆,因為支援 DANE 的用戶端若找不到自己所選傳輸對應的記錄,就會直接以失敗收場。 由於該裝置本來就連得到這台解析器,它就能取得這份釘選資訊, 從而在不預先安裝任何東西的情況下,驗證自己收到的憑證。

支援 DDR 的用戶端會自行找到這一切。Town OS 會在 _dns.resolver.arpa(RFC 9462)發布指定記錄,依優先順序列出 DoH 的 URL 以及 DoT 和 DoQ 的埠——DoH 排在最前,因為 443 埠能穿過那些封鎖 853 的過濾。 只要把支援 DDR 的裝置指向本機做一般 DNS,它就會自己發現並升級到加密端點。

這三個監聽器是在開機時由安裝映像檔的設定開啟的,因此與絕大多數 DNS 設定不同,它們無法從儀表板開啟。 其背後的憑證會在本機僅僅處於執行狀態時於背景續期——不需要重新開機—— 而且新的 DANE 釘選會在舊的撤下之前先行發布,因此不存在任何會讓驗證型用戶端拒絕連線的時間空窗。

封鎖惡意網域

rolodex 可以拿公開的封鎖清單篩檢每一次查詢,讓你不必另外架 Pi-hole 就能獲得全網路的威脅封鎖。相關設定在 DNS → 封鎖清單 中管理。 供應商分為兩類,作用的對象並不相同:

  • DNSBL(網域封鎖清單)——比對被查詢的名稱。真正影響日常瀏覽的是這一側。
  • RBL(即時黑洞清單)——比對 IP 位址,而且只在反向 DNS 查詢時才會用到。

兩者都預設停用,且供應商清單為空,並且都是即時查詢的: Town OS 從不下載、剖析或預先快取任何封鎖清單來源。在你啟用某一類並至少加入一個區域之前, 什麼都不會被檢查。

DNSBL 查詢是怎麼運作的

被解析的名稱會接在供應商區域之前,當成一次普通的 DNS 查詢送出。 在啟用 dbl.spamhaus.org 的情況下解析 badsite.example, 實際會送出對 badsite.example.dbl.spamhaus.org 的查詢。

  • 有回應就代表被列入——對於被列入的名稱,供應商會回傳一筆位址記錄(通常是 127.0.0.x)。rolodex 接著會對發問的裝置回傳 NXDOMAIN
  • NXDOMAIN 代表乾淨——解析照常進行。出錯或逾時的供應商一律視為列入,因此無法連線的封鎖清單絕不會讓你的網路斷線。
  • 封鎖清單贏得過外面的世界,但永遠贏不過你自己的記錄——檢查發生在本機記錄與套件記錄之後(所以 gitea.default.home 一定解析得到),但在上游快取與轉送器之前,因此即使轉送得到的答案已被快取,被列入的名稱仍會遭拒。
  • 依名稱比對,而非依後綴——doubleclick.net 被列入並不會連帶封鎖 stats.g.doubleclick.net,除非供應商也把那個名稱列進去。要不要為了一台主機封掉整個網域,該由你決定,而不是由清單來決定。
  • 結果會短暫快取——命中依供應商的 TTL 快取,乾淨的結果則快取五分鐘。

DNSBL 供應商

以下是儀表板中可一鍵加入的選項。它們都仍在營運、免費,而且會回應會自行遞迴的解析器、 不需要註冊——而 Town OS 正是這樣一台機器。你也可以自行輸入任何其他區域。

供應商區域針對的目標
Spamhaus DBLdbl.spamhaus.org使用最廣泛的網域清單。收錄在垃圾郵件中出現的網域,以及釣魚、代管惡意程式與殭屍網路指揮控制的網域。五者之中涵蓋面最廣。
SURBLmulti.surbl.org彙整出現在未經請求訊息內文中的網域——釣魚網站、惡意程式、遭入侵的網站,以及被濫用的轉址服務與短網址。
URIBLblack.uribl.com在垃圾郵件內文中出現的 URI。black 區域偏保守:只收錄確實活躍於垃圾郵件的網域,對誤判的容忍度很低。
NordSpam DBLdbl.nordspam.com在垃圾郵件中出現的網域,資料來源獨立於上述清單——主要適合當作第二意見,而非主力清單。
Spam Eating Monkeyuribl.spameatingmonkey.net從垃圾郵件 URI 擷取出的網域,包含新註冊以及用過即丟的短命網域。

RBL 查詢是怎麼運作的

IP 位址會被反轉,然後接在供應商區域之前。zen.spamhaus.org 檢查 192.168.1.100,送出的查詢會是 100.1.168.192.zen.spamhaus.org。IPv6 位址則展開成半位元組並以相同方式反轉。 有回應代表被列入、NXDOMAIN 代表乾淨,快取方式與 DNSBL 完全相同。

但有個前提:RBL 區域只會用在反向 DNS 查詢(in-addr.arpaip6.arpa)中出現的 IP 上,而一般網頁瀏覽幾乎不會產生這類查詢。 這些清單存在的意義,是讓郵件伺服器拒絕某個連進來的寄件方,那才是它們真正發揮價值的場合。 在家用路由器上,它們幾乎等於什麼都沒做——真正影響瀏覽的是上面的 DNSBL。 如果你在 Town OS 後面跑郵件服務,那就啟用它們;否則值得你留意的是網域清單。

RBL 供應商

供應商區域針對的目標
Spamhaus ZENzen.spamhaus.org一次查詢涵蓋四個 Spamhaus 清單:SBL(已確認的垃圾郵件來源)、CSS(雪鞋式垃圾郵件操作)、XBL(遭入侵與受感染的機器、開放代理)以及 PBL(本來就不該直接寄信的浮動與終端使用者位址範圍)。
SpamCopbl.spamcop.net由 SpamCop 使用者通報網路回報的 IP。通報停止後條目會自動到期,所以反應快、遺忘也快。
PSBLpsbl.surriel.comPassive Spam Block List:被垃圾郵件誘捕位址逮到的 IP,條目會自動到期。刻意保守,收錄量不大。

刻意不提供的清單

有三個知名區域被刻意排除在一鍵加入清單之外,因為它們都會無聲地失效—— 你會看到一個已設定的供應商,於是以為自己受到保護。若你清楚自己在做什麼,仍可手動加入。

  • SORBSdnsbl.sorbs.net)——已於 2024 年 6 月 5 日停止服務,區域也被清空。它照樣會回應,只是永遠不會列出任何東西:看起來像保護的永久空操作。
  • Barracudab.barracudacentral.org)——免費,但必須先註冊發出查詢的 IP。未註冊的機器可能先能用一陣子,然後被無預警切斷。
  • UCEPROTECT 第 2、3 級——以整個網段與 ASN 為單位收錄,因此你的 ISP 上出現一個壞鄰居,整個 ISP 都會被封。

本機項目與允許清單

  • 本機項目DNS → 封鎖清單)——手動封鎖某個特定網域或 IP,並在旁邊記下理由。網域項目會讓正向查詢回傳 NXDOMAIN,且立即生效。這項檢查排在所有外部供應商之前,也正是你不必訂閱整份清單、就能封掉某一台特定主機的方法。
  • 允許清單DNS → 允許清單)——誤判時的逃生出口。被列入允許清單的名稱會直接跳過整個以名稱為基礎的檢查:既不比對 DNSBL 供應商,也不比對你的本機項目,更不會為它發出任何供應商查詢。與封鎖不同,放行依後綴比對的——放行 vendor.example 也會一併放行 cdn.vendor.example。項目只能是名稱、絕不能是 IP,因此反向 IP 的 RBL 路徑完全不受影響。

該有什麼期待

這些來源是為電子郵件而設計的,不是為廣告封鎖。它們在釣魚、惡意程式與 垃圾郵件酬載網域上表現優異,在廣告與追蹤器上則普通——那是可下載的 hosts 式清單的工作, 而 Town OS 刻意不去吸收那些。沒有任何排程抓取、沒有任何剖析,也沒有任何在你背後進行的快取。 如果你想讓某個廣告或追蹤網域消失,把它加為本機項目即可。

在 DNS 中發布服務

預設情況下,每個已安裝的套件服務都會發布到它所屬 網路的 TLD 之下,讓你能以名稱存取。 在 DNS → 服務 中,你可以逐一切換服務是否發布—— 未發布的服務照常執行,只是不再能以名稱解析。每個服務的完整名稱遵循 <name>.<repo>.<tld> 的格式(就預設網路而言,該 TLD 是 .home)。

DNS 流程
裝置
手機、筆電等
DNS 查詢
DHCP
路由器
發送 Town OS 作為 DNS
DNS = Town OS IP
轉送
Town OS
rolodex DNS 伺服器
權威區域 上游轉送

信任憑證授權單位

Town OS 透過 rolodex 內建的憑證授權單位,為你的內部服務簽發 TLS 憑證。要讓瀏覽器顯示鎖頭而不是警告, 每台裝置都必須先信任 Rolodex 根憑證一次。 取得它有三種方式 — 挑一個適合該裝置的即可。

方式一:透過 DNS 取得(DNS 到得了的地方都行)

Rolodex 會把 CA 憑證鏈發布在 DNS 裡,因此任何能解析你區域的裝置都可以取得 CA — 不需要存取註冊入口。根憑證與各區域的中繼憑證以 CERT 記錄(RFC 4398)形式發布在 _ca.<zone>, 並在 _rolodex-ca.<zone> 提供分段的 TXT 備援:

# 檢視已發布的 CA 記錄
dig @<town-os-ip> CERT _ca.example.home

# 把根憑證擷取成 PEM 檔(根憑證就是自我簽署的那一張)
dig @<town-os-ip> +short CERT _ca.example.home

在瀏覽器中使用這些記錄,最簡單的辦法是 Rolodex 瀏覽器擴充功能(位於 rolodex 儲存庫的 extension/ 目錄,可透過 chrome://extensions 或 Firefox 的 about:debugging 以未封裝方式載入)。開啟彈出視窗的 CA via DNS 區塊,填入你的 DoH 網址 (https://<town-os-ip>/dns-query)與區域,它就會透過 DNS-over-HTTPS 取回憑證鏈,優先使用 CERT 記錄並在必要時自動退回 TXT;若你提供主機名稱, 它還會用已發布的 DANE TLSA 記錄加以驗證,並提供根憑證、中繼憑證與完整憑證鏈的 PEM 下載。

方式二:透過註冊入口取得

在受信任的網路上,前往註冊入口 (預設為 https://<town-os-ip>:8500)並按下 Download root CA (PEM)。上面提到的擴充功能以及 rolodex-ca-ui 本機主控台也做得到。

方式三:透過命令列取得

# 透過管理 CLI(印出根憑證 + 中繼憑證的 PEM)
rolodex-dns-cli ensure-zone-ca --zone example.home

# 或直接抓取入口的下載
curl -k https://<town-os-ip>:8500/api/ca -o rolodex-root-ca.pem

在裝置上安裝根憑證

拿到 rolodex-root-ca.pem 之後,把它加進信任存放區:

平台做法
Firefox設定 → 隱私權與安全性 → 憑證 → 檢視憑證 → 憑證機構 → 匯入(勾選“信任這個 CA 來識別網站”)
Chrome / Edge(桌面版)使用作業系統的信任存放區 — 請依下面的系統列安裝,再重新啟動瀏覽器
macOS連按兩下 PEM 檔加入「鑰匙圈存取」,然後在 SSL 項目下設為“永遠信任”
Linux(Fedora/RHEL)sudo cp rolodex-root-ca.pem /etc/pki/ca-trust/source/anchors/ && sudo update-ca-trust
Linux(Debian/Ubuntu)sudo cp rolodex-root-ca.pem /usr/local/share/ca-certificates/rolodex.crt && sudo update-ca-certificates
Windows連按兩下 → 安裝憑證 → 本機電腦 → “受信任的根憑證授權單位”
Android設定 → 安全性 → 加密與憑證 → 安裝憑證 → CA 憑證
iOS開啟 PEM 檔(AirDrop 或郵件),安裝描述檔,再到設定 → 一般 → 關於本機 → 憑證信任設定中啟用

透過 Rolodex 的 ACME 端點簽發的伺服器,會提供 終端憑證 + 中繼憑證的憑證鏈,可用這張根憑證驗證。支援 DANE 的用戶端 還能用 rolodex 在簽發時自動發布的 TLSA 記錄,額外驗證中繼憑證。

網路與遠端存取

Town OS 把你的服務依網路分組。每台機器一開始都有一個名為 home 的內建網路——它僅限區域網路、解析 .home 下的名稱, 而且沒有通道。若要從家門之外安全地存取你的服務,請再建立其他網路: 每一個都是一層 WireGuard 疊加網路,配有自己的 DNS 頂層網域, 在 儀表板 → 網路 中管理(僅限管理者)。

網路頁面列出內建的 home 網路及其 TLD、子網路、對等點數量與「遠端存取」開關,旁邊是「建立網路」按鈕

預設網路

  • 永遠存在,無法移除。home 網路會自動建立,除非你另外挑選,否則套件都會落在這裡。
  • 僅限本地。它沒有 WireGuard 傳輸層——.home 名稱只在你的區域網路內解析,而且刻意永不對遠端對等點開放。
  • 使用 .home 頂層網域,其值來自 dns_tld 設定。

建立可遠端存取的網路

按下建立網路,給它一個名稱(例如 office), 並可選擇性地指定 TLD(預設與網路名稱相同)。接著 Town OS 會:

  • 產生一個 WireGuard 介面,其疊加子網路由 10.64.0.0/10 範圍中以確定性方式推導而來,本機取用 .1 位址。
  • 取得該網路的 TLD(TLD 在每台機器上唯一——若建立的網路使用了其他網路已占用的 TLD,會被拒絕)。
  • 在疊加網路上執行該網路專屬的 DNS 解析器,讓加入的裝置能解析該網路的名稱。
建立網路對話框,含名稱與 TLD 欄位,並註明 TLD 預設與網路名稱相同

每一列上的遠端存取開關可讓該 WireGuard 介面上線或離線。 關掉它會切斷遠端存取,但容器仍在執行,本地依然連得到。

登錄一台裝置

開啟某個網路的對等點對話框,輸入裝置名稱,按下 新增對等點。Town OS 會回傳一份可直接匯入的 WireGuard 設定—— 把它貼進手機或筆電上的 WireGuard 應用程式即可。

請立刻複製這份設定。它含有一把剛產生、從不保存的私密金鑰—— 之後無法再取回,弄丟了就只能重新登錄該裝置。

產生的設定會把裝置的 DNS 指向本機的疊加網位址,因此通道一建立, 你網路中的名稱就會自動解析。一般裝置請讓執行 rolodex DNS 開關維持關閉;只有當某個對等點本身跑著你想轉送過去的 rolodex DNS 伺服器時才啟用它。

僅限 WireGuard 的帳號

若你想讓某人自行登錄他們的裝置,又不想把整個儀表板交給他們,可以建立一個 僅限 WireGuard 的帳號(建立使用者畫面上的一個核取方塊)。這種帳號:

  • 被限定在一個或多個特定網路上——只能在這些網路上登錄對等點,而且永遠不能用於 home 網路。
  • 採預設拒絕:它可以驗證身分、登錄與更新自己的對等點、取得 CA,但控制層的其他事情一概不行。
  • 登錄的是會過期的對等點。每次登錄都帶有存續時間(預設 2 小時,可於 設定 → WireGuard 對等點 TTL 調整),必須更新才能維持連線;被棄用的裝置會自動過期。由管理者新增的對等點則是永久的。

觀察與中斷對等點

網路頁面上的已連線對等點面板,會列出所有網路中每一個已登錄的對等點, 並顯示即時交握狀態、疊加網 IP、傳輸量與到期時間。若要強制切斷某台裝置, 請使用中斷連線——它會移除該對等點、立即拆掉通道並撤銷其金鑰, 因此該裝置在重新登錄之前都無法再連上來。

在指定網路上安裝套件

安裝對話框(以及頁面對話框)中有一個網路選擇器。 你選的網路決定該服務的 DNS 名稱與 TLS 憑證——安裝在 office 上的套件會在 .office 下解析,憑證也依該名稱簽發。非預設網路上的服務是雙棲的: 對通道對等點解析為疊加網位址,對本地用戶端則解析為區域網路位址。把套件重新安裝到 另一個網路,會把它的 DNS 與憑證一併移到該網路的 TLD 下。

安裝 jitsi 的對話框,最上方是標示「此套件所服務的網路」的網路選擇器,下方為該套件的設定問答

舉例來說,把 Jitsi 安裝在 TLD 為 fart 的網路上,它會發布在 jitsi.default.fart。跑起來之後,該服務會以可點擊的連結出現在你的 儀表板上,網路上的任何裝置都能直接打開。

儀表板的「已安裝服務」清單顯示 jitsi,並附上指向 https://jitsi.default.fart/ 的連結
透過網路進行遠端存取
你的裝置
手機或筆電
WireGuard
通道
Town OS
疊加網路 + DNS
office 子網路 .office 頂層網域
解析
你的服務
可用名稱存取
gitea.default.office

Android 上的 Town OS

Town OS Android 用戶端會透過 WireGuard 把你的手機接進其中一個 網路,並設定好 DNS,讓你的服務在任何地方都能以名稱解析。 它是一個完整的 WireGuard 用戶端,會替你把手機登錄為對等點;不必手動複製任何設定檔。

Town OS Android 用戶端登入畫面——「連線到一台機器」卡片,含機器位址、管理者帳號與密碼欄位
登入一台機器
Town OS Android 用戶端連線畫面——顯示「已中斷連線」狀態,含「連線」與「忘記此網路」按鈕,下方是 DNS 解析器設定卡片
管理連線與 DNS

安裝應用程式

這個應用程式以 APK 形式發布在專案的 釋出頁面 ——它不在 Play 商店,也不在 F-Droid 上。

  • 請下載 debug 版 APKtown-os-client-<version>-debug.apk)。同一版本中的 -unsigned.apk 無法直接安裝——專案並未提供簽署金鑰,所以要拿的是 debug 版。
  • 需要 Android 8.0(Oreo)或更新版本
  • 比較想自己建置?複製儲存庫,在手機以 USB 連接並開啟 USB 偵錯的情況下執行 make deps && make debug && make installmake help 會列出所有目標。

安裝方式有兩種:直接在手機上裝,或是從電腦透過 USB 裝。前者除了手機之外什麼都不需要。

直接在手機上安裝

不用電腦、不用傳輸線,也不用開發人員模式——開發人員選項與 USB 偵錯只跟下面 adb 那條路有關。Android 唯一要的,就是允許安裝一個並非來自應用程式商店的應用程式。

  1. 用手機瀏覽器下載 APK。開啟釋出頁面,點 town-os-client-<version>-debug.apk 這個檔案。瀏覽器會警告這類檔案可能會傷害你的裝置——任何 APK 都會跳這個提示,請選仍要下載
  2. 從下載完成的通知、瀏覽器的下載清單,或是「檔案」應用程式裡開啟它
  3. 允許來源。第一次時,Android 會說開啟它的那個應用程式沒有安裝不明應用程式的權限,並給你一個設定按鈕——點進去,為該應用程式(你的瀏覽器或檔案管理員)開啟允許來自此來源,然後返回。同一個開關也在設定 → 應用程式 → 特殊存取權 → 安裝不明應用程式裡。
  4. 點安裝。Play 保護機制可能會提議掃描這個應用程式,或提示它來自未知的開發人員——對於商店之外安裝的應用程式,這是預期之中的。請選仍要安裝
  5. 安裝完成後,從應用程式匣開啟 Town OS

先從電腦把 APK 傳過去——用 USB 檔案傳輸、adb push,或任何一款同步應用程式—— 再用檔案管理員開啟它,效果完全一樣。

開啟開發人員模式

只有走 adb 這條路才需要它——直接從手機自己的下載清單安裝 APK 並不需要。 adb installmake install 都要透過 USB 偵錯和手機溝通, 而 USB 偵錯藏在 Android 隱藏的開發人員選項選單裡。

  1. 開啟設定 → 關於手機。三星手機上是設定 → 關於手機 → 軟體資訊
  2. 連續點版本號碼七次。Android 會倒數提示(「再點 3 次就能成為開發人員」),並在完成前要求輸入你的 PIN、圖形或密碼。
  3. 畫面會提示你現在已是開發人員。此後開發人員選項會出現在設定 → 系統裡——有些手機把它放在設定的第一層,如果不在你預期的位置,就在設定裡搜尋一下。
  4. 開啟開發人員選項,啟用 USB 偵錯
  5. 把手機接到電腦上。Android 會跳出允許 USB 偵錯嗎?對話框,並顯示該電腦的金鑰指紋——勾選一律允許透過這台電腦進行偵錯並確認。執行 adb devices,確認手機顯示為 device 而不是 unauthorized

用完之後請把 USB 偵錯關掉。它會讓你授權過的每一台電腦都能透過偵錯橋接完全存取這支手機, 日常一直開著是沒有必要的風險。

不用儲存庫,只用 adb 安裝

透過 USB 安裝並不需要用戶端的原始碼樹——make install 只是一層方便的包裝。 走 adb 這條路只需要釋出的 APK,以及 adb 這個執行檔本身, 它來自 Google 的 Android SDK Platform Tools

  • Ubuntu / Debian——sudo apt install adb android-sdk-platform-tools-common。後一個套件帶的是 udev 規則,讓你的一般使用者帳號不必用 root 就能存取手機;裝完之後把手機拔掉重插一次。
  • Arch / Manjaro——sudo pacman -S android-tools android-udev,然後用 sudo usermod -aG adbusers $USER 把自己加進 adbusers 群組,再登出並重新登入。
  • macOS——brew install --cask android-platform-tools。沒有別的要設定的;macOS 不需要驅動程式就能跟手機溝通。
  • Windows——在 PowerShell 裡執行 winget install Google.PlatformTools,或是下載下面的壓縮檔。大多數手機用 Windows 自己裝的驅動程式就能運作;少數廠商(三星、小米)要裝它們自己的 USB 驅動程式,adb 才看得到裝置。
  • 其他任何系統——從 Google 下載 platform-tools 壓縮檔, 解壓縮到任意位置即可。沒有什麼需要安裝的——直接在那個資料夾裡執行 adb (Windows 上是 .\adb.exe)。

adb version 確認裝好了。接著從釋出頁面下載 debug 版 APK,照上面的說明開啟 USB 偵錯,把手機接上,然後:

# 確認手機已連接並已授權
adb devices

# 安裝 APK
adb install town-os-client-<version>-debug.apk

有幾個地方容易卡住:

  • no devices/emulators found——線材只能充電,或是還沒在手機上授權這台電腦。重新插拔線材,然後留意手機螢幕上的允許 USB 偵錯嗎?提示。
  • Linux 上出現 no permissions——上面那個 udev 規則套件沒裝,或是手機在裝它之前就已經插著了。裝上它,重新插一次手機,然後執行 adb kill-server && adb devices
  • INSTALL_FAILED_UPDATE_INCOMPATIBLE——手機上已經裝了一份以不同金鑰簽署的副本,如果你先前自己建置過就會這樣。先移除舊的應用程式(在應用程式匣裡長按它,或用 adb shell pm list packages town 查到套件名稱後 adb uninstall),再重新安裝。
  • 原地升級——adb install -r town-os-client-<version>-debug.apk 會取代既有的安裝並保留其資料,前提是兩者都來自釋出頁面。
  • 這一整套都不會在手機的儲存空間留下東西——adb install 一步完成傳輸與安裝,事後不必再到檔案管理員裡找那個檔案。

連線之前

  • 先建立一個網路。應用程式只能加入帶有 WireGuard 疊加層的網路——內建的 home 網路僅限區域網路,因此不會出現在清單中。請見建立可遠端存取的網路
  • 準備好一個管理者帳號。登錄裝置屬於管理者動作,所以應用程式要用管理者憑證登入。

連線到網路

  1. 登入你的機器。輸入它的位址——純 IP、IP:連接埠,或完整網址(預設連接埠為 5309)——以及你的管理者帳號與密碼。
  2. 加入一個網路。應用程式會列出你可以加入的網路,並顯示各自的 TLD、子網路與對等點數量。給裝置一個名稱(預設用你手機的型號),然後點加入。WireGuard 金鑰對在手機上產生,只有公開的那一半會送到機器上,因此你的私密金鑰絕不離開裝置。
  3. 連線。連線,並同意 Android 的連線要求(VPN)提示。應用程式會建立通道,並把該網路的 TLD 裝為搜尋網域,因此 giteagitea.default.<tld> 都能解析。
  4. 現在你可以從任何地方以名稱使用這個網路了。用中斷連線關閉通道,或用忘記此網路移除已儲存的登錄資料。

疑難排解

  • 通道已連上,但名稱解析不了。常見的元兇是 Android 的嚴格私人 DNS。若它被設為某個指定的供應商主機名稱,Android 會把每一次查詢都送到那裡並忽略通道,於是 Town OS 的名稱就會「找不到」。請把設定 → 網路和網際網路 → 私人 DNS 改成自動關閉。應用程式會偵測到這點,並顯示可直接前往該設定的警告。(自動模式沒問題,不會警告。)
  • 還是不行?在應用程式的 DNS 卡片中,把解析器覆寫設為該機器的區域網路位址——應用程式會把它經由通道路由,因此分離視域 DNS 仍然有效。
  • 機器重新開機後被登出。Town OS 會在重新啟動時清除所有工作階段;重新登入即可。

用 USB 映像檔建立虛擬機器

install 儲存庫包含在虛擬機器中啟動 Town OS 的指令稿,這在測試與開發時很有用—— 可以用你從原始碼建置的映像檔,也可以直接用 已經燒好的實體隨身碟make help 會列出所有目標與變數。

QEMU

make qemu-fg

映像檔過期時會先重新建置,然後在前景啟動一台 QEMU 虛擬機器, 附帶序列主控台、KVM 加速,以及四顆用於測試儲存的虛擬資料磁碟。 這是你該優先選用的目標:你可以在終端機裡直接看著開機與安裝程式,按 Ctrl-C 即可停止。 若你希望它在背景執行,請改用 make qemu (之後以 make serial 連上), 而 make rebuild-qemu 則一次完成停止、清理、重建與重新啟動。

虛擬機器會接上 libvirt 的 default NAT 網路(virbr0), 並固定在 VM_IP(預設為 192.168.122.50), 因此同時執行多台虛擬機器時,請各自給定不同位址。

由於客體機在 NAT 之後,VM_LAN=1(預設值)會把控制 API (5309)、介面(80/443)、ssh (2222)與 WireGuard 的 UDP 連接埠,從主機的區域網路位址轉送進客體機—— 正是這點讓執行 Android 用戶端的手機能連到虛擬機器。 設定 VM_LAN=0 可關閉這些轉送。

從實體 USB 開機(qemu-usb)

make qemu-usb USB_DEV=/dev/sdX

直接從燒好的實體隨身碟(而非建置出的映像檔)在前景啟動 QEMU—— 很適合用來確認剛燒好的隨身碟真的能開機。裝置以唯讀方式開啟(快照模式), 因此客體機的寫入都會被丟棄,真正的隨身碟絕不會被更動;四顆虛擬資料磁碟仍會掛上供測試儲存。 這個目標不會建置任何東西——請先複製 install 儲存庫,用 install.shmake flash 燒一支隨身碟,再把 USB_DEV 指向它。 在 x86_64 主機上,TARGET=aarch64(或 rpi)會以全系統模擬 方式開機該隨身碟,因此沒有對應硬體也能測試異架構的映像檔。

停止與清理

# 停止虛擬機器
make stop

# 停止虛擬機器並刪除映像檔與虛擬磁碟
make clean

環境變數

變數預設值說明
IMAGE_SIZE12GUSB 映像檔建置時的稀疏大小——映像檔之後會被縮小
VM_DISK_SIZE50G四顆虛擬資料磁碟各自的大小(取自 town-os.yaml 中的 vm_disk_size
VM_MEMORY4G虛擬機器記憶體
VM_CPUS4虛擬機器的 vCPU 數量——QEMU 預設的 1 顆會讓 rolodex 的工作執行緒池吃不飽
VM_BRIDGEvirbr0用於網路的主機橋接介面
VM_NAMEtown-os虛擬機器名稱,也用來尋找與停止它
VM_IP192.168.122.50libvirt 的 DHCP 保留位址——同時執行多台虛擬機器時請各自給定
VM_LAN1把 NAT 客體機的連接埠轉送到主機的區域網路位址;0 表示停用
USB_DEVmake flash(寫入)與 make qemu-usb(唯讀開機)使用的實體區塊裝置

序列主控台

# 連上虛擬機器的序列主控台
make serial

# 或手動透過 socat 連線
socat -,rawer,escape=0x1d unix-connect:/tmp/town-os-serial.sock

# 用 Ctrl-] 離開

找到虛擬機器

# 取得虛擬機器的 IP 位址
make vm-ip

# 或從主機透過 mDNS 連線
ssh root@town-os.local
虛擬機器網路
主機
virbr0 —— libvirt 預設網路
QEMU
NAT
Town OS 虛擬機器
完整的 Town OS 堆疊
192.168.122.50 eth0
VM_LAN 轉送
區域網路
手機與區網裝置可連到虛擬機器
5309 · 80 · 443 · WireGuard

RAID 安裝

磁碟佈建由 ttyforce 在首次開機時以互動方式完成。所有儲存都使用 btrfs。

自動選擇 RAID 等級

ttyforce 會偵測可用磁碟,依傳輸類型(NVMe、SATA 等)與相近容量分組, 再依磁碟數量自動選擇 RAID 等級:

  • 1 顆磁碟——single 模式(無備援)
  • 2 顆磁碟——RAID 1(btrfs 鏡像)
  • 3 顆以上——RAID 5(btrfs 含同位元檢查的分散)

預設掛載點是 /town-os。ttyforce 會建立 @etc@var 子磁碟區,以配合 overlayfs 做持久化。

磁碟偵測

ttyforce 會自動偵測可用的儲存裝置。它能辨識 NVMe 磁碟、SATA/SAS 磁碟與 SD 卡, 同時排除開機用的 USB 裝置與任何可卸除式媒體。這套偵測邏輯確保 Town OS 絕不會動到它自己開機所用的那顆磁碟。

疊加檔案系統

無論使用哪種儲存後端,Town OS 都以 overlay 掛載來持久化 /var/etc。下層來自 squashfs 根檔案系統, 上層則位於 RAID/ZFS 儲存上。如此一來,系統設定與服務資料能跨重新開機保留, 而基礎作業系統始終保持不可變。

儲存架構
NVMe
高速固態硬碟
SATA / SAS
機械或固態硬碟
SD 卡
可卸除式
偵測
btrfs single
1 顆磁碟
btrfs RAID 1
2 顆磁碟(鏡像)
btrfs RAID 5
3 顆以上(同位元檢查)
子磁碟區
依套件劃分的儲存
pkg-a pkg-b
Overlay 掛載
持久化的系統目錄
/var /etc

使用操作介面

Town OS 提供一個清爽的網頁儀表板來管理你的伺服器。開機完成後, 開啟瀏覽器前往 Town OS 機器的 IP 位址,或 http://town-os.local

首次開機:建立你的帳號

首次開機時,系統會提示你建立一個管理者帳號。挑好使用者名稱與密碼—— 這個帳號對系統擁有完整控制權。

建立帳號畫面

儀表板

儀表板顯示系統總覽——已安裝的套件以服務卡片呈現,附狀態指示、 快速操作,以及一目了然的系統健康狀況。

儀表板總覽

瀏覽與安裝套件

「套件」檢視讓你搜尋可用的套件、查看細節,並在引導式問答的協助下安裝它們。 每個套件的問題都會呈現為一份表單——填好主機名稱、連接埠與其他設定,再按下安裝。

套件瀏覽器
套件安裝問答
安裝細節

管理服務

已安裝的服務可以在「服務」檢視中啟動、停止與重新啟動。 狀態指示會顯示每個服務是執行中、已停止,或處於錯誤狀態。

服務管理

檢視記錄

「記錄」檢視提供即時的 journal 輸出,並支援篩選與 grep。 你可以依服務、優先層級篩選,也可以搜尋特定文字。

記錄檢視器

儲存管理

檢視與管理 btrfs 子磁碟區、為各套件設定配額, 並監看整個儲存池的磁碟使用量。

儲存管理

監控

Town OS 內建以 Prometheus 與 Node Exporter 為基礎的監控, 可長期追蹤系統指標、服務健康狀況與資源使用量。預設使用輕量的內建儀表板, 也可以選擇升級為 Grafana。

監控儀表板

設定與稽核記錄

「設定」頁面讓你調整全系統的選項。「稽核記錄」會追蹤每一項管理動作—— 安裝、解除安裝、服務狀態變更與設定修改——因此你隨時都清楚什麼被改過、何時改的。

設定
稽核記錄

架設靜態網站

除了容器化的套件,Town OS 還內建靜態網站代管, 它始終開著,並在 儀表板 → 頁面 中管理。每個頁面都由入口後方的 一個共用網頁伺服器提供服務,並像套件一樣,在其 網路的 TLD 下取得 DNS 名稱與 TLS 憑證。

內容來源

建立頁面時,你要挑選一個名稱、一個網域 (預設與名稱相同)、一個網路(預設為 home), 以及三種內容來源之一:

  • 上傳封存檔——上傳你網站的 tarball。在你上傳封存檔之前,頁面會維持待處理狀態。
  • Git 儲存庫——從儲存庫網址複製。你可以指定分支(預設為 main),這對發布在 gh-pages 的網站很方便。
  • 容器映像檔——從 OCI 映像檔中擷取某個目錄。

git 與容器頁面採非同步佈建——表格中會顯示 佈建中… 標記,最後會變成使用中或錯誤。對 git 與容器頁面, 請用重新建置拉取最新內容;至於封存檔頁面,則改為上傳新的 tarball。

頁面是怎麼提供服務的

每個頁面都位於自己的儲存子磁碟區上,並直接以 HTTP 於 80 連接埠提供服務, 而套件服務則會把 80 連接埠導向 HTTPS。由於頁面同樣歸屬某個網路, 它的名稱會在該網路的 TLD 下解析,可從你的區域網路存取;若該網路是 WireGuard 疊加網路,你已登錄的遠端裝置也連得到。

更新 Town OS

Town OS 可以直接在儀表板上就地更新核心服務。在 系統管理頁面,重新整理核心服務按鈕會拉取最新的容器映像檔 並重新啟動每一個核心服務——其中也包括系統控制器本身,這正是這台機器自我更新的方式。

重新整理時會發生什麼

  • 映像檔會依相依順序拉取並重新啟動服務:先是系統控制器(好讓它在重啟自己之前,新映像檔已經就緒),接著是 DNS,最後是其餘部分。
  • 控制器重新啟動時,你的登入工作階段會被刻意重設,因此重新整理進行期間,儀表板會暫緩平常的工作階段檢查,而不是把你彈回登入頁。
  • 進度以五階段步驟列呈現:啟動系統控制器啟動 DNS啟動系統服務重新啟動套件(每個已安裝的套件一列)→ 就緒
  • 重新整理完成後,儀表板會顯示一個重新載入按鈕,而不是在你正在操作時自行重新載入。

正常開機時的佈建畫面上也會出現同樣這五個階段,因此你可以逐一套件地觀察啟動進度。

製作套件

Town OS 的套件是描述如何執行容器化服務的 YAML 檔案。 完整規格請見封裝格式參考文件。 本節談的是實際的工作流程。

儲存庫結構

套件庫就是一個帶有 packages/ 目錄的 git 儲存庫。 每個套件各占一個子目錄,其中放著帶版本號的 YAML 定義:

my-packages/
  packages/
    my-app/
      1.0.yaml
      2.0.yaml

撰寫套件定義

最精簡的套件只需要 image 欄位。典型的套件還會包含描述、 網路、磁碟區與面向使用者的問答:

image: myapp:latest
description: My custom application
supplies: ["http"]
network:
  external:
    "@port@": "8080"
volumes:
  data:
    mountpoint: /app/data
    quota: 5gb
questions:
  port:
    query: "What external port should this app use?"
    type: port
    default: "9000"
notes:
  URL:
    value: "http://@LOCAL_EXTERNAL_HOST@:@port@"
    type: url

除了自由文字與 port,問答還支援幾種型別:secret (留白時自動產生)、boolean(呈現為核取方塊)、 oauth(一個連線按鈕,會跑供應商的裝置流程來取得權杖), 而且任何問題都可以標成 optional: true 以允許留白。 完整清單請見封裝格式參考中的問答一節。 安裝時,操作者也會挑選這個套件要在哪個網路上提供服務。

範本系統

使用 @variable@ 語法來參照問答的回答與內建變數 (@LOCAL_EXTERNAL_HOST@@LOCAL_INTERNAL_HOST@)。 範本可用於環境變數、連接埠對應、配額與註記值之中。

在本機測試

開發環境來測試你的套件。 把儲存庫放在磁碟上,透過介面或 repositories.json 加入它, 然後安裝你的套件。開發環境提供完整的 Town OS 堆疊供你測試。

加入套件庫

你可以透過介面(套件 → 套件庫 → 新增套件庫)把你的套件庫 加進 Town OS,也可以直接編輯 repositories.json

[
  {"name": "default", "url": "https://github.com/town-os/default-packages"},
  {"name": "my-packages", "url": "https://github.com/myuser/my-packages"}
]
套件生命週期
YAML 定義
映像檔、磁碟區、問答
1.0.yaml
安裝
設定問答
使用者回答問題
主機名稱 連接埠
部署
執行中的容器
服務已啟動並掛上磁碟區
@variable@ → 值

可以自架的東西

Town OS 天生就能執行任何以容器映像檔形式發布的軟體。以下是一些能讓你上手的點子—— 其中不少已經收錄在 預設套件庫中, 而任何容器映像檔都能用一份簡單的 YAML 定義封裝進來。

影音娛樂

  • Plex / Jellyfin——把你的電影與影集庫串流到任何裝置
  • Navidrome——個人音樂串流伺服器
  • Calibre-web——管理與閱讀你的電子書收藏

程式碼與協作

  • Gitea / Forgejo——輕量的自架 Git
  • GitLab——完整的 DevOps 平台
  • Nextcloud——檔案、行事曆、聯絡人等等
  • Wiki.js / BookStack——文件與知識庫

通訊

  • Jitsi Meet——私人視訊會議
  • Matrix / Synapse——聯邦式加密聊天
  • Mattermost / Rocket.Chat——團隊訊息

遊戲伺服器

  • ValheimMinecraftTerrariaSatisfactory 專用伺服器
  • 透過 GloriousEggroll 容器支援 Proton/Steam
  • 任何以 Linux 執行檔或容器形式發布的遊戲伺服器

家庭自動化

  • Home Assistant——智慧家庭控制中樞
  • Node-RED——視覺化自動化流程
  • Mosquitto——供 IoT 裝置使用的 MQTT broker

隱私與安全

  • Pi-hole / AdGuard——全網路的廣告封鎖
  • WireGuard / OpenVPN——供遠端存取的 VPN 伺服器
  • Vaultwarden——自架的密碼管理器

生產力

  • Paperless-ngx——文件管理與 OCR
  • Immich——自架的相片與影片管理
  • Planka / Wekan——看板與專案管理

開發環境與測試套組

Town OS 的開發環境以 Podman 容器在本機執行完整的堆疊。 這是測試變更、開發套件與執行測試套組最快的方式。

先決條件

  • Linux——btrfs 與 Podman 的 rootful 容器需要它
  • Podman——容器執行環境(rootful 模式,需要 sudo)
  • Go 1.25+——用於後端 API 伺服器
  • Bun——用於前端建置與開發伺服器
  • btrfs-progs——用於儲存管理
  • QEMU——qemu-system-x86_64 與 qemu-img,用於支援虛擬機器套件
  • libsystemd——systemd 整合所需的開發標頭檔
  • golangci-lint——用於 Go 靜態檢查
  • Python 3——用於建置與測試指令稿

啟動開發環境

git clone https://gitea.com/town-os/town-os.git
cd town-os
make dev

這會啟動具備熱重載的完整開發堆疊。就緒之後, 打開終端機印出的網址即可進入 Town OS 儀表板。

開發環境指令

指令說明
make dev啟動完整的開發環境
make dev-stop停止所有開發容器
make dev-logs追蹤開發容器的記錄
make dev-clean移除開發容器與磁碟區

執行測試

指令說明
make test執行單元測試
make test-integration執行整合測試(需要特權 Podman)
make test-ui-integration執行介面整合測試
make test-full執行全部測試(單元 + 整合 + 介面)
make auto-test監看變更並自動重跑測試

整合測試

整合測試在一個特權 Podman 容器中執行,該容器提供真實的 btrfs 檔案系統、 systemd 以及 Podman-in-Podman。這確保測試走的是與正式安裝相同的程式碼路徑。 測試容器是用完即丟的——每次測試都會重新建立,結束後再清理掉。

開發工作流程
編輯器
撰寫程式碼
Go + Bun
儲存
auto-test
儲存時自動跑測試
make auto-test
重新載入
瀏覽器
即時預覽
localhost:5173
↻ 反覆迭代 — 後端 :5309 · 前端 :5173

回報錯誤

發現哪裡壞了?好的錯誤回報能讓問題更快被修好。 以下說明如何蒐集所需資訊,並提交一份有效的回報。

用 API 蒐集記錄

Town OS 透過它的 REST API 對外提供 journal 記錄。 你可以用 Claude Code 連上該 API,並產生一份近期錯誤的摘要:

# 從 Town OS 取得 error 優先層級的 journal 項目
curl -s http://town-os.local:5309/api/systemd/logs/tail?priority=err | jq .

或者用 Claude Code 以互動方式歸納錯誤:

# Claude Code 提示範例:
"Connect to the Town OS API at http://town-os.local:5309
 and fetch the last 100 error-priority journal entries from
 /api/systemd/logs/tail. Summarize the errors, group them
 by service, and suggest likely causes."

開立 issue

請在 Town OS 的 Gitea 實例上開立 issue: gitea.com/town-os/town-os/issues

應該包含哪些內容

  • 記錄摘要——錯誤記錄輸出,或由 Claude Code 產生的近期錯誤摘要
  • 重現步驟——你是怎麼一步步觸發這個問題的
  • Town OS 版本——開機畫面上顯示的建置日期或提交雜湊
  • 儲存後端——btrfs、btrfs-mdadm 還是 ZFS,以及磁碟數量
  • 執行環境——實體硬體或 QEMU;記憶體與磁碟容量
錯誤回報流程
發現問題
有東西壞了
⚠ 錯誤
curl API
蒐集記錄
取得 journal 項目
:5309/api/systemd/logs
歸納
整理摘要
依服務分組
Claude Code
開立
在 Gitea 上開立
建立含細節的 issue
✓ 已回報