Deployment dan klien
Control Center bukan satu program. Control Center adalah arsitektur thin-client: server headless yang memiliki data Anda dan sekumpulan klien yang menampilkan antarmuka melalui satu koneksi RPC ke server itu. Memahami pemisahan ini menjelaskan mengapa sebagian hal berjalan di tempat tertentu dan apa yang dapat dan tidak dapat dijangkau setiap klien.
Server memiliki data
Bagian berjudul “Server memiliki data”Sebuah proses bernama cc_server memegang basis data — global.db kecil
untuk status seluruh server plus satu file SQLite per ruang kerja,
yang dibuka secara lazy — dan menjalankan pekerjaan berumur panjang: mesin
pipeline, reconciler, permukaan alat MCP, dan listener orkestrasi. Semua yang
perlu bertahan lebih lama dari sebuah jendela, atau yang harus dilihat identik
oleh dua klien, berada di sana.
Tidak ada klien yang membuka file basis data sendiri. Sebaliknya, setiap klien
memegang satu RemoteRpcClient dan mengirim operasi repo serta langganan
melaluinya. Server mengautentikasi koneksi terhadap kunci pre-shared perangkat
yang dipasangkan — lalu berhenti memedulikan koneksi itu. Sebuah sesi tidak
terikat pada ruang kerja. Dispatcher bersifat stateless: setiap panggilan
berlingkup ruang kerja membawa workspace_id-nya sendiri di argumennya dan
panggilan yang menghilangkannya akan ditolak.
Hal itu disengaja dan itulah yang memungkinkan dua klien di satu server berada di ruang kerja berbeda tanpa “ruang kerja saat ini” yang dipegang server untuk mereka perselisihkan. Isolasi ditegakkan per panggilan, dalam empat langkah:
- Operasi mendeklarasikan apakah operasinya berlingkup ruang kerja; jika ya
dan tidak ada
workspace_idyang datang, panggilan ditolak. - Id yang disebutkan harus merupakan ruang kerja yang terdaftar, diperiksa
sebelum apa pun membuka basis data — jika tidak, id yang tidak dikenal akan
mewujudkan
workspace.dbhantu yang kosong pada setiap permintaan. Pencarian yang meleset berarti tidak ditemukan. WorkspaceRolepemanggil harus memenuhi ambang minimum operasi, yang diturunkan dari jenisnya: baca butuh guest, mutasi butuh member, destruktif butuh admin.- Operasi yang mengekspos konten repo juga memerlukan hibah per repo pada
tingkat yang dideklarasikannya —
none/read/review/write— sehingga keanggotaan ruang kerja tidak pernah diam-diam memberi hak istimewa melebihi forge.
Kalimat yang penting: memegang kunci pairing bukanlah batas akses; keanggotaanlah batasnya.
Mengapa dipecah seperti ini
Bagian berjudul “Mengapa dipecah seperti ini”Tiga tekanan mendorong ke arah server:
- Satu sumber kebenaran. Desktop dan ponsel, atau laptop dan desktop, harus melihat status fleet yang sama. Jika masing-masing memiliki basis datanya sendiri, keduanya akan menyimpang. Satu server berarti setiap klien membaca data langsung yang sama.
- Web tidak bisa melayani dirinya sendiri. Peramban tidak dapat menjalankan subproses atau membuka file lokal, jadi build web tidak akan pernah bisa memiliki basis data atau menjalankan agen. Web harus berbicara dengan server yang melakukannya. Alih-alih membangun jalur data kedua yang hanya untuk web, desktop dibawa ke jalur yang sama.
- Operasi headless. Server yang dapat Anda jalankan di mesin kecil — dapat dijangkau dari ponsel di kereta atau peramban di mana pun — lebih berguna daripada GUI yang harus Anda biarkan terbuka.
Komprominya adalah server kini menjadi dependensi: jika server tidak berjalan, tidak ada yang berfungsi. Jawaban bawaan desktop untuk hal itu adalah menjalankan servernya sendiri.
Lima klien menjangkau server, masing-masing dengan bentuk dan profil kepercayaan yang berbeda.
Aplikasi desktop
Bagian berjudul “Aplikasi desktop”Aplikasi desktop Flutter (macOS, Windows, Linux) adalah klien lengkap. Saat pertama kali dijalankan, aplikasi ini menanyakan bagaimana ia harus berjalan, lalu mengingat pilihannya:
- Local: jalankan dan awasi
cc_serverdi mesin ini — penyiapan bawaan, mandiri, dan untuk pengguna tunggal. Desktop menjalankan server, yang memiliki basis data di bawah root app-support dan berbicara dengannya melalui loopback. - Remote: hubungkan ke
cc_serveryang berjalan di tempat lain melalui WebSocket aman. Data berada di server itu; desktop ini murni sebuah perender.
Dalam kedua kasus, klien RPC yang dihasilkan menggantikan penyedia data aplikasi, sehingga seluruh UI — setiap layar, setiap fitur — membaca dan menulis melalui server. Desktop tidak pernah menyentuh file basis data secara langsung.
Klien web
Bagian berjudul “Klien web”Build web adalah aplikasi Flutter yang sama yang dikompilasi untuk berjalan di
peramban, dihosting di app.usectrl.dev sehingga tidak
ada yang perlu diinstal. Peramban tidak dapat menjalankan subproses, jadi klien
web selalu remote: klien ini menghubungi cc_server melalui wss:// dan
menampilkan antarmuka desktop lengkap. Setelah terhubung, klien ini menampilkan
layar yang sama terhadap fleet yang sama, karena kodenya sama melalui RPC yang
sama. Kesenjangannya adalah segelintir tempat yang memerlukan kemampuan native
yang tidak dimiliki peramban — lapisan kualitas perekaman rapat di bawah adalah
yang utama dan notifikasi OS adalah yang lain.
Inilah sebabnya “tanpa unduhan” dan “tanpa server” adalah klaim yang berbeda.
Klien web menghilangkan instalasi; klien ini tidak menghilangkan server. Yang
dihubunginya bisa berupa cc_server di laptop yang sama — ws://localhost:9030,
yang tidak memerlukan sertifikat karena peramban menganggap loopback dapat
dipercaya — atau server di mesin lain, yang harus dapat dijangkau melalui
wss:// dengan sertifikat yang dipercaya peramban. cc_server menolak
mengikat alamat non-loopback tanpa TLS, jadi server jarak jauh berarti entah
sertifikatnya sendiri, proxy terminasi TLS, atau tunnel atau VPN yang
menyediakannya.
Bundle itu sendiri dihosting terpisah dari server yang diajaknya bicara.
cc_server mandiri menyajikan endpoint RPC, permukaan MCP, dan proksinya,
tetapi tidak menyajikan bundle web — jalur file statis ada di server, tetapi
tidak ada web root yang dikonfigurasi pada binary mandiri, jadi tidak ada yang
bisa disajikannya di /. Image kontainer cc-webapp yang diterbitkan adalah
yang menghosting bundle dan scripts/build_web.sh adalah yang membangunnya.
Server hanya menerima origin peramban pada daftar --allowed-origins-nya, yang
bawaannya https://app.usectrl.dev, jadi menghosting sendiri bundle berarti
menambahkan origin-nya.
Media (avatar, gambar feed, tangkapan layar PR) dirutekan melalui endpoint proxy server alih-alih memanggil host hulu secara langsung, sehingga peramban tidak pernah mengambil origin sembarangan. Klien yang di-deploy menyematkan Content-Security Policy per permintaan dari cookie non-sensitif sehingga hanya origin server yang terhubung yang diizinkan.
Pendamping ponsel (cc_remote)
Bagian berjudul “Pendamping ponsel (cc_remote)”Aplikasi Remote adalah klien terpisah yang lebih ringan — PWA web Flutter di remote.usectrl.dev. Aplikasi ini sengaja bukan aplikasi lengkapnya. Aplikasi ini dipasangkan melalui relay WebSocket yang di-broker dan disegel end-to-end, lalu berbicara dengan sebagian besar operasi baca dari permukaan alat: membaca tiket, pesan, dan umpan berita; mengirim balasan atau memperbarui tiket.
Ponsel adalah principal dengan hak istimewa lebih rendah daripada agen lokal. Kebijakan alat yang menolak secara bawaan, kunci pre-shared yang menyegel setiap frame, kapabilitas sesi yang mengunci operasi berhak istimewa (misalnya pairing) dari klien pendamping, pemeriksaan peran dan hibah repo per panggilan di atas, serta pembatasan laju mencegah pairing yang disetujui (atau bocor) menjadi kontrol jarak jauh penuh. Lihat Kontrol jarak jauh dan seluler untuk model keamanan selengkapnya.
Klien MCP
Bagian berjudul “Klien MCP”Alat eksternal — editor, agen lain, CLI — yang berbicara Model Context Protocol terhubung ke permukaan MCP server dan mengonsumsi registri alat bertipe. Ini adalah jalur integrasi programatik; ini bukan GUI. Lihat Gunakan server MCP dan Referensi alat MCP.
Eksekutor fleet (cc_worker)
Bagian berjudul “Eksekutor fleet (cc_worker)”Sebuah cc_worker adalah binary pure-Dart headless yang dipasangkan dengan
cc_server, mendeklarasikan kapabilitas host-nya, dan menarik pekerjaan sewaan
untuk dieksekusi. Worker ini mengalirkan peristiwa proses kembali melalui RPC
yang sama dan tidak menyimpan state permanen: tidak ada basis data, tidak
ada autentikasi, tidak ada persetujuan, tidak ada anggaran — semua itu tidak
pernah meninggalkan cc_server. Worker adalah lengan, bukan otak kedua. Satu
server otoritatif, N lengan bodoh; tidak ada konsensus, tidak ada lalu lintas
antar-worker.
Eksekusi sewaan tetaplah perpipaan sewa. agentRun adalah satu-satunya jenis
yang menjalankan perintah sungguhan (CC_JOB_COMMAND, atau prompt yang digemakan
jika tidak ada yang diberikan). pipelineStep, codeIndex, goldenRender,
benchmark dan evalBatch saat ini melakukan probe dengan git --version /
rev-parse HEAD agar jalur streaming teruji tanpa berpura-pura pekerjaan
tersebut benar-benar berjalan.
Yang diiklankannya adalah sistem operasi, arsitektur CPU, jumlah inti, RAM,
apakah Flutter SDK dapat dijangkau dan backend sandbox apa saja yang dimiliki
host (tidak ada di Windows). Worker khusus juga selalu mengiklankan alwaysOn
dan acceptsParallel, yang menjadi acuan penempatan oleh penjadwal
pin → prefer → spill. Tidak ada probe GPU atau ML.
Desktop tunggal tidak menyadari semua ini, karena pengiriman biasa tidak
melewati fleet sama sekali. Memang ada worker in-process implisit yang
terdaftar untuk host server, tetapi itu hanyalah titik sambung pendaftaran tanpa
runner yang terhubung ke dalamnya — pekerjaan yang benar-benar ditempatkan pada
worker lokal akan gagal alih-alih dieksekusi. Saat ini, hanya cc_worker
sungguhan yang mengeksekusi pekerjaan sewaan.
Apa yang berjalan di mana
Bagian berjudul “Apa yang berjalan di mana”| Perihal | Server (cc_server) |
Eksekutor fleet (cc_worker) |
Klien desktop | Klien web / ponsel |
|---|---|---|---|---|
| Basis data (Drift/SQLite) | memilikinya | — | — | — |
| Autentikasi, persetujuan, anggaran, identitas | memilikinya | — | — | — |
| Pipeline, reconciler, listener orkestrasi | menjalankannya | — | — | — |
| Permukaan alat MCP | menyajikannya | — | — | — |
| Eksekusi agen | menjalankannya (atau menyewakannya) | mengeksekusi pekerjaan sewaan | — | — |
| Perenderan UI | — | — | lengkap | lengkap (web) / sebagian besar baca (ponsel) |
| Perekaman rapat (mikrofon + audio rapat) | — | — | perekaman native | hanya web, melalui audio berbagi layar |
| OAuth kalender (Google) | menyimpan token | — | — | — |
Kedua tingkat ini tidak melakukan sandbox dengan cara yang sama. cc_server
membungkus jalan yang dieksekusinya sendiri dalam sandbox OS host di tempat
backend tersedia — Seatbelt di macOS, bubblewrap di Linux dan WSL2 — dan jatuh
kembali ke batas lain di Windows atau tanpa bwrap dan socat. cc_worker
tidak melakukan sandbox sama sekali. Worker mengiklankan kapabilitas sandbox
agar server dapat menempatkan pekerjaan berdasarkan platform, tetapi tidak ada
apa pun di dalam worker yang menerapkannya: pekerjaan sewaan dijalankan
langsung, dibatasi oleh lingkungan sewa dan worktree yang diberikan kepadanya.
Menyewakan pekerjaan ke luar karena itu memindahkan eksekusi ke luar sandbox
bahkan pada host yang memilikinya. Lihat
Sandbox dan keamanan
untuk apa yang membatasi sebuah jalan di setiap kasus.
Perekaman rapat terjadi di klien karena memerlukan mikrofon dan audio rapat — server di dalam lemari tidak dapat merekam panggilan Anda. Rekaman itu kemudian diringkas oleh pipeline server dan catatannya disimpan di tempat yang dapat dibaca setiap klien.
Klien web juga merekam, yang bisa terabaikan oleh bentuk tabel: klien ini menangkap mikrofon plus trek audio berbagi layar di peramban dan mengalirkan PCM 16 kHz ke server. Klien ini mewajibkan trek audio tersebut, jadi klien ini gagal secara eksplisit di tempat peramban tidak menyediakannya. Yang benar-benar khusus desktop adalah lapisan kualitas di sekitar perekaman — pembatalan gema tingkat sinyal melalui prosesor native, pengukur tingkat masukan dan peringatan mikrofon mati, menjalankan ulang ringkasan, membatalkan pemrosesan, dan tindakan rekam-dan-tautkan pada kalender.
Pairing dan kepercayaan
Bagian berjudul “Pairing dan kepercayaan”Setiap klien membuktikan identitasnya dengan kunci pre-shared yang disediakan sekali:
- Desktop dalam mode lokal membuat kunci baru setiap boot dan menyerahkannya ke server yang dijalankan melalui environment, sehingga tidak ada rahasia yang disimpan di desktop.
- Klien remote (desktop-remote, web, ponsel) memakai kunci yang dibuat oleh
perintah
pairserver dan disimpan di keychain OS (atau, di ponsel, secara lokal).
Pencabutan bersifat langsung, bukan tertunda. Server mengawasi tabel perangkat yang dipasangkan dan memutus sesi apa pun yang terbuka yang perangkatnya telah keluar dari kumpulan aktif, dalam hitungan detik — baik pada jalur WebSocket langsung maupun jalur relay. Anda tidak perlu menunggu perangkat menyambung ulang untuk memutusnya.
Kunci tidak pernah melewati URL yang dapat dilihat host statis. Saat kunci
datang melalui tautan — di ponsel atau di peramban — kunci itu berada di
fragmen URL, bagian setelah #, yang tidak pernah dikirim peramban ke
server yang menghosting halaman. Itulah invarian yang perlu diingat dan berlaku
untuk keduanya. Klien web memiliki dua titik masuk lain: formulir koneksi,
tempat Anda menempelkan server dan kunci secara langsung, serta kode undangan,
yang ditukarkannya dengan kredensial yang dibuat khusus untuk peramban itu saja.
Konsep terkait
Bagian berjudul “Konsep terkait”- Ruang kerja dan isolasi: mengapa setiap panggilan RPC membawa id ruang kerjanya sendiri dan bagaimana pemisahan basis data membuat isolasi bersifat struktural
- Arsitektur: bagaimana basis kode disusun di sekitar server dan klien-kliennya
- Kontrol jarak jauh dan seluler: model pemasangan ponsel dan keamanannya
Panduan terkait
Bagian berjudul “Panduan terkait”- Jalankan server headless: mulai
cc_serverdan pasangkan klien - Hubungkan ke server jarak jauh: arahkan klien desktop atau web ke server di tempat lain
- Pasangkan perangkat: tambahkan ponsel, peramban kedua, atau desktop lain
- Jalankan pekerja fleet: tambahkan
cc_workeryang menarik pekerjaan sewaan