跳到內容

執行無介面伺服器

在一台持續開機的機器上執行 cc_server,然後從你的桌面版、瀏覽器或手機連線過去。 伺服器是純 Dart 的原生二進位檔——不含 Flutter 引擎——因此能在 macOS、Linux 或 Windows 上無介面執行。它擁有資料庫、執行代理,並發出每一次外部 API 呼叫; 用戶端只負責呈現。

每次發行都會發布一套自行託管的堆疊,內含四個 GHCR 映像——後端、兩個靜態精簡 用戶端,以及選用的配對中繼——另外還有一個給公開示範主機用的獨立 cc-server-demo 映像。自行託管的安裝不需要簽出原始碼。請固定版本標籤 (ghcr.io/samuelalev/<image>:v0.0.1-rc.1)或直接使用 latest。

映像 內容 對外連接埠
ghcr.io/samuelalev/cc-server 後端:資料庫、代理、MCP、webhook -p 9030:9030
ghcr.io/samuelalev/cc-webapp 網頁用戶端(靜態 nginx) -p 8080:8080
ghcr.io/samuelalev/cc-remote 手機伴侶 PWA(靜態 nginx) -p 8081:8080
ghcr.io/samuelalev/cc-signaling-server WebRTC 配對中繼(選用) -p 8788:8788

整組堆疊的 compose 檔案如下:

services:
server:
image: ghcr.io/samuelalev/cc-server:latest
ports: ["9030:9030"]
volumes: ["cc_data:/data"]
restart: unless-stopped
web:
image: ghcr.io/samuelalev/cc-webapp:latest
ports: ["8080:8080"]
restart: unless-stopped
remote:
image: ghcr.io/samuelalev/cc-remote:latest
ports: ["8081:8080"]
restart: unless-stopped
signaling:
image: ghcr.io/samuelalev/cc-signaling-server:latest
ports: ["8788:8788"]
restart: unless-stopped
volumes:
cc_data:

在你把上述任何東西開放至回送以外的範圍之前,有幾件事值得先了解:

  • cc-server 映像隨附 CC_SERVER_INSECURE=1(它必須繫結 0.0.0.0 才能收到已發布連接埠的流量),因此只有在可信任網路上、位於負責終止 TLS 的反向 代理——Caddy、Traefik、nginx 或 Cloudflare Tunnel——後方才安全。若要在行程內 終止 TLS,請改為掛載憑證並設定 CC_SERVER_TLS_CERT / CC_SERVER_TLS_KEY (然後取消設定 CC_SERVER_INSECURE)。這一切都能用 docker run -e … 或 compose 的 environment: 區塊覆寫。

  • /data 是唯一保存狀態的地方。 SQLite 資料庫、已配對裝置的密鑰、 下載的模型與會議音訊全都放在這個磁碟區裡——備份它,容器就變成可拋棄的。

  • 在首次連線前,從映像內部配對一個用戶端,這是步驟 3 的容器等效做法:

    終端機視窗
    docker run --rm -it -v cc_data:/data \
    --entrypoint /app/bin/cc_server ghcr.io/samuelalev/cc-server:latest pair
  • 兩個用戶端映像都是靜態 nginx 主機——執行時沒有什麼要設定;每個用戶端的 連線表單會接收你的伺服器 URL、裝置 id 與配對金鑰。中繼也一樣只需要它的 連接埠(SIGNALING_HOST / SIGNALING_PORT 可覆寫);它是無狀態的、不保存 任何密鑰,而且是備援路徑——手機在可觸達時會使用回送、LAN、tailnet 或 直接的 wss:// 路徑,只有在全都不可達時才退回中繼。如果你的用戶端一律能 直接觸達伺服器,就可以略過它。

