Bỏ qua để đến nội dung

Chạy máy chủ không giao diện

Chạy cc_server trên một chiếc máy luôn bật, rồi kết nối tới nó từ máy tính, trình duyệt hoặc điện thoại của bạn. Máy chủ là một tệp nhị phân native viết thuần Dart — không có engine Flutter — nên nó chạy không giao diện trên macOS, Linux hoặc Windows. Nó sở hữu cơ sở dữ liệu, chạy các agent và thực hiện mọi lời gọi API bên ngoài; các máy khách chỉ dựng giao diện.

Mỗi bản phát hành công bố một bộ ngăn xếp tự lưu trữ gồm bốn ảnh GHCR — backend, hai máy khách mỏng tĩnh và bộ chuyển tiếp ghép nối tùy chọn — cùng một ảnh cc-server-demo riêng cho máy chủ demo công khai. Một bản cài đặt tự lưu trữ không cần checkout mã nguồn. Hãy ghim một thẻ phiên bản (ghcr.io/samuelalev/<image>:v0.0.1-rc.1) hoặc dùng latest.

Ảnh Là gì Cổng công bố
ghcr.io/samuelalev/cc-server Backend: cơ sở dữ liệu, agent, MCP, webhook -p 9030:9030
ghcr.io/samuelalev/cc-webapp Máy khách web (nginx tĩnh) -p 8080:8080
ghcr.io/samuelalev/cc-remote PWA đồng hành trên điện thoại (nginx tĩnh) -p 8081:8080
ghcr.io/samuelalev/cc-signaling-server Bộ chuyển tiếp ghép nối WebRTC (tùy chọn) -p 8788:8788

Toàn bộ ngăn xếp dưới dạng một tệp 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:

Những điều đáng biết trước khi bạn để lộ bất kỳ phần nào trong số này ra ngoài loopback:

  • Ảnh cc-server đi kèm CC_SERVER_INSECURE=1 (nó phải bind 0.0.0.0 để nhận lưu lượng qua cổng đã công bố), điều chỉ an toàn phía sau một reverse proxy kết thúc TLS — Caddy, Traefik, nginx hoặc Cloudflare Tunnel — trên một mạng tin cậy. Để kết thúc TLS ngay trong tiến trình thay vào đó, hãy gắn một chứng chỉ và đặt CC_SERVER_TLS_CERT / CC_SERVER_TLS_KEY (rồi bỏ đặt CC_SERVER_INSECURE). Mọi thứ đều có thể ghi đè bằng docker run -e … hoặc khối environment: của compose.

  • /data là trạng thái duy nhất. Các cơ sở dữ liệu SQLite, bí mật của thiết bị đã ghép nối, mô hình đã tải về và âm thanh cuộc họp đều nằm trong volume — hãy sao lưu nó và container trở nên có thể vứt bỏ.

  • Ghép nối một máy khách từ bên trong ảnh trước kết nối đầu tiên, tương đương với bước 3 trong container:

    Cửa sổ terminal
    docker run --rm -it -v cc_data:/data \
    --entrypoint /app/bin/cc_server ghcr.io/samuelalev/cc-server:latest pair
  • Hai ảnh máy khách là những máy chủ nginx tĩnh — không có gì phải cấu hình lúc chạy; biểu mẫu kết nối của mỗi máy khách nhận URL máy chủ, id thiết bị và khóa ghép nối của bạn. Bộ chuyển tiếp signaling cũng chỉ cần cổng của nó (SIGNALING_HOST / SIGNALING_PORT có thể ghi đè); nó phi trạng thái, không giữ bí mật nào và là một phương án dự phòng — một chiếc điện thoại dùng loopback, LAN, tailnet hoặc đường wss:// trực tiếp khi có đường nào với tới được, và chỉ quay về bộ chuyển tiếp khi không có đường nào. Hãy bỏ qua nó nếu các máy khách của bạn luôn với tới máy chủ trực tiếp.

Phần còn lại của hướng dẫn này dựng máy chủ từ mã nguồn; các cờ và hành vi mà nó đề cập cũng chính là những thứ mà các biến môi trường của ảnh đưa vào.

  • SDK Dart. Kho mã này ghim SDK của nó bằng fvm, nên hãy thêm tiền tố fvm vào mọi lệnh.
  • Kho mã này đã được checkout.
  • Bộ công cụ native mà script staging cần — Rust/cargo, cùng meson, ninja, pkg-config, git và một trình biên dịch C++. Mỗi script build đều nêu rõ thứ nó không tìm thấy và cách cài đặt.
  • Một máy chủ nhà có thể với tới: loopback cho dùng cục bộ, hoặc một địa chỉ LAN/công khai kèm TLS cho các máy khách từ xa.

