İçeriğe geç

Dağıtım ve istemciler

Control Center tek bir program değildir. Bir ince istemci mimarisidir: verilerinize sahip olan headless bir sunucu ve ona tek bir RPC bağlantısı üzerinden arayüz çizen bir dizi istemci. Bu ayrımı anlamak, bazı şeylerin neden orada çalıştığını ve her istemcinin neye erişip neye erişemediğini açıklar.

cc_server adlı bir süreç veritabanlarını tutar — sunucu geneli durum için küçük bir global.db ve her çalışma alanı için tembel açılan birer SQLite dosyası — ve uzun ömürlü işleri yürütür: pipeline motorları, uzlaştırıcılar, MCP araç yüzeyi ve orkestrasyon dinleyicisi. Bir pencereyi aşması gereken ya da iki istemcinin birebir aynı görmesi gereken her şey orada yaşar.

Hiçbir istemci veritabanı dosyasını kendisi açmaz. Bunun yerine her istemci tek bir RemoteRpcClient tutar ve depo işlemlerini ve abonelikleri onun üzerinden yürütür. Sunucu bağlantının kimliğini eşleştirilmiş cihaz ön paylaşımlı anahtarına karşı doğrular — ve sonra bağlantıyı umursamayı bırakır. Bir oturum bir çalışma alanına bağlı değildir. Dağıtıcı durumsuzdur: çalışma alanı kapsamlı her çağrı kendi workspace_id değerini bağımsız değişkenlerinde taşır ve bunu atlayan bir çağrı reddedilir.

Bu bilinçli bir tercihtir ve bir sunucudaki iki istemcinin, anlaşmazlığa düşebilecekleri, sunucunun tuttuğu bir “geçerli çalışma alanı” olmadan farklı çalışma alanlarında oturabilmesini sağlayan da budur. Yalıtım bunun yerine her çağrıda, dört adımda uygulanır:

  1. İşlem, çalışma alanı kapsamlı olup olmadığını bildirir; öyleyse ve workspace_id gelmediyse çağrı reddedilir.
  2. Adı verilen kimlik kayıtlı bir çalışma alanı olmalıdır ve bu, herhangi bir şey veritabanı açmadan önce denetlenir — aksi halde bilinmeyen bir kimlik her istekte boş bir hayalet workspace.db yaratırdı. Bir ıska, bulunamadı demektir.
  3. Çağıranın WorkspaceRole değeri işlemin tabanını karşılamalıdır; bu taban işlemin türünden türetilir: okuma misafir ister, değiştirme üye ister, yıkıcı işlem yönetici ister.
  4. Depo içeriğini açığa çıkaran bir işlem ayrıca, bildirdiği düzeyde depo başına bir yetki gerektirir — none / read / review / write — böylece çalışma alanı üyeliği forge’un ayrıcalıklarını sessizce aşmaz.

Önemli olan cümle şu: erişim sınırı eşleme anahtarına sahip olmak değildir; üyeliktir.

Sunucuya doğru üç baskı itti:

  1. Tek doğruluk kaynağı. Bir masaüstü ile bir telefon ya da bir dizüstü ile bir masaüstü aynı filo durumunu görmelidir. Her biri kendi veritabanına sahip olsaydı birbirlerinden ayrışırlardı. Tek bir sunucu, her istemcinin aynı canlı veriyi okuması demektir.
  2. Web kendi kendine yetemez. Bir tarayıcı alt süreç başlatamaz ya da yerel bir dosya açamaz, bu yüzden bir web yapısı veritabanına asla sahip olamaz ya da bir ajan çalıştıramaz. Veritabanına sahip olan bir sunucuyla konuşmak zorundadır. İkinci, yalnızca web’e özel bir veri yolu kurmak yerine masaüstü aynı yola taşındı.
  3. Headless çalıştırma. Küçük bir kutuda çalıştırabileceğiniz — trende telefondan ya da herhangi bir yerden bir tarayıcıdan erişilebilen — bir sunucu, açık bırakmanız gereken bir GUI’den daha kullanışlıdır.

Ödünleşim, sunucunun artık bir bağımlılık olmasıdır: çalışmıyorsa hiçbir şey çalışmaz. Masaüstünün buna verdiği varsayılan yanıt kendisinin bir tane başlatmasıdır.