本指南其餘部分會從原始碼建置伺服器;它所涵蓋的旗標與行為,和映像的環境變數 所餵入的是同一批。

  • Dart SDK。本儲存庫以 fvm 固定其 SDK,因此請在每個指令前加上 fvm。
  • 已簽出本儲存庫。
  • 暫存指令碼所需的原生工具鏈——Rust/cargo,加上 meson、ninja、pkg-config、git 與 C++ 編譯器。每個建置指令碼都會指出它找不到什麼,以及該如何安裝。
  • 一台可觸達的主機:本機使用時採回送,遠端用戶端則採具備 TLS 的 LAN/公開位址。

原生程式庫是必要的——沒有降級模式。請先從儲存庫根目錄建置它們,再做其他 任何事:

終端機視窗
scripts/natives/build_natives.sh

這會把 rift、fff、tree-sitter 及其文法、aec、lame、 ccpty、cc_watcher、cc_inference 與 SAML 原生程式庫建置到 build/natives/。cc_server 的建置 掛鉤會把暫存在那裡的內容重新輸出到套件包中,因此略過這個步驟,不是直接讓建置 失敗,就是——使用下方的逃生出口時——產生一個二進位檔,其開機前檢查會拒絕 啟動並指出缺少的程式庫。

終端機視窗
cd apps/cc_server
fvm dart build cli

套件包會連同二進位檔一起帶上 libsqlite3 與暫存好的原生程式庫——不需要系統 SQLite,也不需要 Flutter 引擎。執行檔會產生在 ./build/cli/<os_arch>/bundle/bin/cc_server。

全新的資料目錄沒有已配對裝置,也沒有預先共享的金鑰,因此用戶端的配對金鑰 提示還無法被滿足。請用 pair 子指令簽發一組。它會直接寫入 global.db 與 secrets.json;一個已經在提供該資料目錄服務的伺服器,不需重新啟動就會接收 這台新裝置:

終端機視窗
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030

它會印出三個值,讓你貼進用戶端的連線表單:

Connect a thin client (paste into its connect form):
Server ws://localhost:9030/rpc
Device id web-client
Pairing key <a freshly generated key>

實用的旗標:--device(預設為 web-client;這個 id 也決定裝置層級—— desktop、ios、android,其他任何值都是網頁用戶端)、--label (預設為平台名稱加上這台機器的主機名)、--host(要印在 URL 中的主機,因為 回送無法從另一台機器觸達)以及 --client-url。重新執行 pair 會輪替該裝置的 金鑰並終止其使用中的工作階段。配對永遠不會建立工作區:連線用戶端的導引流程 會為第一個工作區命名並建立它。

加上 --client-url 時,它還會印出可在終端機掃描的 QR 碼,開啟時網頁用戶端 已預先填好:

終端機視窗
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030 \
--bind any --host 192.168.1.42 --client-url https://app.usectrl.dev

由 cc_server pair 簽發的認證資訊不會過期;在應用程式內簽發的則一律在 30 天後過期。用畢之後,請從設定 → 你 → 你的裝置撤銷 CLI 簽發的裝置。

在回送上——這是預設值,對於同一台機器上的用戶端來說也已經足夠:

終端機視窗
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030

若要從另一台機器觸達,請繫結所有介面。沒有 TLS 的非回送繫結會在啟動時 遭拒(Refusing to bind non-loopback address … without TLS),因此請提供 憑證:

終端機視窗
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \
--bind any --tls-cert /path/fullchain.pem --tls-key /path/privkey.pem \
--public-url wss://server.example:9030/rpc

或者,在可信任私人網路的反向代理上終止 TLS,並在其後方選擇使用明文繫結:

終端機視窗
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \
--bind any --insecure --public-url wss://server.example/rpc

當 --tls-cert 與 --tls-key 兩者都設定時,--insecure 會被忽略——TLS 一律優先。伺服器會在 GET /healthz 中以 insecure: true 公告明文狀態, 用戶端則會把這類連線標示為不安全。

伺服器會一直執行到收到 SIGINT/SIGTERM,然後乾淨地關機。

