Przejdź do głównej zawartości

Uruchom serwer headless

Uruchom cc_server na maszynie, która jest stale włączona, a następnie połącz się z nim ze swojego pulpitu, przeglądarki lub telefonu. Serwer to natywna binarka napisana w czystym Dart — bez silnika Flutter — więc działa headless w systemach macOS, Linux i Windows. Jest właścicielem bazy danych, uruchamia agentów i wykonuje wszystkie wywołania zewnętrznych API; klienty tylko renderują interfejs.

Każde wydanie publikuje hostowany samodzielnie stos czterech obrazów GHCR — backend, dwa statyczne cienkie klienty i opcjonalny przekaźnik parowania — a także osobny obraz cc-server-demo dla publicznego hosta demonstracyjnego. Instalacja hostowana samodzielnie nie wymaga pobierania źródeł. Przypnij tag wersji (ghcr.io/samuelalev/<image>:v0.0.1-rc.1) albo korzystaj z latest.

Obraz Co to jest Publikuje
ghcr.io/samuelalev/cc-server Backend: baza danych, agenci, MCP, webhooki -p 9030:9030
ghcr.io/samuelalev/cc-webapp Klient webowy (statyczny nginx) -p 8080:8080
ghcr.io/samuelalev/cc-remote Towarzysząca aplikacja PWA na telefon (statyczny nginx) -p 8081:8080
ghcr.io/samuelalev/cc-signaling-server Przekaźnik parowania WebRTC (opcjonalny) -p 8788:8788

Cały stos jako plik compose:

services:
server:
image: ghcr.io/samuelalev/cc-server:latest
ports: ["9030:9030"]
volumes: ["cc_data:/data"]
restart: unless-stopped
web:
image: ghcr.io/samuelalev/cc-webapp:latest
ports: ["8080:8080"]
restart: unless-stopped
remote:
image: ghcr.io/samuelalev/cc-remote:latest
ports: ["8081:8080"]
restart: unless-stopped
signaling:
image: ghcr.io/samuelalev/cc-signaling-server:latest
ports: ["8788:8788"]
restart: unless-stopped
volumes:
cc_data:

O czym warto wiedzieć, zanim wystawisz to wszystko poza loopback:

  • Obraz cc-server zawiera CC_SERVER_INSECURE=1 (musi nasłuchiwać na 0.0.0.0, aby odbierać ruch z opublikowanych portów), co jest bezpieczne tylko za terminującym TLS odwrotnym proxy — Caddy, Traefik, nginx lub Cloudflare Tunnel — w zaufanej sieci. Aby zamiast tego terminować TLS w samym procesie, zamontuj certyfikat i ustaw CC_SERVER_TLS_CERT / CC_SERVER_TLS_KEY (a następnie usuń CC_SERVER_INSECURE). Wszystko można nadpisać za pomocą docker run -e … lub bloku environment: w compose.

  • /data to cały stan. Bazy SQLite, sekrety sparowanych urządzeń, pobrane modele i dźwięk ze spotkań — wszystko to mieszka w wolumenie; zrób jego kopię zapasową, a kontener stanie się jednorazowy.

  • Sparuj klienta z wnętrza obrazu przed pierwszym połączeniem — to kontenerowy odpowiednik kroku 3:

    Okno terminala
    docker run --rm -it -v cc_data:/data \
    --entrypoint /app/bin/cc_server ghcr.io/samuelalev/cc-server:latest pair
  • Oba obrazy klientów to statyczne hosty nginx — niczego nie trzeba konfigurować w czasie działania; formularz połączenia każdego klienta przyjmuje adres URL serwera, identyfikator urządzenia i klucz parowania. Przekaźnik sygnalizacyjny również potrzebuje wyłącznie swojego portu (SIGNALING_HOST / SIGNALING_PORT można nadpisać); jest bezstanowy, nie przechowuje żadnych sekretów i stanowi mechanizm awaryjny — telefon korzysta z loopbacku, sieci LAN, tailnetu albo bezpośredniej ścieżki wss://, gdy któraś z nich jest osiągalna, a sięga po przekaźnik tylko wtedy, gdy żadna nie jest. Pomiń go, jeśli Twoi klienci zawsze łączą się z serwerem bezpośrednio.

