Přeskočit na obsah

Nasazení a klienti

Control Center není jeden program. Je to architektura tenkého klienta: server bez rozhraní, který vlastní tvoje data, a sada klientů, kteří přes jedno spojení RPC kreslí rozhraní. Tohle rozdělení vysvětluje, proč některé věci běží tam, kde běží, a na co který klient dosáhne a na co ne.

Proces jménem cc_server drží databáze — malý global.db pro stav celého serveru plus jeden soubor SQLite na každý pracovní prostor, otevřený až ve chvíli, kdy je potřeba — a spouští dlouho žijící práci: enginy pipeline, reconcilery, plochu nástrojů MCP a posluchače orchestrace. Všechno, co má přežít okno, nebo co mají dva klienti vidět stejně, žije tam.

Žádný klient neotevírá databázový soubor sám. Každý místo toho drží jednoho RemoteRpcClient a přes něj posílá operace nad repozitáři a odběry. Server spojení ověří proti předem sdílenému klíči spárovaného zařízení a pak už ho spojení nezajímá. Relace není vázaná na pracovní prostor. Dispečer je bezstavový: každé volání vázané na pracovní prostor nese ve svých argumentech vlastní workspace_id a volání, které ho vynechá, se odmítne.

Je to schválně. Díky tomu můžou dva klienti na jednom serveru sedět v různých pracovních prostorech a nemají serverem držený „aktuální pracovní prostor“, o kterém by se mohli rozejít. Izolace se místo toho vynucuje u každého volání, ve čtyřech krocích:

  1. Operace deklaruje, jestli je vázaná na pracovní prostor; když je a nedorazilo workspace_id, volání se odmítne.
  2. Uvedené id musí být registrovaný pracovní prostor a kontrola proběhne dřív, než cokoli otevře databázi. Jinak by neznámé id při každém požadavku vytvořilo prázdný přízrak workspace.db. Chybějící id je nenalezeno.
  3. WorkspaceRole volajícího musí splnit spodní hranici operace, odvozenou od jejího druhu: čtení vyžaduje roli Host, změna roli Člen, destruktivní operace roli Admin.
  4. Operace, která odhaluje obsah repozitáře, navíc vyžaduje oprávnění k danému repozitáři na úrovni, kterou deklaruje — none / read / review / write — aby členství v pracovním prostoru nikdy tiše nepřekonalo oprávnění forge.

Věta, na které záleží: držet párovací klíč není hranice přístupu; tou je členství.

K serveru vedly tři tlaky:

  1. Jeden zdroj pravdy. Desktop a telefon, nebo notebook a desktop, mají vidět stejný stav flotily. Kdyby každý vlastnil vlastní databázi, rozešly by se. Jeden server znamená, že každý klient čte tatáž živá data.
  2. Web se neobslouží sám. Prohlížeč neumí spustit podproces ani otevřít místní soubor, takže webové sestavení nikdy nemůže vlastnit databázi ani spustit agenta. Musí mluvit se serverem, který to umí. Místo druhé, čistě webové datové cesty se na stejnou cestu dostal i desktop.
  3. Provoz bez rozhraní. Server, který spustíš na malém stroji a dosáhneš na něj z telefonu ve vlaku nebo z prohlížeče kdekoli, je užitečnější než grafické rozhraní, které musíš nechat otevřené.

Daní za to je, že server je teď závislost: když neběží, nefunguje nic. Výchozí odpověď desktopu je spustit ho sám.

Na server dosáhne pět klientů, každý s jiným tvarem a profilem důvěry.

Desktopová aplikace ve Flutteru (macOS, Windows, Linux) je plný klient. Při prvním spuštění se zeptá, jak má běžet, a volbu si pak pamatuje:

  • Místní: spustí a hlídá cc_server na tomhle stroji. Výchozí, samostatné nastavení pro jednoho uživatele. Desktop server spustí, ten vlastní databázi pod kořenem podpory aplikace a mluví s ním přes loopback.
  • Vzdálené: připojí se k cc_server, který běží jinde, přes zabezpečený WebSocket. Data žijí na tom serveru; tenhle desktop jen vykresluje.

V obou případech výsledný klient RPC přepíše poskytovatele dat aplikace, takže celé rozhraní — každá obrazovka, každá funkce — čte a zapisuje přes server. Desktop se databázového souboru přímo nikdy nedotkne.