Sunucuya beş istemci erişir; her birinin biçimi ve güven profili farklıdır.

Flutter masaüstü uygulaması (macOS, Windows, Linux) tam istemcidir. İlk açılışta nasıl çalışacağını sorar ve sonra seçimi hatırlar:

  • Yerel: bu makinede bir cc_server’ı başlatın ve denetleyin — varsayılan, kendi kendine yeten, tek kullanıcılı kurulum. Masaüstü sunucuyu başlatır; sunucu veritabanına uygulama destek kökü altında sahip olur ve onunla loopback üzerinden konuşur.
  • Uzak: güvenli bir WebSocket üzerinden başka bir yerde çalışan bir cc_server’a bağlanın. Veriler o sunucuda yaşar; bu masaüstü yalnızca bir görüntüleyicidir.

Her iki durumda da elde edilen RPC istemcisi uygulamanın veri sağlayıcısını geçersiz kılar; böylece tüm arayüz — her ekran, her özellik — okumayı ve yazmayı sunucu üzerinden yapar. Masaüstü veritabanı dosyasına asla doğrudan dokunmaz.

Web yapısı, tarayıcıda çalışacak biçimde derlenmiş aynı Flutter uygulamasıdır ve app.usectrl.dev adresinde barındırılır, yani kurulacak bir şey yoktur. Bir tarayıcı alt süreç başlatamaz, bu yüzden web istemcisi her zaman uzaktır: bir cc_server’a wss:// üzerinden bağlanır ve tam masaüstü arayüzünü çizer. Bağlandıktan sonra aynı filoya karşı aynı ekranları gösterir, çünkü aynı RPC üzerinden çalışan aynı koddur. Eksik kalanlar, tarayıcının sahip olmadığı yerel bir yetenek gerektiren birkaç yerdir — bunların başında aşağıdaki toplantı kaydı kalite katmanı gelir, işletim sistemi bildirimleri de bir diğeridir.

“İndirme yok” ile “sunucu yok”un neden farklı iddialar olduğunu da bu açıklar. Web istemcisi kurulumu ortadan kaldırır; sunucuyu ortadan kaldırmaz. Bağlandığı şey aynı dizüstündeki bir cc_server olabilir — ws://localhost:9030, sertifika gerektirmez çünkü tarayıcılar loopback’i güvenilir sayar — ya da başka bir makinedeki bir cc_server olabilir; buna tarayıcının güvendiği bir sertifikayla wss:// üzerinden erişilebilmelidir. cc_server, TLS olmadan loopback dışı bir adrese bağlanmayı reddeder; yani uzak bir sunucu ya kendi sertifikası, ya TLS sonlandıran bir proxy, ya da bunu sağlayan bir tünel veya VPN demektir.

Paketin kendisi, konuştuğu sunucudan ayrı olarak barındırılır. Tek başına çalışan bir cc_server, RPC uç noktasını, MCP yüzeyini ve proxy’lerini sunar ama bir web paketi sunmaz — statik dosya yolu sunucuda vardır ancak tek başına çalışan ikili üzerinde hiçbir web kökü yapılandırılmamıştır, dolayısıyla / altında sunacak bir şeyi yoktur. Yayımlanan cc-webapp kapsayıcı imajı paketi barındıran şeydir, onu derleyen de scripts/build_web.sh’dir. Bir sunucu tarayıcı kaynaklarını yalnızca --allowed-origins listesinde kabul eder; bu listenin varsayılanı https://app.usectrl.dev olduğundan, paketi kendi sunucunuzda barındırmak kaynağını eklemek anlamına gelir.

Medya (avatarlar, akış görselleri, PR ekran görüntüleri) doğrudan üst kaynak sunuculara gitmek yerine sunucunun proxy uç noktası üzerinden yönlendirilir, böylece bir tarayıcı hiçbir zaman rastgele bir kaynaktan içerik çekmez. Dağıtılan istemci, hassas olmayan bir çerezden istek başına bir İçerik Güvenliği Politikası damgalar; böylece yalnızca bağlı sunucunun kaynağına izin verilir.