把用戶端指向步驟 3 的伺服器 URL 與配對金鑰:

  • 桌面應用程式:在首次執行畫面(Control Center 要以何種方式執行?)填入連線欄位,而不要選在此應用程式內執行;或之後在 設定 → 伺服器 → 連線與狀態新增伺服器。請參閱連線至遠端伺服器。
  • 網頁用戶端:開啟 app.usectrl.dev(或你自己的網頁版本副本),並輸入伺服器 URL、裝置 id 與配對金鑰。
  • 手機:從執行中的用戶端配對 Remote 應用程式——請參閱配對裝置。

若要在沒有連接任何圖形介面的情況下同步工作區的日曆,請使用裝置代碼流程:

終端機視窗
GOOGLE_OAUTH_CLIENT_ID=<id> GOOGLE_OAUTH_CLIENT_SECRET=<secret> \
./build/cli/<os_arch>/bundle/bin/cc_server calendar connect \
--data-dir ./data --workspace <workspace-id>

它會印出一組代碼與一個網址,讓你在另一台裝置上核准;它會把重新整理權杖存放 在伺服器端,然後結束。原始碼建置不會隨附 Google 用戶端,因此 id 與密鑰這兩個 環境變數是必要的——沒有 --google-client-id 旗標。請參閱連接 Google Calendar。

上面用到的旗標是初次設定所需的那些。完整的介面——23 組旗標與環境變數配對、 四個子指令,以及資料目錄的配置——請參閱 cc_server CLI 參考。

你最可能需要的四個:

旗標 預設值 意義
--data-dir 作業系統的每位使用者應用程式資料目錄 資料庫、密鑰、模型與快取媒體
--port 9030 TCP 連接埠(0 = 臨時)
--bind loopback loopback,或 any 表示所有介面(需要 TLS 或 --insecure)
--public-url 由繫結推導 向已配對用戶端公告的 RPC URL。在代理、NAT 或通道後方請明確設定這個值

預設資料目錄在 macOS 上是 ~/Library/Application Support/control-center, 在 Windows 上是 %APPDATA%\control-center,其他平台則是 $XDG_DATA_HOME/control-center(否則為 ~/.local/share/control-center)。 只有在無法解析出任何家目錄或應用程式資料目錄時,才會使用相對於目前工作目錄的 .cc_server。

cc_server 是整個產品介面,而不是其中一部分:儲存庫 RPC 資料路徑與訂閱、 位於同一連接埠上 POST /mcp 與 GET /sse 的 MCP 工具端點、傳入的 webhook、 媒體與字型代理、單一登入回呼、機群租約協定、每 30 分鐘排程的伺服器端 RSS 抓取,以及向量搜尋(sqlite_vector 擴充功能會註冊為行程全域的自動擴充功能, 只有在該擴充功能無法使用時才降級為全文搜尋)。

派送器是無狀態的:沒有逐工作區的工作階段繫結。 每一項以工作區為範圍的呼叫都攜帶自己的 workspace_id,存取權由呼叫者的工作區 成員資格與角色決定——而不是持有配對金鑰。

在你把連接埠開放至回送以外的範圍之前,有兩件事要知道:

  • MCP 在主機外會失效即關閉。 沒有權杖的 MCP 介面只回應回送。繫結 any 並不會擴大範圍——來自主機外的請求會遭到拒絕,並附上「MCP requires a bearer token to answer non-loopback clients」,直到你設定一個權杖(mcp.setToken,或 設定 → 伺服器 → MCP 伺服器)。
  • GET /healthz 依設計不需驗證,具有萬用字元 CORS,並會公布伺服器的 id、名稱、身分指紋與建置版本。用戶端需要指紋才能釘選伺服器,因此這是刻意的——但這也確實表示任何可觸達的 cc_server 都會向任何詢問的人揭露這麼多資訊。