Webové sestavení je ta samá aplikace ve Flutteru, přeložená pro prohlížeč a hostovaná na app.usectrl.dev, takže není co instalovat. Prohlížeč neumí spustit podproces, takže webový klient je vždy vzdálený: naváže cc_server přes wss:// a vykreslí plné desktopové rozhraní. Po připojení ukazuje tytéž obrazovky nad toutéž flotilou, protože je to tentýž kód nad týmž RPC. Mezery jsou ta hrstka míst, která potřebují nativní schopnost, kterou prohlížeč nemá. Hlavní je vrstva kvality záznamu schůzky níže, další jsou oznámení operačního systému.

Proto jsou „bez stahování“ a „bez serveru“ různá tvrzení. Webový klient odstraňuje instalaci, server ne. Kam se připojuje, může být cc_server na tom samém notebooku — ws://localhost:9030, který nepotřebuje certifikát, protože prohlížeče berou loopback jako důvěryhodný — nebo server na jiném stroji, který musí být dostupný přes wss:// s certifikátem, jemuž prohlížeč důvěřuje. cc_server odmítne naslouchat na adrese mimo loopback bez TLS, takže vzdálený server znamená vlastní certifikát, proxy, která TLS ukončuje, nebo tunel či VPN, která ho dodá.

Samotný balík je hostovaný odděleně od serveru, se kterým mluví. Samostatný cc_server obsluhuje koncový bod RPC, plochu MCP a své proxy, ale ne webový balík. Cesta ke statickým souborům na serveru existuje, ale na samostatném binárním souboru není nastavený webový kořen, takže na / nemá co servírovat. Publikovaný obraz kontejneru cc-webapp balík hostuje a scripts/build_web.sh ho sestavuje. Server přijímá originy prohlížeče jen ze seznamu --allowed-origins, který má výchozí hodnotu https://app.usectrl.dev, takže vlastní hostování balíku znamená přidat jeho origin.

Média (avatary, obrázky kanálu, snímky PR) jdou přes proxy koncový bod serveru, ne přímo na upstream hostitele, takže prohlížeč nikdy nenačítá libovolný origin. Nasazený klient nastaví pro každý požadavek Content-Security Policy z necitlivé cookie, takže je povolený jen origin připojeného serveru.

Telefonní společník (cc_remote)

Sekce “Telefonní společník (cc_remote)”

Aplikace Remote je samostatný, lehčí klient — webová PWA ve Flutteru na remote.usectrl.dev. Schválně to není plná aplikace. Páruje se přes zprostředkované WebSocket relé zapečetěné od konce ke konci a mluví převážně čtecím výřezem plochy nástrojů: čte tickety, zprávy a kanál novinek; pošle odpověď nebo aktualizuje ticket.

Telefon je subjekt s nižšími oprávněními než místní agent. Politika nástrojů s výchozím zákazem, předem sdílený klíč, který zapečeťuje každý rámec, schopnost relace, která zamkne privilegované operace (třeba párování) mimo doprovodného klienta, kontroly role a oprávnění k repozitáři u každého volání výše a omezování četnosti brání tomu, aby schválené (nebo uniklé) párování znamenalo plné vzdálené ovládání. Viz Vzdálené ovládání a mobil pro celý model zabezpečení.

Externí nástroje — editory, jiní agenti, CLI — které mluví Model Context Protocol, se připojí k ploše MCP serveru a berou typovaný registr nástrojů. Tohle je programová integrační cesta; není to grafické rozhraní. Viz Používejte MCP server a Nástroje MCP.

Workeři flotily (cc_worker)

Sekce “Workeři flotily (cc_worker)”

cc_worker je binární soubor v čistém Dartu bez rozhraní, který se spáruje s cc_server, ohlásí schopnosti hostitele a stahuje zapůjčené úlohy k provedení. Události procesu streamuje zpátky po tom samém RPC a nedrží trvalý stav: žádnou databázi, žádnou autentizaci, žádná schválení, žádné rozpočty. To všechno zůstává v cc_server. Worker je končetina, ne druhý mozek. Jeden autoritativní server, N hloupých končetin; žádný konsenzus, žádný provoz mezi workery.

Zapůjčené provádění je pořád jen rozvod zapůjčení. agentRun je jediný druh, který spustí skutečný příkaz (CC_JOB_COMMAND, nebo ozvěnu promptu, když žádný nebyl dodaný). pipelineStep, codeIndex, goldenRender, benchmark a evalBatch teď sondí přes git --version / rev-parse HEAD, aby se procvičila streamovací cesta a nepředstíralo se, že ty úlohy opravdu běžely.