Các thư viện native là bắt buộc — không có chế độ suy giảm nào. Hãy dựng chúng từ gốc kho mã trước mọi việc khác:

Cửa sổ terminal
scripts/natives/build_natives.sh

Lệnh này dựng rift, fff, tree-sitter cùng các ngữ pháp của nó, aec, lame, ccpty, cc_watcher, cc_inference và thư viện native SAML vào build/natives/. Hook build của cc_server phát lại mọi thứ được chuẩn bị ở đó vào bundle, nên bỏ qua bước này hoặc làm build thất bại hoàn toàn, hoặc — với lối thoát bên dưới — tạo ra một tệp nhị phân mà bước kiểm tra trước khởi động từ chối chạy và nêu tên thư viện còn thiếu.

Cửa sổ terminal
cd apps/cc_server
fvm dart build cli

Bản bundle đi kèm libsqlite3 và các thư viện native đã chuẩn bị bên cạnh tệp nhị phân — không cần SQLite hệ thống và không cần engine Flutter. Tệp thực thi nằm tại ./build/cli/<os_arch>/bundle/bin/cc_server.

Bước 3: Ghép nối một máy khách trước lần khởi động đầu tiên

Phần tiêu đề “Bước 3: Ghép nối một máy khách trước lần khởi động đầu tiên”

Một thư mục dữ liệu mới chưa có thiết bị nào được ghép nối và không có khóa chia sẻ trước, nên lời nhắc nhập khóa ghép nối của máy khách chưa thể được đáp ứng. Hãy tạo một khóa bằng lệnh con pair. Nó ghi trực tiếp global.db và secrets.json; một máy chủ đang phục vụ thư mục dữ liệu đó sẽ nhận thiết bị mới mà không cần khởi động lại:

Cửa sổ terminal
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030

Nó in ra ba giá trị để dán vào biểu mẫu kết nối của máy khách:

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

Các cờ hữu ích: --device (mặc định web-client; id cũng chọn hạng thiết bị — desktop, ios, android, bất kỳ giá trị nào khác là máy khách web), --label (mặc định là tên nền tảng cộng với hostname của máy này), --host (máy chủ để in trong URL, vì loopback không với tới được từ máy khác) và --client-url. Chạy lại pair sẽ xoay khóa của thiết bị đó và hủy các phiên đang hoạt động của nó. Việc ghép nối không bao giờ tạo không gian làm việc: phần hướng dẫn ban đầu của máy khách kết nối sẽ đặt tên và tạo không gian làm việc đầu tiên.

Với --client-url, nó cũng in một mã QR quét được từ terminal để mở máy khách web với các trường đã điền sẵn:

Cửa sổ terminal
./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

Thông tin xác thực do cc_server pair tạo không có thời hạn; thông tin xác thực tạo trong ứng dụng luôn hết hạn sau 30 ngày. Hãy thu hồi thiết bị do CLI tạo từ Cài đặt → Bạn → Thiết bị của bạn khi bạn không cần nữa.

Trên loopback — mặc định và là tất cả những gì bạn cần cho một máy khách trên cùng máy:

Cửa sổ terminal
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030

Để với tới nó từ máy khác, hãy bind mọi giao diện. Một bind không phải loopback mà không có TLS sẽ bị từ chối ngay khi khởi động (Refusing to bind non-loopback address … without TLS), nên hãy cung cấp một chứng chỉ:

Cửa sổ terminal
./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

Hoặc kết thúc TLS trong một reverse proxy trên mạng riêng đáng tin cậy và chọn một bind dạng thuần phía sau nó:

Cửa sổ terminal
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \
--bind any --insecure --public-url wss://server.example/rpc

--insecure bị bỏ qua khi cả --tls-cert và --tls-key đều được đặt — TLS luôn thắng. Máy chủ quảng bá trạng thái thuần là insecure: true trong GET /healthz và các máy khách đánh dấu kết nối như vậy là không an toàn.

Máy chủ chạy cho tới khi nhận SIGINT/SIGTERM, rồi tắt một cách sạch sẽ.