Remote uygulaması ayrı, daha hafif bir istemcidir — remote.usectrl.dev adresindeki bir Flutter web PWA. Kasıtlı olarak tam uygulama değildir. Aracılı, uçtan uca mühürlenmiş bir WebSocket aktarıcısı üzerinden eşleşir ve araç yüzeyinin ağırlıklı olarak okuma amaçlı bir dilimini konuşur: biletleri, mesajları ve haber akışını okuyun; bir yanıt gönderin ya da bir bileti güncelleyin.

Telefon, yerel bir ajandan daha düşük ayrıcalıklı bir varlıktır. Varsayılan olarak reddeden bir araç politikası, her çerçeveyi mühürleyen bir ön paylaşımlı anahtar, ayrıcalıklı işlemleri (örneğin eşleştirmeyi) bir yardımcı istemcinin dışında tutan bir oturum yeteneği, yukarıdaki çağrı başına rol ve depo yetkisi denetimleri ve hız sınırlama, onaylanmış (ya da sızdırılmış) bir eşleştirmenin tam uzaktan kontrole dönüşmesini engeller. Tam güvenlik modeli için Uzaktan kontrol ve mobil sayfasına bakın.

Model Context Protocol’ü konuşan harici araçlar — düzenleyiciler, diğer ajanlar, CLI’lar — sunucunun MCP yüzeyine bağlanır ve tiplenmiş araç kaydını tüketir. Bu, programatik entegrasyon yoludur; bir GUI değildir. MCP sunucusunu kullanma ve MCP araçları referansı sayfalarına bakın.

Bir cc_worker, bir cc_server ile eşleşen, ana makine yeteneklerini bildiren ve yürütmek üzere kiralanmış işleri çeken headless, saf Dart ikilisidir. Süreç olaylarını aynı RPC üzerinden geri akıtır ve kalıcı durum tutmaz: veritabanı yok, kimlik doğrulama yok, onay yok, bütçe yok — bunlar cc_server’dan asla ayrılmaz. Bir worker bir uzuvdur, ikinci bir beyin değil. Tek yetkili sunucu, N aptal uzuv; uzlaşma yok, worker’dan worker’a trafik yok.

Kiralanmış yürütme hâlâ yalnızca kiralama altyapısıdır. Gerçek bir komutu çalıştıran tek tür agentRun’dır (CC_JOB_COMMAND ya da hiç verilmediyse yankılanan bir istem). pipelineStep, codeIndex, goldenRender, benchmark ve evalBatch ise şu anda git --version / rev-parse HEAD ile yoklama yapar; böylece akış yolu, o işler çalışmış gibi yapılmadan denenmiş olur.

Bildirdiği şeyler işletim sistemi, CPU mimarisi, çekirdek sayısı, RAM, bir Flutter SDK’ya ulaşılıp ulaşılamadığı ve ana makinenin hangi sandbox arka uçlarına sahip olduğudur (Windows’ta hiçbiri). Özel bir worker ayrıca her zaman alwaysOn ve acceptsParallel bildirir; sabitle → tercih et → taşır zamanlayıcısının yerleştirme yaparken kullandığı da bunlardır. GPU ya da ML yoklaması yoktur.

Tek başına çalışan bir masaüstü bunların hiçbirini fark etmez, çünkü olağan gönderim filodan hiç geçmez. Sunucu ana makinesi için kayıtlı örtük bir süreç içi worker vardır, ama bu, içine hiçbir çalıştırıcının bağlanmadığı bir kayıt dikişinden ibarettir — yerel worker’a gerçekten yerleştirilen bir iş, çalışmak yerine başarısız olur. Bugün kiralanmış işleri yalnızca gerçek bir cc_worker yürütür.

Konu Sunucu (cc_server) Filo yürütücüsü (cc_worker) Masaüstü istemcisi Web / telefon istemcisi
Veritabanları (Drift/SQLite) bunlara sahiptir — — —
Kimlik doğrulama, onaylar, bütçeler, kimlik bunlara sahiptir — — —
Pipeline’lar, uzlaştırıcılar, orkestrasyon dinleyicisi bunları yürütür — — —
MCP araç yüzeyi bunu sunar — — —
Ajan yürütme bunu yürütür (ya da kiralar) kiralanmış işleri yürütür — —
Arayüz çizimi — — tam tam (web) / ağırlıklı olarak okuma (telefon)
Toplantı kaydı (mikrofon + toplantı sesi) — — yerel kayıt yalnızca web, ekran paylaşımı sesi üzerinden
Takvim OAuth (Google) belirteçleri saklar — — —