Co ohlašuje, je operační systém, architektura CPU, počet jader, RAM, jestli je dosažitelný Flutter SDK a které backendy sandboxu hostitel má (na Windows žádný). Vyhrazený worker navíc vždy ohlašuje alwaysOn a acceptsParallel, a právě podle toho plánovač připnout → upřednostnit → přelít umisťuje úlohy. Sonda GPU ani ML tu není.

Samotný desktop si toho nevšimne, protože běžné odeslání flotilou vůbec neprochází. Implicitní worker v procesu pro hostitele serveru existuje, ale je to jen registrační spoj bez napojených vykonavatelů. Úloha, která se na místního workera opravdu umístí, selže, místo aby se provedla. Dnes zapůjčené úlohy provádí jen skutečný cc_worker.

Oblast Server (cc_server) Worker flotily (cc_worker) Desktopový klient Webový / telefonní klient
Databáze (Drift/SQLite) vlastní je — — —
Autentizace, schválení, rozpočty, identita vlastní je — — —
Pipeline, reconcilery, posluchač orchestrace spouští je — — —
Plocha nástrojů MCP obsluhuje ji — — —
Provádění agentů spouští ho (nebo ho zapůjčí) provádí zapůjčené úlohy — —
Vykreslování rozhraní — — plné plné (web) / převážně čtení (telefon)
Záznam schůzky (mikrofon + zvuk schůzky) — — nativní záznam jen web, přes zvuk sdílení obrazovky
OAuth kalendáře (Google) ukládá tokeny — — —

Obě vrstvy sandbox nepoužívají stejně. cc_server obalí běhy, které provádí sám, sandboxem operačního systému hostitele, kde backend existuje — Seatbelt na macOS, bubblewrap na Linuxu a WSL2 — a na Windows nebo bez bwrap a socat spadne na ostatní hranice. cc_worker sandbox nepoužívá vůbec. Ohlašuje schopnost sandbox, aby server uměl umisťovat úlohy podle platformy, ale ve workeru se žádný neaplikuje: zapůjčená úloha se spustí přímo, ohraničená prostředím zapůjčení a worktree, který dostala. Zapůjčit práci ven tedy přesune provádění mimo sandbox i na hostiteli, který ho má. Viz Sandbox a zabezpečení pro to, co běh v každém případě ohraničuje.

Záznam schůzky probíhá na klientovi, protože potřebuje mikrofon a zvuk schůzky. Server ve skříni ti hovor nenahraje. Nahrávku pak shrne pipeline serveru a poznámky se uloží tam, kde je přečte každý klient.

Nahrává i webový klient, což tvar tabulky může zakrýt: v prohlížeči bere mikrofon plus zvukovou stopu sdílení obrazovky a streamuje 16 kHz PCM na server. Tu zvukovou stopu vyžaduje, takže tam, kde ji prohlížeč nedá, selže explicitně. Co je opravdu jen na desktopu, je vrstva kvality kolem záznamu: potlačení ozvěny na úrovni signálu nativním procesorem, měřič úrovně vstupu a upozornění na mrtvý mikrofon, opětovné spuštění souhrnu, zrušení zpracování a akce kalendáře nahrát a propojit.

Každý klient prokáže, kdo je, předem sdíleným klíčem zřízeným jednou:

  • Desktop v místním režimu vygeneruje při každém startu čerstvý klíč a předá ho spuštěnému serveru přes prostředí, takže na desktopu nezůstane nic tajného.
  • Vzdálení klienti (desktop na dálku, web, telefon) použijí klíč, který vydá příkaz pair na serveru, a uloží ho do systémové klíčenky (na telefonu místně).

Odvolání je okamžité, ne odložené. Server sleduje tabulku spárovaných zařízení a do několika sekund shodí každou otevřenou relaci, jejíž zařízení opustilo aktivní množinu, na přímé cestě WebSocket i na cestě relé. Nemusíš čekat, až se zařízení znovu připojí, abys ho odřízl.

Klíč nikdy neputuje URL, kterou statický hostitel uvidí. Když přijde odkazem, na telefonu nebo v prohlížeči, veze se ve fragmentu URL, v části za #, kterou prohlížeč nikdy nepošle serveru hostujícímu stránku. To je invariant, který stojí za zapamatování, a platí pro obojí. Webový klient má dva další vstupy: formulář připojení, kam vložíš server a klíč přímo, a kód pozvánky, který vymění za pověření vydané jen pro ten prohlížeč.