Rulează un server headless
Rulează cc_server pe o mașină care rămâne pornită, apoi conectează-te la el de
pe desktop, dintr-un browser sau de pe telefon. Serverul este un binar nativ scris
în pur Dart — fără motor Flutter —, așa că rulează headless pe macOS, Linux sau
Windows. El deține baza de date, rulează agenții și face fiecare apel API extern;
clienții doar afișează.
Rulează în Docker (imagini precompilate)
Secțiune intitulată „Rulează în Docker (imagini precompilate)”Fiecare versiune publică un stack auto-găzduit de patru imagini GHCR — backendul,
cele două clienturi subțiri statice și releul de asociere opțional — plus o
imagine separată cc-server-demo pentru gazda publică de demo. O instalare
auto-găzduită nu are nevoie de o copie a codului sursă. Fixează un tag de
versiune (ghcr.io/samuelalev/<image>:v0.0.1-rc.1) sau mergi pe latest.
| Imagine | Ce este | Publică |
|---|---|---|
ghcr.io/samuelalev/cc-server |
Backendul: baza de date, agenții, MCP, webhook-uri | -p 9030:9030 |
ghcr.io/samuelalev/cc-webapp |
Clientul web (nginx static) | -p 8080:8080 |
ghcr.io/samuelalev/cc-remote |
PWA-ul companion de pe telefon (nginx static) | -p 8081:8080 |
ghcr.io/samuelalev/cc-signaling-server |
Releul de asociere WebRTC (opțional) | -p 8788:8788 |
Tot stackul ca fișier 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:Lucruri pe care merită să le știi înainte de a expune toate acestea dincolo de loopback:
-
Imaginea
cc-servervine cuCC_SERVER_INSECURE=1(trebuie să se lege la0.0.0.0ca să primească traficul din porturile publicate), ceea ce este sigur doar în spatele unui proxy invers care termină TLS — Caddy, Traefik, nginx sau un Cloudflare Tunnel —, într-o rețea de încredere. Ca să termini TLS în proces, montează în schimb un certificat și seteazăCC_SERVER_TLS_CERT/CC_SERVER_TLS_KEY(apoi scoateCC_SERVER_INSECURE). Totul poate fi suprascris cudocker run -e …sau cu bloculenvironment:din compose. -
/dataeste singura stare. Bazele de date SQLite, secretele dispozitivelor asociate, modelele descărcate și audio-ul întâlnirilor stau toate în volum — fă-i un backup, iar containerul devine eliminabil. -
Asociază un client din interiorul imaginii înainte de prima conexiune, echivalentul în container al pasului 3:
Fereastră terminal docker run --rm -it -v cc_data:/data \--entrypoint /app/bin/cc_server ghcr.io/samuelalev/cc-server:latest pair -
Cele două imagini de client sunt gazde nginx statice — nimic de configurat la runtime; formularul de conectare al fiecărui client preia URL-ul serverului, ID-ul de dispozitiv și cheia de asociere. Releul de semnalizare, la fel, are nevoie doar de portul său (
SIGNALING_HOST/SIGNALING_PORTpot fi suprascrise); este fără stare, nu ține niciun secret și este o soluție de rezervă — un telefon folosește loopback, LAN-ul, tailnet-ul sau o calewss://directă atunci când una este accesibilă și trece pe releu doar când niciuna nu este. Sari peste el dacă clienții tăi ajung întotdeauna direct la server.
Restul acestui ghid construiește serverul din surse; flagurile și comportamentul pe care le acoperă sunt aceleași pe care le alimentează variabilele de mediu ale imaginii.
Cerințe preliminare
Secțiune intitulată „Cerințe preliminare”- SDK-ul Dart. Acest depozit își fixează SDK-ul cu fvm, așa că pune prefixul
fvmînaintea fiecărei comenzi. - Acest depozit clonat local.
- Lanțul de unelte native de care are nevoie scriptul de pregătire — Rust/
cargo, plusmeson,ninja,pkg-config,gitși un compilator C++. Fiecare script de build numește ce nu găsește și cum se instalează. - O gazdă accesibilă: loopback pentru uz local sau o adresă LAN/publică cu TLS pentru clienții remote.
Pasul 1: Pregătește bibliotecile native
Secțiune intitulată „Pasul 1: Pregătește bibliotecile native”Bibliotecile native sunt obligatorii — nu există un mod degradat. Compilează-le din rădăcina depozitului înainte de orice altceva:
scripts/natives/build_natives.shAceasta compilează rift, fff, tree-sitter plus gramaticile sale, aec, lame,
ccpty, cc_watcher, cc_inference și biblioteca nativă SAML în
build/natives/. Hook-ul de build al cc_server
reemite în bundle tot ce este pregătit acolo, așa că sărirea peste acest pas fie
eșuează direct buildul, fie — cu portița de scăpare de mai jos — produce un binar
a cărui verificare prealabilă de la pornire refuză să pornească și numește
biblioteca lipsă.
Pasul 2: Compilează binarul
Secțiune intitulată „Pasul 2: Compilează binarul”cd apps/cc_serverfvm dart build cliBundle-ul include libsqlite3 și bibliotecile native pregătite alături de binar —
nu e nevoie de SQLite de sistem și nici de motorul Flutter. Executabilul ajunge în
./build/cli/<os_arch>/bundle/bin/cc_server.
Pasul 3: Asociază un client înainte de prima pornire
Secțiune intitulată „Pasul 3: Asociază un client înainte de prima pornire”Un director de date nou nu are niciun dispozitiv asociat și nicio cheie
prepartajată, așa că solicitarea cheii de asociere a unui client nu poate fi
satisfăcută încă. Emite una cu subcomanda pair. Ea scrie direct global.db și
secrets.json; un server care servește deja acel director de date preia noul
dispozitiv fără repornire:
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030Afișează trei valori pe care să le lipești în formularul de conectare al unui client:
Connect a thin client (paste into its connect form): Server ws://localhost:9030/rpc Device id web-client Pairing key <a freshly generated key>Flaguri utile: --device (implicit web-client; ID-ul alege și nivelul
dispozitivului — desktop, ios, android, orice altceva este un client web),
--label (implicit numele platformei plus numele gazdă al acestei mașini),
--host (gazda de afișat în URL, pentru că loopback nu este accesibil de pe altă
mașină) și --client-url. Rularea din nou a lui pair rotește cheia acelui
dispozitiv și elimină sesiunile lui active. Asocierea nu creează niciodată un
spațiu de lucru: onboarding-ul clientului care se conectează numește și creează
primul spațiu.
Cu --client-url afișează și un cod QR care poate fi scanat din terminal și care
deschide clientul web deja completat:
./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.devO acreditare emisă de cc_server pair nu are nicio expirare; una emisă în
aplicație expiră întotdeauna după 30 de zile. Revocă un dispozitiv emis din CLI
din Setări → Tu → Dispozitivele tale când ai terminat cu el.
Pasul 4: Pornește serverul
Secțiune intitulată „Pasul 4: Pornește serverul”Pe loopback — valoarea implicită și tot ce ai nevoie pentru un client de pe aceeași mașină:
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030Ca să ajungi la el de pe altă mașină, leagă-l la toate interfețele. O legare
non-loopback fără TLS este refuzată la pornire (Refusing to bind non-loopback address … without TLS), așa că furnizează un certificat:
./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/rpcSau termină TLS într-un proxy invers, într-o rețea privată de încredere, și optează pentru o legare în text simplu în spatele lui:
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \ --bind any --insecure --public-url wss://server.example/rpc--insecure este ignorat când --tls-cert și --tls-key sunt ambele setate —
TLS câștigă întotdeauna. Serverul anunță starea de text simplu ca
insecure: true în GET /healthz, iar clienții marchează o astfel de conexiune
ca nesigură.
Serverul rulează până la SIGINT/SIGTERM, apoi se oprește curat.
Pasul 5: Conectează un client
Secțiune intitulată „Pasul 5: Conectează un client”Îndreaptă un client către URL-ul serverului și cheia de asociere de la pasul 3:
- Aplicația desktop: pe ecranul de prima rulare (Cum ar trebui să ruleze Control Center?) completează câmpurile de conectare în loc de Rulează în această aplicație, sau adaugă serverul mai târziu din Setări → Server → Conexiune și stare. Vezi Conectează-te la un server remote.
- Client web: deschide app.usectrl.dev (sau propria copie a versiunii web) și introdu URL-ul serverului, ID-ul de dispozitiv și cheia de asociere.
- Telefon: asociază aplicația Remote dintr-un client care rulează — vezi Asociază un dispozitiv.
Pasul 6: Conectează un Google Calendar (opțional)
Secțiune intitulată „Pasul 6: Conectează un Google Calendar (opțional)”Ca să sincronizezi calendarul unui spațiu de lucru fără nicio interfață grafică atașată, folosește fluxul cu cod de dispozitiv:
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>Afișează un cod și un URL de aprobat pe alt dispozitiv, stochează tokenul de
reîmprospătare pe partea de server și iese. O compilare din surse nu include
niciun client Google, așa că variabilele de mediu pentru ID și secret sunt
obligatorii — nu există un flag --google-client-id. Vezi Conectează un Google
Calendar.
Configurare
Secțiune intitulată „Configurare”Flagurile folosite mai sus sunt cele de care are nevoie o primă configurare.
Suprafața completă — 23 de perechi de flaguri și variabile de mediu, cele patru
subcomenzi și structura directorului de date — se află în referința CLI
cc_server.
Cele patru de care vei avea cel mai probabil nevoie:
| Flag | Implicit | Semnificație |
|---|---|---|
--data-dir |
directorul de date de aplicație per utilizator al sistemului de operare | Bazele de date, secretele, modelele și media din cache |
--port |
9030 |
Port TCP (0 = efemer) |
--bind |
loopback |
loopback sau any pentru toate interfețele (necesită TLS sau --insecure) |
--public-url |
derivat din legare | URL-ul RPC anunțat clienților asociați. Setează-l explicit în spatele unui proxy, NAT sau tunel |
Directorul de date implicit este ~/Library/Application Support/control-center pe
macOS, %APPDATA%\control-center pe Windows și $XDG_DATA_HOME/control-center
(altfel ~/.local/share/control-center) în restul cazurilor. Un .cc_server
relativ la directorul de lucru este folosit doar când nu se rezolvă niciun
director home sau de date de aplicație.
Ce servește serverul
Secțiune intitulată „Ce servește serverul”cc_server este întreaga suprafață a produsului, nu un subset: calea de date
repo-RPC și abonamentele, endpointul de instrumente MCP la POST /mcp și
GET /sse pe același port, webhook-urile primite, proxy-urile de media și de
fonturi, callback-urile de autentificare unică, protocolul de concesionare al
flotei, preluarea RSS pe partea de server la un interval de 30 de minute și
căutarea vectorială (extensia sqlite_vector este înregistrată ca auto-extensie
globală de proces, degradând la căutare full-text doar acolo unde extensia nu
este disponibilă).
Dispatcherul este fără stare: nu există nicio legătură de spațiu de lucru per
sesiune. Fiecare apel limitat la un spațiu de lucru își poartă propriul
workspace_id, iar accesul este decis de apartenența și rolul apelantului în
spațiul de lucru — nu de deținerea unei chei de asociere.
Două lucruri de știut înainte de a expune portul dincolo de loopback:
- MCP eșuează închis în afara gazdei. O suprafață MCP fără token răspunde doar pe loopback. Legarea la
anynu o lărgește — o cerere din afara gazdei este refuzată cu „MCP requires a bearer token to answer non-loopback clients” până când configurezi unul (mcp.setToken, sau Setări → Server → Servere MCP). GET /healthzeste neautentificat prin proiectare, cu CORS wildcard, și publică ID-ul, numele, amprenta de identitate și versiunea de build ale serverului. Clienții au nevoie de amprentă ca să fixeze serverul, deci este deliberat — dar înseamnă că oricecc_serveraccesibil dezvăluie atât oricui întreabă.
Ghiduri conexe
Secțiune intitulată „Ghiduri conexe”- Conectează-te la un server remote
- Asociază un dispozitiv
- Rulează un worker din flotă
- Conectează un Google Calendar