Wdrażanie i klienci
Control Center to nie jeden program. To architektura cienkiego klienta: serwer headless, który zarządza Twoimi danymi, oraz zestaw klientów, które renderują interfejs przez jedno połączenie RPC z nim. Zrozumienie tego podziału wyjaśnia, dlaczego niektóre rzeczy działają tam, gdzie działają, i do czego każdy klient ma dostęp, a do czego nie.
Serwer zarządza danymi
Dział zatytułowany „Serwer zarządza danymi”Proces o nazwie cc_server przechowuje bazy danych — mały plik global.db
ze stanem całego serwera oraz po jednym pliku SQLite na każdy obszar roboczy,
otwieranym leniwie — i wykonuje długotrwałą pracę: silniki potoków,
rekonsyliatory, zestaw narzędzi MCP i nasłuch orkiestracji. Wszystko, co musi
przetrwać zamknięcie okna albo co dwa klienty powinny widzieć identycznie,
znajduje się właśnie tam.
Żaden klient sam nie otwiera pliku bazy danych. Zamiast tego każdy klient trzyma
jeden RemoteRpcClient i wykonuje przez niego operacje na repozytoriach oraz
subskrypcje. Serwer uwierzytelnia połączenie na podstawie wstępnie
współdzielonego klucza sparowanego urządzenia — a potem przestaje się
połączeniem przejmować. Sesja nie jest powiązana z obszarem roboczym.
Dyspozytor jest bezstanowy: każde wywołanie w zakresie obszaru roboczego niesie
własny workspace_id w argumentach, a wywołanie, które go pomija, zostaje
odrzucone.
To celowe rozwiązanie: dzięki niemu dwa klienty na jednym serwerze mogą działać w różnych obszarach roboczych, bez przechowywanego na serwerze „bieżącego obszaru roboczego”, o który mogłyby się spierać. Izolację egzekwuje się natomiast przy każdym wywołaniu, w czterech krokach:
- Operacja deklaruje, czy działa w zakresie obszaru roboczego; jeśli tak, a nie
dotarł żaden
workspace_id, wywołanie zostaje odrzucone. - Podany identyfikator musi należeć do zarejestrowanego obszaru roboczego;
sprawdza się to, zanim cokolwiek otworzy bazę danych — inaczej nieznany
identyfikator tworzyłby przy każdym żądaniu pusty, widmowy plik
workspace.db. Brak trafienia oznacza odpowiedź „nie znaleziono”. - Rola
WorkspaceRolewywołującego musi spełniać minimalny próg operacji, wynikający z jej rodzaju: odczyt wymaga roli guest, modyfikacja — member, operacja destrukcyjna — admin. - Operacja, która udostępnia zawartość repozytorium, wymaga dodatkowo
uprawnienia na poziomie repozytorium w zadeklarowanym zakresie —
none/read/review/write— dzięki czemu członkostwo w obszarze roboczym nigdy po cichu nie daje większych uprawnień niż forge.
Zdanie, które ma znaczenie: posiadanie klucza parowania nie jest granicą dostępu; jest nią członkostwo.
Dlaczego taki podział
Dział zatytułowany „Dlaczego taki podział”Ku architekturze z serwerem skłoniły trzy czynniki:
- Jedno źródło prawdy. Komputer i telefon albo laptop i komputer powinny widzieć ten sam stan floty. Gdyby każde miało własną bazę danych, rozjechałyby się. Jeden serwer sprawia, że każdy klient czyta te same, bieżące dane.
- Przeglądarka nie obsłuży się sama. Przeglądarka nie może uruchomić podprocesu ani otworzyć lokalnego pliku, więc wersja webowa nigdy nie może zarządzać bazą danych ani uruchamiać agenta. Musi rozmawiać z serwerem, który to potrafi. Zamiast budować drugą, wyłącznie webową ścieżkę dostępu do danych, aplikację desktopową wprowadzono na tę samą ścieżkę.
- Działanie bez interfejsu. Serwer, który można uruchomić na małym komputerze — dostępny z telefonu w pociągu albo z przeglądarki gdziekolwiek — jest użyteczniejszy niż GUI, które trzeba trzymać otwarte.
Kompromis jest taki, że serwer staje się zależnością: jeśli nie działa, nic nie działa. Domyślną odpowiedzią aplikacji desktopowej jest uruchomienie własnego serwera.
Klienci
Dział zatytułowany „Klienci”Do serwera sięga pięć klientów, każdy o innym kształcie i profilu zaufania.
Aplikacja desktopowa
Dział zatytułowany „Aplikacja desktopowa”Aplikacja desktopowa we Flutterze (macOS, Windows, Linux) to pełny klient. Przy pierwszym uruchomieniu pyta, jak ma działać, a potem zapamiętuje wybór:
- Lokalnie: uruchomienie i nadzorowanie
cc_serverna tym komputerze — domyślna, samowystarczalna konfiguracja dla jednego użytkownika. Aplikacja desktopowa uruchamia serwer, który przechowuje bazę danych w katalogu wsparcia aplikacji i komunikuje się z nią przez loopback. - Zdalnie: połączenie z
cc_serverdziałającym gdzie indziej przez bezpieczny WebSocket. Dane znajdują się na tym serwerze; ten komputer jest wyłącznie rendererem.
W obu przypadkach powstały klient RPC zastępuje dostawcę danych aplikacji, więc cały interfejs — każdy ekran, każda funkcja — czyta i zapisuje przez serwer. Aplikacja desktopowa nigdy nie dotyka bezpośrednio pliku bazy danych.
Klient webowy
Dział zatytułowany „Klient webowy”Wersja webowa to ta sama aplikacja we Flutterze skompilowana do działania
w przeglądarce, hostowana pod app.usectrl.dev, więc
nie ma nic do zainstalowania. Przeglądarka nie może uruchomić podprocesu,
dlatego klient webowy jest zawsze zdalny: łączy się z cc_server przez
wss:// i renderuje pełny interfejs desktopowy. Po połączeniu pokazuje te same
ekrany i tę samą flotę, bo to ten sam kod przez to samo RPC. Luki to garść
miejsc wymagających natywnych możliwości, których przeglądarka nie ma — główną
jest opisana niżej warstwa jakości nagrywania spotkań, a drugą powiadomienia
systemowe.
Dlatego stwierdzenia „bez pobierania” i „bez serwera” to dwie różne rzeczy.
Klient webowy usuwa instalację, ale nie usuwa serwera. To, z czym się łączy,
może być cc_server na tym samym laptopie — ws://localhost:9030, który nie
wymaga certyfikatu, bo przeglądarki uznają loopback za zaufany — albo na innym
komputerze, który musi być dostępny przez wss:// z certyfikatem, któremu
przeglądarka ufa. cc_server odmawia bindowania adresu innego niż loopback bez
TLS, więc zdalny serwer oznacza albo własny certyfikat, albo proxy kończące TLS,
albo tunel lub VPN, który go dostarcza.
Sam pakiet jest hostowany oddzielnie od serwera, z którym rozmawia. Samodzielny
cc_server obsługuje punkt końcowy RPC, zestaw MCP i swoje proxy, ale nie
pakiet webowy — ścieżka plików statycznych istnieje w serwerze, jednak
w samodzielnym binarium nie skonfigurowano katalogu głównego stron, więc nie ma
czego udostępniać pod /. Opublikowany obraz kontenera cc-webapp hostuje
pakiet, a scripts/build_web.sh go buduje. Serwer przyjmuje pochodzenia
przeglądarek tylko z listy --allowed-origins, która domyślnie zawiera
https://app.usectrl.dev, więc samodzielny hosting pakietu wymaga dodania jego
pochodzenia.
Multimedia (awatary, obrazy w newsfeedzie, zrzuty ekranu z PR-ów) przechodzą przez punkt końcowy proxy serwera, a nie trafiają bezpośrednio do hostów źródłowych, więc przeglądarka nigdy nie pobiera danych z dowolnego pochodzenia. Wdrożony klient dodaje do każdego żądania politykę Content-Security Policy pochodzącą z niesensywnego pliku cookie, dzięki czemu dozwolone jest wyłącznie pochodzenie połączonego serwera.
Towarzysz na telefonie (cc_remote)
Dział zatytułowany „Towarzysz na telefonie (cc_remote)”Aplikacja Remote to osobny, lżejszy klient — PWA we Flutterze dostępna pod remote.usectrl.dev. Celowo nie jest pełną aplikacją. Paruje się przez brokerski, szyfrowany end-to-end przekaźnik WebSocket i obsługuje głównie odczytowy wycinek zestawu narzędzi: czytanie zgłoszeń, wiadomości i newsfeedu; wysyłanie odpowiedzi lub aktualizowanie zgłoszenia.
Telefon to podmiot o niższych uprawnieniach niż lokalny agent. Domyślnie odrzucająca polityka narzędzi, wstępnie współdzielony klucz zabezpieczający każdą ramkę, uprawnienie sesji, które wyklucza uprzywilejowane operacje (na przykład parowanie) z aplikacji towarzyszącej, opisane wyżej kontrole roli i uprawnień do repozytoriów przy każdym wywołaniu oraz ograniczanie tempa sprawiają, że zatwierdzone (lub ujawnione) parowanie nie staje się pełnym zdalnym sterowaniem. Pełny model bezpieczeństwa opisano w sekcji Zdalne sterowanie i mobile.
Klienci MCP
Dział zatytułowany „Klienci MCP”Zewnętrzne narzędzia — edytory, inni agenci, narzędzia CLI — które mówią protokołem Model Context Protocol, łączą się z zestawem MCP serwera i korzystają z typowanego rejestru narzędzi. To programistyczna ścieżka integracji, a nie GUI. Zobacz Używaj serwera MCP i dokumentację referencyjną narzędzi MCP.
Workery floty (cc_worker)
Dział zatytułowany „Workery floty (cc_worker)”cc_worker to napisane w czystym Darcie binarium headless, które paruje się
z cc_server, deklaruje możliwości swojego hosta i pobiera wydzierżawione
zadania do wykonania. Strumieniuje zdarzenia procesów z powrotem tym samym
kanałem RPC i nie przechowuje żadnego trwałego stanu: żadnej bazy danych,
żadnych poświadczeń, zgód ani budżetów — te nigdy nie opuszczają cc_server.
Worker jest kończyną, nie drugim mózgiem. Jeden autorytatywny serwer, N głupich
kończyn; nie ma konsensusu ani ruchu między workerami.
Wydzierżawione wykonanie to nadal tylko mechanika dzierżawy. agentRun to
jedyny rodzaj, który uruchamia prawdziwe polecenie (CC_JOB_COMMAND albo
powtórzony prompt, jeśli żadnego nie podano). pipelineStep, codeIndex,
goldenRender, benchmark i evalBatch na razie sondują poleceniem
git --version / rev-parse HEAD, aby przetestować ścieżkę strumieniowania, nie
udając, że te zadania naprawdę się wykonały.
Ogłasza system operacyjny, architekturę procesora, liczbę rdzeni, pamięć RAM,
dostępność pakietu Flutter SDK oraz backendy sandboxa dostępne na hoście
(w Windows żaden). Dedykowany worker zawsze ogłasza też alwaysOn
i acceptsParallel, pod które planuje harmonogram przypnij → preferuj →
przelej. Nie ma sondy GPU ani ML.
Samotny komputer desktopowy nic z tego nie zauważa, bo zwykłe wysyłanie zadań
w ogóle nie przechodzi przez flotę. Istnieje wprawdzie niejawny worker działający
w tym samym procesie, zarejestrowany dla hosta serwera, ale to jedynie szew
rejestracyjny, do którego nie podłączono żadnych modułów wykonawczych — zadanie
faktycznie umieszczone na lokalnym workerze kończy się niepowodzeniem, zamiast
się wykonać. Dziś tylko prawdziwy cc_worker wykonuje wydzierżawione zadania.
Co działa gdzie
Dział zatytułowany „Co działa gdzie”| Zagadnienie | Serwer (cc_server) |
Worker floty (cc_worker) |
Klient desktopowy | Klient webowy / na telefonie |
|---|---|---|---|---|
| Bazy danych (Drift/SQLite) | zarządza nimi | — | — | — |
| Uwierzytelnianie, zgody, budżety, tożsamość | zarządza nimi | — | — | — |
| Potoki, rekonsyliatory, nasłuch orkiestracji | uruchamia je | — | — | — |
| Zestaw narzędzi MCP | obsługuje go | — | — | — |
| Wykonywanie agentów | uruchamia je (lub wydzierżawia) | wykonuje wydzierżawione zadania | — | — |
| Renderowanie interfejsu | — | — | pełne | pełne (web) / głównie odczyt (telefon) |
| Nagrywanie spotkań (mikrofon + dźwięk spotkania) | — | — | natywne nagrywanie | tylko web, przez dźwięk z udostępniania ekranu |
| OAuth kalendarza (Google) | przechowuje tokeny | — | — | — |
Oba poziomy nie stosują takiego samego sandboxa. cc_server zamyka
uruchomienia, które wykonuje samodzielnie, w sandboxie systemu operacyjnego hosta,
gdy backend jest dostępny — Seatbelt w macOS, bubblewrap w Linuksie i WSL2 —
a w Windows lub bez bwrap i socat sięga po pozostałe ograniczenia.
cc_worker w ogóle nie stosuje sandboxa. Ogłasza możliwość sandbox, aby
serwer mógł rozdzielać zadania według platformy, ale nic w workerze jej nie
egzekwuje: wydzierżawione zadanie jest uruchamiane bezpośrednio, ograniczone
środowiskiem dzierżawy i przydzielonym worktree. Wydzierżawianie pracy przenosi
więc wykonanie poza sandbox, nawet na hoście, który go ma. Co ogranicza
uruchomienie w każdym z tych przypadków, opisano w sekcji
Sandbox i bezpieczeństwo.
Nagrywanie spotkań odbywa się po stronie klienta, bo wymaga mikrofonu i dźwięku spotkania — serwer stojący w szafie nie nagra Twojej rozmowy. Nagranie jest następnie podsumowywane przez potok serwera, a notatki trafiają tam, skąd każdy klient może je odczytać.
Klient webowy też nagrywa, co układ tabeli może przesłaniać: przechwytuje w przeglądarce mikrofon oraz ścieżkę dźwięku z udostępniania ekranu i strumieniuje PCM 16 kHz do serwera. Ta ścieżka dźwięku jest wymagana, więc gdy przeglądarka jej nie udostępni, nagrywanie jawnie zawodzi. Wyłącznie desktopowa jest natomiast warstwa jakości wokół nagrywania — anulowanie echa na poziomie sygnału przez natywny procesor, miernik poziomu wejścia i ostrzeżenie o niedziałającym mikrofonie, ponowne generowanie podsumowania, anulowanie przetwarzania oraz akcja „Nagraj i podlinkuj” w kalendarzu.
Parowanie i zaufanie
Dział zatytułowany „Parowanie i zaufanie”Każdy klient potwierdza swoją tożsamość wstępnie współdzielonym kluczem, który nadaje się jednorazowo:
- Aplikacja desktopowa w trybie lokalnym generuje świeży klucz przy każdym starcie i przekazuje go uruchomionemu serwerowi przez zmienne środowiskowe, więc na komputerze nie zapisuje się żaden sekret.
- Klienci zdalni (desktopowy w trybie zdalnym, webowy, telefon) używają klucza
utworzonego przez polecenie
pairserwera i przechowywanego w pęku kluczy systemu operacyjnego (a na telefonie — lokalnie).
Unieważnianie działa na bieżąco, a nie leniwie. Serwer obserwuje tabelę sparowanych urządzeń i w ciągu kilku sekund zrywa każdą otwartą sesję, której urządzenie opuściło aktywny zbiór — zarówno na bezpośredniej ścieżce WebSocket, jak i przez przekaźnik. Nie trzeba czekać, aż urządzenie połączy się ponownie, żeby je odciąć.
Klucz nigdy nie podróżuje w adresie URL, który może zobaczyć statyczny host. Gdy
trafia przez link — na telefonie lub w przeglądarce — jedzie we fragmencie
URL, czyli części po #, której przeglądarka nigdy nie wysyła do serwera
hostującego stronę. To niezmiennik wart zapamiętania i dotyczy obu przypadków.
Klient webowy ma dwa inne punkty wejścia: formularz połączenia, w którym wkleja
się serwer i klucz bezpośrednio, oraz kod zaproszenia, wymieniany na
poświadczenie utworzone wyłącznie dla tej przeglądarki.
Powiązane koncepcje
Dział zatytułowany „Powiązane koncepcje”- Obszary robocze i izolacja: dlaczego każde wywołanie RPC niesie własny identyfikator obszaru roboczego i jak podział bazy danych czyni izolację strukturalną
- Architektura: jak kod jest zorganizowany wokół serwera i jego klientów
- Zdalne sterowanie i mobile: model parowania telefonu i bezpieczeństwa
Powiązane poradniki
Dział zatytułowany „Powiązane poradniki”- Uruchom serwer headless: uruchom
cc_serveri sparuj klienta - Połącz się ze zdalnym serwerem: skieruj klienta desktopowego lub webowego na serwer działający gdzie indziej
- Sparuj urządzenie: dodaj telefon, drugą przeglądarkę lub inny komputer
- Uruchom workera floty: dodaj
cc_worker, który pobiera wydzierżawione zadania