Dalsza część tego poradnika buduje serwer ze źródeł; omawiane tu flagi i zachowanie to te same, które zasilają zmienne środowiskowe obrazu.

  • Dart SDK. To repozytorium przypina swoje SDK za pomocą fvm, więc każdą komendę poprzedź prefiksem fvm.
  • Sklonowane to repozytorium.
  • Natywny łańcuch narzędzi wymagany przez skrypt przygotowujący — Rust/cargo oraz meson, ninja, pkg-config, git i kompilator C++. Każdy skrypt budowania wskazuje, czego nie potrafi znaleźć i jak to zainstalować.
  • Osiągalny host: loopback do użytku lokalnego albo adres LAN/publiczny z TLS dla klientów zdalnych.

Biblioteki natywne są wymagane — nie istnieje tryb zdegradowany. Zbuduj je z katalogu głównego repozytorium, zanim zrobisz cokolwiek innego:

Okno terminala
scripts/natives/build_natives.sh

To buduje rift, fff, tree-sitter wraz z jego gramatykami, aec, lame, ccpty, cc_watcher, cc_inference oraz bibliotekę natywną SAML do build/natives/. Hak budowania cc_server ponownie emituje do pakietu wszystko, co zostało tam przygotowane, więc pominięcie tego kroku albo od razu powoduje błąd budowania, albo — dzięki furtce awaryjnej opisanej poniżej — daje binarkę, której kontrola wstępna podczas rozruchu odmawia uruchomienia i wskazuje brakującą bibliotekę.

Okno terminala
cd apps/cc_server
fvm dart build cli

Pakiet zawiera libsqlite3 i przygotowane biblioteki natywne obok binarki — nie potrzeba systemowego SQLite ani silnika Flutter. Plik wykonywalny trafia do ./build/cli/<os_arch>/bundle/bin/cc_server.

Świeży katalog danych nie ma sparowanego urządzenia ani współdzielonego klucza, więc monitu klienta o klucz parowania nie da się jeszcze zaspokoić. Wygeneruj taki klucz podkomendą pair. Zapisuje ona bezpośrednio pliki global.db i secrets.json; serwer, który już obsługuje ten katalog danych, wykryje nowe urządzenie bez restartu:

Okno terminala
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030

Wyświetla trzy wartości do wklejenia w formularz połączenia klienta:

Connect a thin client (paste into its connect form):
Server ws://localhost:9030/rpc
Device id web-client
Pairing key <a freshly generated key>

Przydatne flagi: --device (domyślnie web-client; identyfikator wybiera też klasę urządzenia — desktop, ios, android, cokolwiek innego to klient webowy), --label (domyślnie nazwa platformy plus nazwa hosta tej maszyny), --host (host wypisywany w adresie URL, ponieważ loopback nie jest osiągalny z innej maszyny) oraz --client-url. Ponowne uruchomienie pair rotuje klucz tego urządzenia i porzuca jego aktywne sesje. Parowanie nigdy nie tworzy obszaru roboczego: robi to onboarding łączącego się klienta, nadając nazwę i tworząc pierwszy obszar.

Z opcją --client-url wypisuje też kod QR do zeskanowania z terminala, który otwiera klienta webowego z już wypełnionymi danymi:

Okno terminala
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030 \
--bind any --host 192.168.1.42 --client-url https://app.usectrl.dev

Poświadczenie wygenerowane przez cc_server pair nie ma terminu ważności; to wygenerowane w aplikacji zawsze wygasa po 30 dniach. Gdy urządzenie utworzone z CLI przestanie być potrzebne, odwołaj je w Ustawienia → Ty → Twoje urządzenia.

Na loopbacku — to ustawienie domyślne i wszystko, czego potrzebujesz dla klienta na tej samej maszynie:

Okno terminala
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030

Aby uzyskać do niego dostęp z innej maszyny, powiąż wszystkie interfejsy. Powiązanie z adresem innym niż loopback bez TLS jest odrzucane przy starcie (Refusing to bind non-loopback address … without TLS), więc dostarcz certyfikat:

Okno terminala
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \
--bind any --tls-cert /path/fullchain.pem --tls-key /path/privkey.pem \
--public-url wss://server.example:9030/rpc

Albo terminuj TLS w odwrotnym proxy w zaufanej sieci prywatnej i zdecyduj się na nieszyfrowane powiązanie za nim:

Okno terminala
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \
--bind any --insecure --public-url wss://server.example/rpc

--insecure jest ignorowane, gdy ustawione są jednocześnie --tls-cert i --tls-key — TLS zawsze wygrywa. Serwer ogłasza stan nieszyfrowany jako insecure: true w GET /healthz, a klienty oznaczają takie połączenie jako niezabezpieczone.

Serwer działa aż do SIGINT/SIGTERM, po czym zamyka się czysto.

