Een headless server draaien
Draai cc_server op een machine die aan blijft staan en maak er vervolgens
verbinding mee vanaf je desktop, een browser of je telefoon. De server is een
native binary in puur Dart — geen Flutter-engine — en draait dus headless op
macOS, Linux en Windows. Hij bezit de database, draait de agenten en doet elke
externe API-aanroep; de clients renderen alleen.
Draaien in Docker (vooraf gebouwde images)
Sectie getiteld “Draaien in Docker (vooraf gebouwde images)”Elke release publiceert een self-hosted stack van vier GHCR-images — de backend,
de twee statische thin clients en de optionele koppelingsrelay — plus een aparte
cc-server-demo-image voor de openbare demohost. Een self-hosted installatie
heeft geen source-checkout nodig. Pin een versietag
(ghcr.io/samuelalev/<image>:v0.0.1-rc.1) of blijf op latest.
| Image | Wat het is | Publiceert |
|---|---|---|
ghcr.io/samuelalev/cc-server |
De backend: database, agenten, MCP, webhooks | -p 9030:9030 |
ghcr.io/samuelalev/cc-webapp |
De webclient (statische nginx) | -p 8080:8080 |
ghcr.io/samuelalev/cc-remote |
De telefooncompanion-PWA (statische nginx) | -p 8081:8080 |
ghcr.io/samuelalev/cc-signaling-server |
De WebRTC-koppelingsrelay (optioneel) | -p 8788:8788 |
De hele stack als composebestand:
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:Dingen die je moet weten voordat je dit alles buiten loopback beschikbaar stelt:
-
De
cc-server-image levertCC_SERVER_INSECURE=1mee (hij moet0.0.0.0binden om verkeer via gepubliceerde poorten te ontvangen), wat alleen veilig is achter een TLS-beëindigende reverse proxy — Caddy, Traefik, nginx of een Cloudflare Tunnel — op een vertrouwd netwerk. Wil je TLS juist in-process beëindigen, mount dan een certificaat en stelCC_SERVER_TLS_CERT/CC_SERVER_TLS_KEYin (haal daarnaCC_SERVER_INSECUREweg). Alles is te overschrijven metdocker run -e …of hetenvironment:-blok van compose. -
/datais de enige state. De SQLite-databases, secrets van gekoppelde apparaten, gedownloade modellen en vergaderaudio staan allemaal in het volume — maak er een back-up van en de container is wegwerpbaar. -
Koppel een client vanuit de image zelf vóór de eerste verbinding, het container-equivalent van stap 3:
Terminalvenster docker run --rm -it -v cc_data:/data \--entrypoint /app/bin/cc_server ghcr.io/samuelalev/cc-server:latest pair -
De twee client-images zijn statische nginx-hosts — niets om tijdens runtime te configureren; het verbindingsformulier van elke client neemt je server-URL, apparaat-id en koppelingssleutel. De signaleringsrelay heeft net zo goed alleen zijn poort nodig (
SIGNALING_HOST/SIGNALING_PORTzijn te overschrijven); hij is stateless, bevat geen secrets en is een fallback — een telefoon gebruikt loopback, LAN, tailnet of een directwss://-pad wanneer dat bereikbaar is, en valt alleen terug op de relay als geen daarvan werkt. Sla hem over als je clients de server altijd rechtstreeks bereiken.
De rest van deze gids bouwt de server uit de source; de flags en het gedrag die erin aan bod komen, zijn dezelfde als waar de omgevingsvariabelen van de image op uitkomen.
Vereisten
Sectie getiteld “Vereisten”- De Dart SDK. Deze repo pint zijn SDK met fvm, dus zet
fvmvoor elk commando. - Deze repository uitgecheckt.
- De native toolchain die het staging-script nodig heeft — Rust/
cargo, plusmeson,ninja,pkg-config,giten een C++-compiler. Elk buildscript noemt wat het niet kan vinden en hoe je het installeert. - Een bereikbare host: loopback voor lokaal gebruik, of een LAN-/openbaar adres met TLS voor externe clients.
Stap 1: De native libraries klaarzetten
Sectie getiteld “Stap 1: De native libraries klaarzetten”De natives zijn verplicht — er is geen degraded mode. Bouw ze als eerste vanuit de root van de repo:
scripts/natives/build_natives.shDit bouwt rift, fff, tree-sitter plus de grammatica’s, aec, lame,
ccpty, cc_watcher, cc_inference en de SAML-native naar
build/natives/. De buildhook van cc_server neemt alles wat daar is
klaargezet opnieuw op in de bundle, dus als je deze stap overslaat mislukt de
build of — met de noodoplossing hieronder — levert hij een binary op waarvan de
boot-preflight weigert te starten en de ontbrekende library noemt.
Stap 2: De binary bouwen
Sectie getiteld “Stap 2: De binary bouwen”cd apps/cc_serverfvm dart build cliDe bundle levert libsqlite3 en de klaargezette natives mee naast de binary —
geen systeem-SQLite en geen Flutter-engine nodig. Het uitvoerbare bestand komt
terecht in ./build/cli/<os_arch>/bundle/bin/cc_server.
Stap 3: Een client koppelen vóór de eerste start
Sectie getiteld “Stap 3: Een client koppelen vóór de eerste start”Een verse datadirectory heeft geen gekoppeld apparaat en geen vooraf gedeelde
sleutel, dus de prompt om een koppelingssleutel in een client kan nog niet worden
vervuld. Maak er een aan met het subcommando pair. Het schrijft rechtstreeks
naar global.db en secrets.json; een server die die datadir al bedient, neemt
het nieuwe apparaat over zonder herstart:
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030Hij drukt drie waarden af die je in het verbindingsformulier van een client plakt:
Connect a thin client (paste into its connect form): Server ws://localhost:9030/rpc Device id web-client Pairing key <a freshly generated key>Nuttige flags: --device (standaard web-client; het id bepaalt ook de
apparaattier — desktop, ios, android, al het andere is een webclient),
--label (standaard de platformnaam plus de hostnaam van deze machine), --host
(de host die in de URL wordt afgedrukt, want loopback is niet bereikbaar vanaf
een andere machine) en --client-url. Als je pair opnieuw uitvoert, roteert de
sleutel van dat apparaat en vervallen de live-sessies. Koppelen maakt nooit een
werkruimte aan: de onboarding van de verbindende client benoemt en maakt de
eerste.
Met --client-url drukt hij ook een QR af die je in de terminal kunt scannen en
die de webclient al ingevuld opent:
./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.devEen referentie die door cc_server pair is aangemaakt, heeft geen
vervaldatum; een referentie die in de app is aangemaakt, verloopt altijd na 30
dagen. Trek een via de CLI aangemaakt apparaat in via
Instellingen → Jij → Je apparaten wanneer je het niet meer nodig hebt.
Stap 4: De server starten
Sectie getiteld “Stap 4: De server starten”Op loopback — de standaard en alles wat je nodig hebt voor een client op dezelfde machine:
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030Om hem vanaf een andere machine te bereiken, bind je elke interface. Een
niet-loopbackbind zonder TLS wordt bij het opstarten geweigerd (Refusing to bind non-loopback address … without TLS), dus geef een certificaat mee:
./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/rpcOf beëindig TLS in een reverse proxy op een vertrouwd privénetwerk en kies voor een plaintextbind erachter:
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \ --bind any --insecure --public-url wss://server.example/rpc--insecure wordt genegeerd wanneer --tls-cert en --tls-key beide zijn
ingesteld — TLS wint altijd. De server adverteert de plaintext-state als
insecure: true in GET /healthz en clients tonen zo’n verbinding met de badge
Onveilig.
De server blijft draaien tot SIGINT/SIGTERM en sluit dan netjes af.
Stap 5: Een client verbinden
Sectie getiteld “Stap 5: Een client verbinden”Wijs een client naar de server-URL en koppelingssleutel uit stap 3:
- Desktop-app: vul op het eerste scherm (Hoe moet Control Center draaien?) de verbindingsvelden in in plaats van In deze app uitvoeren, of voeg de server later toe via Instellingen → Server → Verbinding en status. Zie Verbinden met een externe server.
- Webclient: open app.usectrl.dev (of je eigen kopie van de webbuild) en voer de server-URL, het apparaat-id en de koppelingssleutel in.
- Telefoon: koppel de Remote-app vanuit een draaiende client — zie Een apparaat koppelen.
Stap 6: Een Google Calendar verbinden (optioneel)
Sectie getiteld “Stap 6: Een Google Calendar verbinden (optioneel)”Gebruik de devicecode-flow om de agenda van een werkruimte te synchroniseren zonder GUI:
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>Hij drukt een code en een URL af om op een ander apparaat goed te keuren, slaat
het refreshtoken server-side op en stopt. Een build uit de source levert geen
Google-client mee, dus de omgevingsvariabelen voor id en secret zijn verplicht —
er is geen --google-client-id-flag. Zie Een Google Calendar verbinden.
Configuratie
Sectie getiteld “Configuratie”De flags hierboven zijn de flags die een eerste setup nodig heeft. Het volledige
oppervlak — 23 paren van flags en omgevingsvariabelen, de vier subcommando’s en
de indeling van de datadirectory — staat in de
cc_server CLI-referentie.
De vier die je het waarschijnlijkst nodig hebt:
| Flag | Standaard | Betekenis |
|---|---|---|
--data-dir |
de app-gegevensmap per gebruiker van het OS | Databases, secrets, modellen en gecachte media |
--port |
9030 |
TCP-poort (0 = kortstondig) |
--bind |
loopback |
loopback, of any voor elke interface (vereist TLS of --insecure) |
--public-url |
afgeleid van de bind | De RPC-URL die aan gekoppelde clients wordt geadverteerd. Stel deze expliciet in achter een proxy, NAT of tunnel |
De standaard datadir is ~/Library/Application Support/control-center op macOS,
%APPDATA%\control-center op Windows en $XDG_DATA_HOME/control-center (anders
~/.local/share/control-center) elders. Een .cc_server relatief aan de cwd
wordt alleen gebruikt als er geen home- of app-gegevensmap kan worden bepaald.
Wat de server serveert
Sectie getiteld “Wat de server serveert”cc_server is het volledige productoppervlak, geen subset: het repo-RPC-
gegevenspad en de abonnementen, het MCP-tool-endpoint op POST /mcp en
GET /sse op dezelfde poort, inkomende webhooks, de media- en font-proxy’s,
callbacks voor eenmalige aanmelding, het fleet-leaseprotocol, server-side
RSS-fetching volgens een schema van 30 minuten en vectorsearch (de
sqlite_vector-extensie wordt geregistreerd als proces-globale auto-extensie en
degradeert alleen naar full-text search waar de extensie niet beschikbaar is).
De dispatcher is stateless: er is geen werkruimtebinding per sessie. Elke
aanroep met werkruimtescope draagt zijn eigen workspace_id mee en toegang wordt
bepaald door het lidmaatschap en de rol van de aanroeper in de werkruimte — niet
door het bezit van een koppelingssleutel.
Twee dingen die je moet weten voordat je de poort buiten loopback beschikbaar stelt:
- MCP faalt gesloten buiten de host. Een MCP-oppervlak zonder token antwoordt alleen op loopback.
anybinden verruimt dat niet — een verzoek van buiten de host wordt geweigerd met “MCP requires a bearer token to answer non-loopback clients” tot je er een configureert (mcp.setToken, of Instellingen → Server → MCP-servers). GET /healthzis bewust niet geauthenticeerd, met wildcard-CORS, en publiceert het id, de naam, de identiteitsvingerafdruk en de buildversie van de server. Clients hebben de vingerafdruk nodig om de server te pinnen, dus dit is opzettelijk — maar het betekent wel dat elke bereikbarecc_serverdat alles prijsgeeft aan wie er maar om vraagt.
Gerelateerde gidsen
Sectie getiteld “Gerelateerde gidsen”- Verbinden met een externe server
- Een apparaat koppelen
- Een fleet-worker draaien
- Een Google Calendar verbinden