İki katman aynı biçimde sandbox uygulamaz. cc_server, kendi yürüttüğü çalıştırmaları, bir arka ucun bulunduğu yerlerde ana makinenin işletim sistemi sandbox’ına sarar — macOS’ta Seatbelt, Linux ve WSL2’de bubblewrap — ve Windows’ta ya da bwrap ile socat yokken diğer sınırlara geri düşer. cc_worker hiç sandbox uygulamaz. Sunucunun işleri platforma göre yerleştirebilmesi için bir sandbox yeteneği bildirir, ama worker’da bunu uygulayan hiçbir şey yoktur: kiralanmış bir iş doğrudan başlatılır ve sınırlarını kiralamanın ortamı ile kendisine verilen çalışma ağacı çizer. Dolayısıyla işi dışarıya kiralamak, sandbox’ı olan bir ana makinede bile yürütmeyi sandbox’ın dışına taşır. Her durumda bir çalıştırmayı neyin sınırladığı için Sandbox ve güvenlik sayfasına bakın.

Toplantı kaydı bir istemcide gerçekleşir çünkü bir mikrofona ve toplantının sesine ihtiyaç duyar — bir dolaptaki sunucu aramanızı kaydedemez. Kayıt daha sonra sunucunun pipeline’ı tarafından özetlenir ve notlar her istemcinin okuyabileceği bir yerde saklanır.

Web istemcisi de kaydeder; tablonun biçimi bunu gizleyebilir: tarayıcıda mikrofona ek olarak bir ekran paylaşımı ses kanalı yakalar ve sunucuya 16 kHz PCM akıtır. Bu ses kanalını gerektirir, dolayısıyla tarayıcının kanalı sağlamadığı yerlerde açıkça başarısız olur. Gerçekten yalnızca masaüstüne özel olan şey, kaydın etrafındaki kalite katmanıdır — yerel işlemci üzerinden sinyal düzeyinde yankı engelleme, giriş seviyesi göstergesi ve ölü mikrofon uyarısı, bir özeti yeniden çalıştırma, işlemeyi iptal etme ve takvimin kaydı başlat ve bağla eylemi.

Her istemci kimliğini, bir kez sağlanan bir ön paylaşımlı anahtarla kanıtlar:

  • Yerel moddaki masaüstü her açılışta yeni bir anahtar üretir ve bunu başlattığı sunucuya ortam üzerinden verir; böylece masaüstünde hiçbir gizli bilgi kalıcı olmaz.
  • Uzak istemciler (uzak masaüstü, web, telefon) sunucunun pair komutuyla üretilen ve işletim sistemi anahtarlığında (telefonda ise yerel olarak) saklanan bir anahtar kullanır.

İptal anında gerçekleşir, tembel değildir. Sunucu eşleştirilmiş cihaz tablosunu izler ve etkin kümeden çıkmış bir cihazın açık oturumunu saniyeler içinde düşürür — doğrudan WebSocket yolunda da aktarma yolunda da. Bir cihazın bağlantısını kesmek için yeniden bağlanmasını beklemeniz gerekmez.

Anahtar, statik bir ana makinenin görebileceği bir URL üzerinden asla yol almaz. Bir bağlantıyla geldiğinde — telefonda ya da bir tarayıcıda — URL parçası içinde, yani # sonrasındaki bölümde taşınır; tarayıcı bunu sayfayı barındıran sunucuya asla göndermez. Hatırlamaya değer değişmez budur ve her ikisi için de geçerlidir. Web istemcisinin iki giriş noktası daha vardır: bir sunucu ile anahtarı doğrudan yapıştırdığınız bağlantı formu ve yalnızca o tarayıcı için üretilmiş bir kimlik bilgisi karşılığında kullandığı bir davet kodu.

  • Çalışma alanları ve yalıtım: her RPC çağrısının neden kendi çalışma alanı kimliğini taşıdığı ve veritabanı bölünmesinin yalıtımı nasıl yapısal kıldığı
  • Mimari: kod tabanının sunucu ve istemcileri etrafında nasıl yapılandırıldığı
  • Uzaktan kontrol ve mobil: telefon eşleştirme ve güvenlik modeli