Skieruj klienta na adres URL serwera i klucz parowania z kroku 3:

  • Aplikacja desktopowa: na ekranie pierwszego uruchomienia (Jak ma działać Control Center?) wypełnij pola połączenia zamiast Uruchom w tej aplikacji, albo dodaj serwer później w Ustawienia → Serwer → Połączenie i status. Zobacz Połącz się ze zdalnym serwerem.
  • Klient webowy: otwórz app.usectrl.dev (lub własną kopię kompilacji webowej) i podaj adres URL serwera, identyfikator urządzenia oraz klucz parowania.
  • Telefon: sparuj aplikację Remote z działającego klienta — zobacz Sparuj urządzenie.

Aby zsynchronizować kalendarz obszaru roboczego bez podłączonego interfejsu graficznego, użyj przepływu kodu urządzenia:

Okno terminala
GOOGLE_OAUTH_CLIENT_ID=<id> GOOGLE_OAUTH_CLIENT_SECRET=<secret> \
./build/cli/<os_arch>/bundle/bin/cc_server calendar connect \
--data-dir ./data --workspace <workspace-id>

Wypisuje kod i adres URL do zatwierdzenia na innym urządzeniu, zapisuje token odświeżania po stronie serwera i kończy działanie. Kompilacja ze źródeł nie zawiera klienta Google, więc zmienne środowiskowe z identyfikatorem i sekretem są wymagane — nie ma flagi --google-client-id. Zobacz Połącz Google Calendar.

Flagi użyte powyżej to te, których potrzebuje pierwsza konfiguracja. Pełny zakres — 23 pary flag i zmiennych środowiskowych, cztery podkomendy oraz układ katalogu danych — znajdziesz w dokumentacji referencyjnej CLI cc_server.

Cztery, które najprawdopodobniej Ci się przydadzą:

Flaga Wartość domyślna Znaczenie
--data-dir katalog danych aplikacji dla użytkownika w danym systemie Bazy danych, sekrety, modele i pamięć podręczna multimediów
--port 9030 Port TCP (0 = efemeryczny)
--bind loopback loopback albo any dla wszystkich interfejsów (wymaga TLS lub --insecure)
--public-url wyprowadzany z powiązania Adres URL RPC ogłaszany sparowanym klientom. Ustaw go jawnie za proxy, NAT lub tunelem

Domyślny katalog danych to ~/Library/Application Support/control-center w systemie macOS, %APPDATA%\control-center w systemie Windows oraz $XDG_DATA_HOME/control-center (a w przeciwnym razie ~/.local/share/control-center) w pozostałych systemach. Katalog .cc_server względny wobec bieżącego katalogu roboczego jest używany tylko wtedy, gdy nie da się ustalić katalogu domowego ani katalogu danych aplikacji.

cc_server to cała powierzchnia produktu, a nie jej podzbiór: ścieżka danych repo-RPC i subskrypcje, endpoint narzędzi MCP pod POST /mcp i GET /sse na tym samym porcie, przychodzące webhooki, proxy multimediów i fontów, wywołania zwrotne logowania jednokrotnego, protokół dzierżawy floty, pobieranie RSS po stronie serwera według 30-minutowego harmonogramu oraz wyszukiwanie wektorowe (rozszerzenie sqlite_vector jest rejestrowane jako auto-rozszerzenie globalne dla procesu, degradując do wyszukiwania pełnotekstowego tylko tam, gdzie rozszerzenie jest niedostępne).

Dyspozytor jest bezstanowy: nie ma powiązania obszaru roboczego z sesją. Każde wywołanie w zakresie obszaru roboczego niesie własny workspace_id, a o dostępie decyduje członkostwo i rola wywołującego w obszarze roboczym — nie posiadanie klucza parowania.

Dwie rzeczy, o których warto wiedzieć, zanim wystawisz port poza loopback:

  • MCP działa w trybie fail-closed poza hostem. Powierzchnia MCP bez tokenu odpowiada tylko na loopbacku. Powiązanie any tego nie rozszerza — żądanie spoza hosta jest odrzucane komunikatem „MCP requires a bearer token to answer non-loopback clients”, dopóki nie skonfigurujesz tokenu (mcp.setToken albo Ustawienia → Serwer → Serwery MCP).
  • GET /healthz jest celowo nieuwierzytelnione, ma wieloznaczne CORS i publikuje identyfikator serwera, jego nazwę, odcisk tożsamości i wersję kompilacji. Klienty potrzebują odcisku, aby przypiąć serwer, więc jest to zamierzone — oznacza to jednak, że każdy osiągalny cc_server ujawnia tyle każdemu, kto o to zapyta.