Trỏ một máy khách tới URL máy chủ và khóa ghép nối từ bước 3:

  • Ứng dụng máy tính: trên màn hình lần chạy đầu tiên (Control Center nên chạy như thế nào?), hãy điền vào các trường kết nối thay vì Chạy trong ứng dụng này, hoặc thêm máy chủ sau tại Cài đặt → Máy chủ → Kết nối & trạng thái. Xem Kết nối máy chủ từ xa.
  • Máy khách web: mở app.usectrl.dev (hoặc bản sao bản dựng web của riêng bạn) và nhập URL máy chủ, id thiết bị và khóa ghép nối.
  • Điện thoại: ghép nối ứng dụng Remote từ một máy khách đang chạy — xem Ghép nối thiết bị.

Bước 6: Kết nối một Google Calendar (tùy chọn)

Phần tiêu đề “Bước 6: Kết nối một Google Calendar (tùy chọn)”

Để đồng bộ lịch của một không gian làm việc mà không cần giao diện, hãy dùng luồng mã thiết bị:

Cửa sổ terminal
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>

Nó in ra một mã và một URL để phê duyệt trên thiết bị khác, lưu refresh token ở phía máy chủ rồi thoát. Một bản dựng từ mã nguồn không kèm máy khách Google nào, nên các biến môi trường id và secret là bắt buộc — không có cờ --google-client-id. Xem Kết nối Google Calendar.

Các cờ dùng ở trên là những cờ mà lần thiết lập đầu tiên cần. Toàn bộ bề mặt — 23 cặp cờ và biến môi trường, bốn lệnh con và cách bố trí thư mục dữ liệu — nằm trong tham chiếu CLI cc_server.

Bốn cờ bạn có nhiều khả năng cần nhất:

Cờ Mặc định Ý nghĩa
--data-dir thư mục dữ liệu ứng dụng theo từng người dùng của hệ điều hành Cơ sở dữ liệu, bí mật, mô hình và nội dung đa phương tiện được lưu đệm
--port 9030 Cổng TCP (0 = tạm thời)
--bind loopback loopback, hoặc any cho mọi giao diện (cần TLS hoặc --insecure)
--public-url được suy ra từ bind URL RPC được quảng bá cho các máy khách đã ghép nối. Hãy đặt tường minh khi đứng sau proxy, NAT hoặc tunnel

Thư mục dữ liệu mặc định là ~/Library/Application Support/control-center trên macOS, %APPDATA%\control-center trên Windows và $XDG_DATA_HOME/control-center (nếu không thì ~/.local/share/control-center) ở những nơi khác. Một .cc_server tương đối theo cwd chỉ được dùng khi không phân giải được thư mục home hay thư mục dữ liệu ứng dụng nào.

cc_server là toàn bộ bề mặt sản phẩm chứ không phải một tập con: đường dữ liệu repo-RPC cùng các gói đăng ký, điểm cuối công cụ MCP tại POST /mcp và GET /sse trên cùng cổng, các webhook đến, proxy nội dung đa phương tiện và phông chữ, callback đăng nhập một lần, giao thức thuê của đội máy, việc lấy RSS phía máy chủ theo lịch 30 phút và tìm kiếm vector (tiện ích mở rộng sqlite_vector được đăng ký như một auto-extension toàn tiến trình, chỉ suy giảm xuống tìm kiếm toàn văn ở nơi không có tiện ích mở rộng đó).

Bộ điều phối là phi trạng thái: không có ràng buộc không gian làm việc theo từng phiên. Mọi lời gọi thuộc phạm vi không gian làm việc đều mang workspace_id của riêng nó, và quyền truy cập được quyết định bởi tư cách thành viên và vai trò không gian làm việc của bên gọi — chứ không phải bởi việc giữ một khóa ghép nối.

Hai điều cần biết trước khi bạn để lộ cổng ra ngoài loopback:

  • MCP đóng an toàn khi ở ngoài máy chủ. Một bề mặt MCP không token chỉ trả lời loopback. Việc bind any không mở rộng điều đó — một yêu cầu từ ngoài máy chủ bị từ chối với thông báo “MCP requires a bearer token to answer non-loopback clients” cho tới khi bạn cấu hình một token (mcp.setToken, hoặc Cài đặt → Máy chủ → Máy chủ MCP).
  • GET /healthz không yêu cầu xác thực theo thiết kế, có CORS ký tự đại diện và công bố id, tên, dấu vân tay danh tính và phiên bản build của máy chủ. Các máy khách cần dấu vân tay để ghim máy chủ, nên đây là chủ ý — nhưng điều đó cũng có nghĩa là bất kỳ cc_server nào với tới được cũng tiết lộ chừng đó cho bất kỳ ai hỏi.