Executar um servidor headless
Execute o cc_server em uma máquina que fique ligada e depois conecte-se a ele
pelo seu desktop, por um navegador ou pelo seu celular. O servidor é um binário
nativo em Dart puro — sem engine do Flutter — então ele roda headless no macOS,
Linux ou Windows. Ele detém o banco de dados, executa os agentes e faz todas as
chamadas de API externas; os clientes apenas renderizam.
Executar no Docker (imagens pré-compiladas)
Seção intitulada “Executar no Docker (imagens pré-compiladas)”Cada release publica um stack auto-hospedado de quatro imagens do GHCR — o
backend, os dois clientes finos estáticos e o relay de pareamento opcional — além
de uma imagem cc-server-demo separada para o host de demonstração público. Uma
instalação auto-hospedada não precisa de nenhum checkout do código-fonte. Fixe
uma tag de versão (ghcr.io/samuelalev/<image>:v0.0.1-rc.1) ou use latest.
| Imagem | O que é | Publica |
|---|---|---|
ghcr.io/samuelalev/cc-server |
O backend: banco de dados, agentes, MCP, webhooks | -p 9030:9030 |
ghcr.io/samuelalev/cc-webapp |
O cliente web (nginx estático) | -p 8080:8080 |
ghcr.io/samuelalev/cc-remote |
O PWA complementar para celular (nginx estático) | -p 8081:8080 |
ghcr.io/samuelalev/cc-signaling-server |
O relay de pareamento WebRTC (opcional) | -p 8788:8788 |
O stack inteiro como um arquivo 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:Coisas que vale a pena saber antes de expor qualquer parte disso além do loopback:
-
A imagem
cc-servervem comCC_SERVER_INSECURE=1(ela precisa se vincular a0.0.0.0para receber o tráfego da porta publicada), o que só é seguro atrás de um proxy reverso que termina TLS — Caddy, Traefik, nginx ou um Cloudflare Tunnel — em uma rede confiável. Para terminar o TLS no próprio processo, monte um certificado e definaCC_SERVER_TLS_CERT/CC_SERVER_TLS_KEY(e então removaCC_SERVER_INSECURE). Tudo pode ser sobrescrito comdocker run -e …ou o blocoenvironment:do compose. -
/dataé o único estado. Os bancos SQLite, os segredos dos dispositivos emparelhados, os modelos baixados e o áudio das reuniões ficam todos no volume — faça backup dele e o contêiner se torna descartável. -
Emparelhe um cliente de dentro da imagem antes da primeira conexão, o equivalente em contêiner do passo 3:
Janela do terminal docker run --rm -it -v cc_data:/data \--entrypoint /app/bin/cc_server ghcr.io/samuelalev/cc-server:latest pair -
As duas imagens de cliente são hosts nginx estáticos — nada para configurar em tempo de execução; o formulário de conexão de cada cliente recebe a URL do servidor, o id do dispositivo e a chave de pareamento. O relay de sinalização também precisa apenas da porta dele (
SIGNALING_HOST/SIGNALING_PORTsobrescrevíveis); ele não tem estado, não guarda segredos e é um fallback — um celular usa loopback, LAN, tailnet ou um caminhowss://direto quando algum está acessível, e só recorre ao relay quando nenhum está. Pule-o se os seus clientes sempre alcançam o servidor diretamente.
O restante deste guia compila o servidor a partir do código-fonte; as flags e o comportamento abordados são os mesmos alimentados pelas variáveis de ambiente da imagem.
Pré-requisitos
Seção intitulada “Pré-requisitos”- O Dart SDK. Este repositório fixa o SDK dele com fvm, então prefixe todo comando com
fvm. - Este repositório clonado.
- O toolchain nativo de que o script de preparação precisa — Rust/
cargo, além demeson,ninja,pkg-config,gite um compilador de C++. Cada script de build diz o que não conseguiu encontrar e como instalá-lo. - Um host alcançável: loopback para uso local, ou um endereço de LAN/público com TLS para clientes remotos.
Etapa 1: Preparar as bibliotecas nativas
Seção intitulada “Etapa 1: Preparar as bibliotecas nativas”As bibliotecas nativas são obrigatórias — não existe modo degradado. Compile-as a partir da raiz do repositório antes de qualquer outra coisa:
scripts/natives/build_natives.shIsso compila rift, fff, tree-sitter e as grammars dele, aec, lame,
ccpty, cc_watcher, cc_inference e a biblioteca nativa de SAML em
build/natives/. O hook de build do cc_server
reemite para o pacote tudo o que estiver preparado ali, então pular esta etapa
ou faz o build falhar de imediato ou — com a válvula de escape abaixo — produz um
binário cuja verificação de inicialização se recusa a iniciar e aponta a
biblioteca ausente.
Etapa 2: Compilar o binário
Seção intitulada “Etapa 2: Compilar o binário”cd apps/cc_serverfvm dart build cliO pacote traz libsqlite3 e os nativos preparados junto ao binário — não é
preciso SQLite do sistema nem engine do Flutter. O executável fica em
./build/cli/<os_arch>/bundle/bin/cc_server.
Etapa 3: Emparelhe um cliente antes da primeira inicialização
Seção intitulada “Etapa 3: Emparelhe um cliente antes da primeira inicialização”Um diretório de dados novo não tem nenhum dispositivo emparelhado nem chave
pré-compartilhada, então o prompt de chave de pareamento de um cliente ainda não
pode ser atendido. Gere uma com o subcomando pair.
Ele grava global.db e secrets.json diretamente; um servidor que já está
atendendo esse diretório de dados reconhece o novo dispositivo sem reiniciar:
./build/cli/<os_arch>/bundle/bin/cc_server pair --data-dir ./data --port 9030Ele imprime três valores para colar no formulário de conexão de um cliente:
Connect a thin client (paste into its connect form): Server ws://localhost:9030/rpc Device id web-client Pairing key <a freshly generated key>Flags úteis: --device (padrão web-client; o id também define o nível do
dispositivo — desktop, ios, android, qualquer outra coisa é um cliente
web), --label (o padrão é o nome da plataforma mais o hostname desta máquina),
--host (o host a imprimir na URL, já que o loopback não é alcançável de outra
máquina) e --client-url. Executar pair de novo rotaciona a chave desse
dispositivo e derruba as sessões ativas dele. O pareamento nunca cria um espaço
de trabalho: o onboarding do cliente que se conecta dá nome e cria o primeiro.
Com --client-url ele também imprime um QR code escaneável pelo terminal que
abre o cliente web já preenchido:
./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.devUma credencial gerada por cc_server pair não tem expiração; uma gerada no
app sempre expira após 30 dias. Revogue um dispositivo gerado pela CLI em
Configurações → Você → Seus dispositivos quando terminar de usá-lo.
Etapa 4: Iniciar o servidor
Seção intitulada “Etapa 4: Iniciar o servidor”No loopback — o padrão e tudo o que você precisa para um cliente na mesma máquina:
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030Para alcançá-lo de outra máquina, vincule todas as interfaces. Um bind que não
seja de loopback sem TLS é recusado na inicialização (Refusing to bind non-loopback address … without TLS), então forneça um certificado:
./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/rpcOu termine o TLS em um proxy reverso em uma rede privada confiável e opte por um bind em texto simples atrás dele:
./build/cli/<os_arch>/bundle/bin/cc_server --data-dir ./data --port 9030 \ --bind any --insecure --public-url wss://server.example/rpc--insecure é ignorado quando --tls-cert e --tls-key estão ambos definidos —
o TLS sempre vence. O servidor anuncia o estado de texto simples como
insecure: true em GET /healthz e os clientes marcam essa conexão como
insegura.
O servidor roda até receber SIGINT/SIGTERM e então encerra de forma limpa.
Etapa 5: Conectar um cliente
Seção intitulada “Etapa 5: Conectar um cliente”Aponte um cliente para a URL do servidor e a chave de pareamento da etapa 3:
- App de desktop: na tela de primeira execução (Como o Control Center deve ser executado?) preencha os campos de conexão em vez de Executar neste aplicativo, ou adicione o servidor depois em Configurações → Servidor → Conexão e status. Consulte Conectar a um servidor remoto.
- Cliente web: abra app.usectrl.dev (ou a sua própria cópia do build web) e informe a URL do servidor, o id do dispositivo e a chave de pareamento.
- Celular: emparelhe o app Remote a partir de um cliente em execução — consulte Emparelhar um dispositivo.
Etapa 6: Conectar um Google Calendar (opcional)
Seção intitulada “Etapa 6: Conectar um Google Calendar (opcional)”Para sincronizar o calendário de um espaço de trabalho sem nenhuma GUI conectada, use o fluxo de código de dispositivo:
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>Ele imprime um código e uma URL para aprovar em outro dispositivo, armazena o
refresh token no servidor e encerra. Um build a partir do código-fonte não traz
cliente Google, então as variáveis de ambiente de id e secret são obrigatórias —
não existe flag --google-client-id. Consulte
Conectar um Google Calendar.
Configuração
Seção intitulada “Configuração”As flags usadas acima são as de que uma primeira configuração precisa. A
superfície completa — 23 pares de flag e variável de ambiente, os quatro
subcomandos e o layout do diretório de dados — está na
referência da CLI do cc_server.
As quatro que você provavelmente vai precisar:
| Flag | Padrão | Significado |
|---|---|---|
--data-dir |
o diretório de dados de aplicativo por usuário do SO | Bancos de dados, segredos, modelos e mídia em cache |
--port |
9030 |
Porta TCP (0 = efêmera) |
--bind |
loopback |
loopback, ou any para todas as interfaces (precisa de TLS ou --insecure) |
--public-url |
derivado do bind | A URL de RPC anunciada aos clientes emparelhados. Defina-a explicitamente atrás de um proxy, NAT ou túnel |
O diretório de dados padrão é ~/Library/Application Support/control-center no
macOS, %APPDATA%\control-center no Windows e $XDG_DATA_HOME/control-center
(ou então ~/.local/share/control-center) nos demais. Um .cc_server relativo
ao cwd só é usado quando nenhum diretório home ou de dados de aplicativo é
resolvido.
O que o servidor serve
Seção intitulada “O que o servidor serve”O cc_server é toda a superfície do produto, não um subconjunto: o caminho de
dados e as assinaturas de repo-RPC, o endpoint de ferramentas MCP em POST /mcp
e GET /sse na mesma porta, webhooks de entrada, os proxies de mídia e de
fontes, callbacks de logon único, o protocolo de lease da frota, a busca de RSS
do lado do servidor em um agendamento de 30 minutos e a busca vetorial (a
extensão sqlite_vector é registrada como auto-extension global do processo,
degradando para busca de texto completo apenas onde a extensão não está
disponível).
O dispatcher é sem estado: não existe vínculo de espaço de trabalho por
sessão. Toda chamada com escopo de espaço de trabalho carrega o próprio
workspace_id e o acesso é decidido pela associação e pelo papel do chamador no
espaço de trabalho — não por deter uma chave de pareamento.
Duas coisas que você precisa saber antes de expor a porta além do loopback:
- O MCP falha fechado fora do host. Uma superfície MCP sem token responde apenas ao loopback. Vincular
anynão a amplia — uma requisição de fora do host é recusada com “MCP requires a bearer token to answer non-loopback clients” até você configurar um (mcp.setToken, ou Configurações → Servidor → Servidores MCP). GET /healthznão tem autenticação por design, com CORS curinga, e publica o id, o nome, a impressão digital de identidade e a versão de build do servidor. Os clientes precisam da impressão digital para fixar o servidor, então isso é deliberado — mas significa que qualquercc_serveralcançável divulga tudo isso a quem perguntar.
Guias relacionados
Seção intitulada “Guias relacionados”- Conectar a um servidor remoto
- Emparelhar um dispositivo
- Executar um worker da frota
- Conectar um Google Calendar