Triển khai và máy khách
Control Center không phải là một chương trình duy nhất. Nó là một kiến trúc máy khách mỏng: một máy chủ không giao diện sở hữu dữ liệu của bạn, cùng một tập hợp máy khách dựng giao diện qua một kết nối RPC duy nhất tới máy chủ đó. Hiểu được sự tách bạch này sẽ lý giải vì sao một số thứ chạy ở nơi chúng chạy, và mỗi máy khách có thể với tới hay không với tới được những gì.
Máy chủ sở hữu dữ liệu
Phần tiêu đề “Máy chủ sở hữu dữ liệu”Một tiến trình có tên cc_server nắm giữ các cơ sở dữ liệu — một tệp
global.db nhỏ cho trạng thái toàn máy chủ, cộng thêm một tệp SQLite cho mỗi
không gian làm việc, được mở theo
kiểu lười — và chạy những công việc dài hạn: các engine quy trình, bộ đối chiếu,
bề mặt công cụ MCP và trình lắng nghe điều phối. Mọi thứ cần tồn tại lâu hơn một
cửa sổ, hoặc mà hai máy khách phải thấy giống hệt nhau, đều nằm ở đó.
Không máy khách nào tự mở tệp cơ sở dữ liệu. Thay vào đó, mỗi máy khách giữ một
RemoteRpcClient và phát các thao tác kho mã cùng các gói đăng ký qua nó.
Máy chủ xác thực kết nối dựa trên khóa chia sẻ trước của thiết bị đã ghép nối —
rồi không quan tâm tới kết nối đó nữa. Một phiên không bị ràng buộc vào một
không gian làm việc. Bộ điều phối là phi trạng thái: 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ó trong tham số, và lời
gọi nào thiếu trường đó sẽ bị từ chối.
Đó là chủ ý, và chính điều này cho phép hai máy khách trên cùng một máy chủ nằm ở các không gian làm việc khác nhau mà không cần máy chủ giữ một “không gian làm việc hiện tại” để chúng mâu thuẫn với nhau. Thay vào đó, việc cô lập được thi hành theo từng lời gọi, qua bốn bước:
- Thao tác tự khai báo liệu nó có thuộc phạm vi không gian làm việc hay không;
nếu có mà không có
workspace_idnào được gửi tới, lời gọi sẽ bị từ chối. - Id được nêu tên phải là một không gian làm việc đã đăng ký, và được kiểm
tra trước khi bất cứ thứ gì mở cơ sở dữ liệu — nếu không, một id không xác
định sẽ tạo ra một
workspace.dbrỗng ma trên mỗi yêu cầu. Tra không ra thì trả về not-found. WorkspaceRolecủa bên gọi phải đạt mức tối thiểu của thao tác, vốn được suy ra từ loại thao tác: đọc cần guest, thay đổi cần member, phá hủy cần admin.- Một thao tác để lộ nội dung kho mã còn đòi hỏi quyền cấp theo từng kho ở đúng
mức mà nó khai báo —
none/read/review/write— để tư cách thành viên không gian làm việc không bao giờ âm thầm vượt quyền máy chủ mã.
Câu cần nhớ: giữ khóa ghép nối không phải là ranh giới truy cập; tư cách thành viên mới là.
Vì sao lại tách theo cách này
Phần tiêu đề “Vì sao lại tách theo cách này”Ba sức ép đã đẩy kiến trúc về phía một máy chủ:
- Một nguồn sự thật duy nhất. Một máy tính và một điện thoại, hay một laptop và một máy tính, phải thấy cùng một trạng thái đội máy. Nếu mỗi bên sở hữu cơ sở dữ liệu riêng, chúng sẽ lệch nhau. Chỉ một máy chủ nghĩa là mọi máy khách đọc cùng một dữ liệu trực tiếp.
- Web không thể tự phục vụ. Trình duyệt không thể sinh tiến trình con hay mở tệp cục bộ, nên một bản dựng web không bao giờ có thể sở hữu cơ sở dữ liệu hay chạy agent. Nó buộc phải nói chuyện với một máy chủ làm được những việc đó. Thay vì dựng thêm một đường dẫn dữ liệu thứ hai chỉ dành cho web, ứng dụng máy tính được đưa lên cùng đường dẫn đó.
- Vận hành không giao diện. Một máy chủ bạn có thể chạy trên một chiếc máy nhỏ — với tới được từ điện thoại trên tàu hay từ trình duyệt ở bất cứ đâu — hữu ích hơn một GUI mà bạn phải để mở.
Đánh đổi là máy chủ giờ trở thành một phụ thuộc: nếu nó không chạy, mọi thứ đều không hoạt động. Câu trả lời mặc định của ứng dụng máy tính cho điều đó là tự sinh một máy chủ.
Các máy khách
Phần tiêu đề “Các máy khách”Năm máy khách kết nối tới máy chủ, mỗi loại có hình dạng và mức độ tin cậy khác nhau.
Ứng dụng máy tính
Phần tiêu đề “Ứng dụng máy tính”Ứng dụng máy tính viết bằng Flutter (macOS, Windows, Linux) là máy khách đầy đủ. Trong lần khởi chạy đầu tiên, nó hỏi bạn muốn chạy theo cách nào, rồi ghi nhớ lựa chọn đó:
- Cục bộ: sinh và giám sát một
cc_servertrên chính máy này — thiết lập mặc định, khép kín, dành cho một người dùng. Ứng dụng máy tính khởi chạy máy chủ; máy chủ sở hữu cơ sở dữ liệu nằm dưới thư mục gốc app-support và trao đổi với ứng dụng qua loopback. - Từ xa: kết nối tới một
cc_serverđang chạy ở nơi khác qua WebSocket bảo mật. Dữ liệu nằm trên máy chủ đó; máy tính này thuần túy chỉ dựng giao diện.
Trong cả hai trường hợp, máy khách RPC thu được sẽ thay thế nhà cung cấp dữ liệu của ứng dụng, nên toàn bộ giao diện — mọi màn hình, mọi tính năng — đều đọc và ghi qua máy chủ. Ứng dụng máy tính không bao giờ chạm trực tiếp vào tệp cơ sở dữ liệu.
Máy khách web
Phần tiêu đề “Máy khách web”Bản dựng web cũng chính là ứng dụng Flutter đó, được biên dịch để chạy trong
trình duyệt và lưu trữ tại app.usectrl.dev, nên không
có gì phải cài đặt. Trình duyệt không thể sinh tiến trình con, vì vậy máy khách
web luôn ở chế độ từ xa: nó quay số tới một cc_server qua wss:// và dựng
toàn bộ giao diện máy tính. Sau khi kết nối, nó hiển thị đúng những màn hình đó
trên cùng một đội máy, bởi vì đó là cùng một đoạn mã chạy trên cùng một RPC.
Những khoảng trống chỉ là vài chỗ cần một năng lực gốc mà trình duyệt không có —
lớp chất lượng ghi âm cuộc họp ở phần dưới là chỗ chính, và thông báo hệ điều
hành là chỗ khác.
Đây là lý do “không cần tải về” và “không cần máy chủ” là hai khẳng định khác
nhau. Máy khách web loại bỏ việc cài đặt; nó không loại bỏ máy chủ. Thứ nó quay
số tới có thể là một cc_server trên chính chiếc laptop đó — ws://localhost:9030,
vốn không cần chứng chỉ vì trình duyệt coi loopback là đáng tin — hoặc một máy
chủ trên máy khác, khi đó phải với tới được qua wss:// với chứng chỉ mà trình
duyệt tin cậy. cc_server từ chối bind một địa chỉ không phải loopback nếu không
có TLS, nên máy chủ từ xa đồng nghĩa với việc nó có chứng chỉ riêng, một proxy
kết thúc TLS, hoặc một tunnel hay VPN cung cấp chứng chỉ đó.
Bản bundle được lưu trữ tách biệt khỏi máy chủ mà nó nói chuyện cùng. Một
cc_server độc lập phục vụ điểm cuối RPC, bề mặt MCP và các proxy của nó, nhưng
không phục vụ bundle web — đường dẫn tệp tĩnh có tồn tại trong máy chủ, song
không có web root nào được cấu hình trên tệp nhị phân độc lập, nên nó không có gì
để phục vụ tại /. Ảnh container cc-webapp đã phát hành là thứ lưu trữ bundle,
và scripts/build_web.sh là thứ dựng nó. Máy chủ chỉ chấp nhận các origin của
trình duyệt nằm trong danh sách --allowed-origins, mặc định là
https://app.usectrl.dev, nên tự lưu trữ bundle nghĩa là phải thêm origin của nó
vào danh sách.
Phương tiện (ảnh đại diện, ảnh bảng tin, ảnh chụp PR) đi qua điểm cuối proxy của máy chủ thay vì gọi thẳng tới các máy chủ thượng nguồn, nên trình duyệt không bao giờ tải từ một origin tùy ý. Máy khách đã triển khai gắn một Content-Security Policy riêng cho từng yêu cầu, lấy từ một cookie không nhạy cảm, để chỉ origin của máy chủ đang kết nối được phép.
Ứng dụng đồng hành trên điện thoại (cc_remote)
Phần tiêu đề “Ứng dụng đồng hành trên điện thoại (cc_remote)”Ứng dụng Remote là một máy khách riêng, nhẹ hơn — một PWA Flutter web tại remote.usectrl.dev. Nó không phải bản ứng dụng đầy đủ, và đó là chủ ý. Nó ghép nối qua một relay WebSocket được môi giới, niêm phong đầu-cuối, và chỉ nói một lát cắt thiên về đọc của bề mặt công cụ: đọc phiếu, tin nhắn và bảng tin; gửi trả lời hoặc cập nhật một phiếu.
Một chiếc điện thoại là chủ thể có đặc quyền thấp hơn so với một agent cục bộ. Chính sách công cụ từ chối mặc định, khóa chia sẻ trước niêm phong mọi khung, một năng lực phiên khóa các thao tác đặc quyền (chẳng hạn ghép nối) khỏi máy khách đồng hành, các kiểm tra vai trò và quyền cấp theo kho ở trên, cùng việc giới hạn tần suất — tất cả giữ cho một lần ghép nối đã được duyệt (hay bị lộ) không biến thành toàn quyền điều khiển từ xa. Xem Điều khiển từ xa và di động để biết đầy đủ mô hình bảo mật.
Máy khách MCP
Phần tiêu đề “Máy khách MCP”Các công cụ bên ngoài — trình soạn thảo, agent khác, CLI — nói được Model Context Protocol sẽ kết nối tới bề mặt MCP của máy chủ và dùng sổ đăng ký công cụ có kiểu. Đây là đường tích hợp lập trình; nó không phải một GUI. Xem Dùng máy chủ MCP và Tham chiếu công cụ MCP.
Máy thực thi đội máy (cc_worker)
Phần tiêu đề “Máy thực thi đội máy (cc_worker)”Một cc_worker là tệp nhị phân Dart thuần, không giao diện, ghép nối với một
cc_server, khai báo các năng lực của máy chủ nhà rồi kéo về những công việc đã
được thuê để thực thi. Nó truyền sự kiện tiến trình về qua chính RPC đó và không
giữ trạng thái bền vững nào: không cơ sở dữ liệu, không xác thực, không phê
duyệt, không ngân sách — những thứ đó không bao giờ rời khỏi cc_server. Một
worker là một cánh tay, không phải bộ não thứ hai. Một máy chủ có thẩm quyền, N
cánh tay ngờ nghệch; không có đồng thuận, không có lưu lượng worker-với-worker.
Thực thi theo hợp đồng thuê vẫn chỉ là phần đường ống của việc thuê. agentRun là
loại duy nhất chạy một lệnh thật (CC_JOB_COMMAND, hoặc một prompt được lặp lại
nếu không có lệnh nào được cung cấp). pipelineStep, codeIndex, goldenRender,
benchmark và evalBatch hiện chỉ dò bằng git --version / rev-parse HEAD để
đường truyền phát được chạy thử mà không giả vờ rằng các công việc đó đã thực sự
chạy.
Những gì nó quảng bá là hệ điều hành, kiến trúc CPU, số nhân, RAM, việc có với tới
được một Flutter SDK hay không, và máy chủ nhà có những backend sandbox nào (không
có trên Windows). Một worker chuyên dụng còn luôn quảng bá alwaysOn và
acceptsParallel, và đó là những gì bộ lập lịch pin → prefer → spill dựa vào để
xếp việc. Không có phép dò GPU hay ML nào.
Một máy tính chạy đơn lẻ không hề nhận ra những điều này, bởi việc điều phối thông
thường hoàn toàn không đi qua đội máy. Đúng là có một worker ngầm trong cùng
tiến trình được đăng ký cho máy chủ nhà, nhưng nó chỉ là một điểm nối đăng ký
không có runner nào được mắc vào — một công việc thực sự được xếp lên worker cục
bộ sẽ thất bại thay vì được thực thi. Hiện nay, chỉ một cc_worker thật mới thực
thi các công việc được thuê.
Cái gì chạy ở đâu
Phần tiêu đề “Cái gì chạy ở đâu”| Mối quan tâm | Máy chủ (cc_server) |
Máy thực thi đội máy (cc_worker) |
Máy khách máy tính | Máy khách web / điện thoại |
|---|---|---|---|---|
| Cơ sở dữ liệu (Drift/SQLite) | sở hữu chúng | — | — | — |
| Xác thực, phê duyệt, ngân sách, danh tính | sở hữu | — | — | — |
| Quy trình, bộ đối chiếu, trình lắng nghe điều phối | chạy chúng | — | — | — |
| Bề mặt công cụ MCP | phục vụ chúng | — | — | — |
| Thực thi agent | chạy chúng (hoặc cho thuê ra ngoài) | thực thi các công việc được thuê | — | — |
| Dựng giao diện | — | — | đầy đủ | đầy đủ (web) / thiên về đọc (điện thoại) |
| Ghi âm cuộc họp (mic + âm thanh cuộc họp) | — | — | ghi âm gốc | chỉ trên web, qua âm thanh chia sẻ màn hình |
| OAuth lịch (Google) | lưu token | — | — | — |
Hai tầng không sandbox giống nhau. cc_server bọc những lượt chạy mà nó tự thực
thi trong sandbox của hệ điều hành máy chủ nhà khi có backend — Seatbelt trên
macOS, bubblewrap trên Linux và WSL2 — và lùi về các ranh giới khác trên Windows
hoặc khi thiếu bwrap và socat. cc_worker hoàn toàn không sandbox. Nó
quảng bá một năng lực sandbox để máy chủ có thể xếp việc theo nền tảng, nhưng
không có gì trong worker áp dụng năng lực đó: một công việc được thuê được sinh
thẳng ra, chỉ bị giới hạn bởi môi trường của hợp đồng thuê và worktree mà nó được
cấp. Vì vậy, cho thuê công việc ra ngoài sẽ đẩy việc thực thi ra khỏi sandbox ngay
cả trên một máy chủ nhà có sandbox. Xem
Sandbox và bảo mật để biết điều gì
giới hạn một lượt chạy trong từng trường hợp.
Việc ghi âm cuộc họp diễn ra trên máy khách vì nó cần micro và âm thanh của cuộc họp — một máy chủ nằm trong tủ đựng chổi không thể ghi lại cuộc gọi của bạn. Bản ghi sau đó được quy trình của máy chủ tóm tắt, và ghi chú được lưu ở nơi mọi máy khách đều đọc được.
Máy khách web cũng ghi âm, điều mà hình dạng của bảng có thể che khuất: nó thu micro cùng một track âm thanh chia sẻ màn hình ngay trong trình duyệt rồi truyền PCM 16 kHz tới máy chủ. Nó bắt buộc phải có track âm thanh đó, nên sẽ thất bại rõ ràng ở những nơi trình duyệt không cung cấp được. Điều thực sự chỉ có trên máy tính là lớp chất lượng quanh việc ghi âm — khử vọng ở mức tín hiệu qua bộ xử lý gốc, đồng hồ đo mức đầu vào và cảnh báo mic chết, chạy lại một bản tóm tắt, hủy xử lý, và thao tác ghi-rồi-liên kết của lịch.
Ghép nối và tin cậy
Phần tiêu đề “Ghép nối và tin cậy”Mọi máy khách chứng minh danh tính của mình bằng một khóa chia sẻ trước được cấp một lần:
- Ứng dụng máy tính ở chế độ cục bộ tạo một khóa mới mỗi lần khởi động rồi đưa nó cho máy chủ được sinh ra qua biến môi trường, nên không có gì bí mật bị lưu lại trên máy tính.
- Các máy khách từ xa (máy tính ở chế độ từ xa, web, điện thoại) dùng một khóa do
lệnh
paircủa máy chủ tạo ra và lưu trong keychain của hệ điều hành (hoặc, trên điện thoại, lưu cục bộ).
Việc thu hồi diễn ra trực tiếp, không chậm trễ. Máy chủ theo dõi bảng thiết bị đã ghép nối và cắt mọi phiên đang mở mà thiết bị của nó đã rời khỏi tập đang hoạt động, trong vòng vài giây — trên cả đường WebSocket trực tiếp lẫn đường relay. Bạn không phải chờ thiết bị kết nối lại mới cắt được nó.
Khóa không bao giờ đi qua một URL mà máy chủ tĩnh có thể thấy. Khi nó đến qua liên
kết — trên điện thoại hoặc trong trình duyệt — nó nằm trong URL fragment, phần
sau dấu #, thứ mà trình duyệt không bao giờ gửi tới máy chủ lưu trữ trang. Đó là
bất biến đáng ghi nhớ, và nó đúng cho cả hai. Máy khách web còn hai điểm vào khác:
biểu mẫu kết nối, nơi bạn dán trực tiếp máy chủ và khóa, cùng một mã mời mà nó đổi
lấy thông tin xác thực được tạo riêng cho trình duyệt đó.
Các khái niệm liên quan
Phần tiêu đề “Các khái niệm liên quan”- Không gian làm việc và cô lập: vì sao mọi lời gọi RPC đều mang id không gian làm việc của riêng nó và cách việc tách cơ sở dữ liệu khiến cô lập trở thành cấu trúc
- Kiến trúc: cách cơ sở mã được tổ chức quanh máy chủ và các máy khách của nó
- Điều khiển từ xa và di động: mô hình ghép nối điện thoại và bảo mật
Các hướng dẫn liên quan
Phần tiêu đề “Các hướng dẫn liên quan”- Chạy máy chủ không giao diện: khởi chạy
cc_servervà ghép nối một máy khách - Kết nối máy chủ từ xa: trỏ máy khách máy tính hoặc web tới một máy chủ ở nơi khác
- Ghép nối thiết bị: thêm một điện thoại, một trình duyệt thứ hai, hoặc một máy tính khác
- Chạy một worker đội máy: thêm một
cc_workerđể kéo về những công việc được thuê