Pular para o conteúdo

Controle remoto e mobile

O Control Center é um app de desktop, mas o trabalho que ele acompanha não para quando você se afasta da mesa. O controle remoto emparelha o seu celular com o seu cc_server, usando o Remote, o app complementar em remote.usectrl.dev, para você acompanhar a frota de lá: ler mensagens e tickets, responder a um agente, triar o seu feed de notícias, sem que o áudio, a transcrição, o código-fonte ou as credenciais que vivem no servidor jamais saiam dele.

Pense nele como um companheiro predominantemente de leitura para o deck. Ele não foi feito para substituir o cliente de desktop completo.

O celular alcança o cc_server por um WebSocket: ou uma conexão direta (loopback, LAN, tailnet ou wss://), ou uma sala com entrada por convite em um broker de sinalização que repassa frames selados. Não há canal de dados ponto a ponto.

O celular emparelhado alcança uma fatia pequena e intencional da superfície do servidor:

  • Ler tickets, agentes, espaços e as mensagens deles, e o seu feed de notícias
  • Responder: enviar uma mensagem em um espaço, atualizar ou atribuir um ticket, marcar um artigo como lido ou salvo
  • Executar um playbook: instanciar um modelo de plano salvo com os seus parâmetros. Isso apenas propõe um plano; nada é executado nem gasta até que um operador o aprove no Plan Studio, em um cliente completo
  • Trocar de espaço de trabalho, pelo seletor do próprio celular

Isso é praticamente tudo. Um celular é um principal com menos privilégios do que um cliente completo. Ele não pode criar processos, gastar orçamento de LLM, fazer push para o GitHub nem criar espaços de trabalho. Consulte o modelo de acesso para ver como isso é imposto, e não apenas pretendido.

A conexão viaja por um relay intermediado: o servidor entra como proprietário em uma sala com entrada por convite em um broker wss://, e cada cliente emparelhado — desktop, web, celular — entra na mesma sala com um token de admissão de prazo limitado. Três peças se juntam:

Peça Papel
Remote (app de celular) O app web complementar, hospedado por padrão em remote.usectrl.dev, ou auto-hospedado. Carrega em um navegador e guarda o registro de emparelhamento no IndexedDB.
Broker de relay Um relay wss:// sem estado (cc_signaling_server) que hospeda salas de N vias. Ele transporta apenas frames opacos: cada payload é selado de ponta a ponta entre o celular e o servidor, então o broker nunca vê dados do app nem o segredo de emparelhamento, e nunca interpreta o que repassa.
cc_server O proprietário da sala. Ele responde às chamadas do celular pelo mesmo dispatcher que todos os outros clientes usam.

Uma oferta de emparelhamento carrega um descritor de conexão que lista todos os caminhos até o servidor (LAN, tailnet, wss:// público, relay) mais a sua impressão digital de identidade. O resolvedor do celular escolhe o melhor caminho alcançável e fixa a impressão digital na primeira conexão (TOFU); o relay garante a conectividade mesmo quando não existe caminho direto, sem nunca conseguir ler o tráfego.

O emparelhamento é emitido pelo app e entregue como um deep link; consulte Emparelhar um dispositivo para ver o procedimento. O que importa aqui é a anatomia.

https://<pwa-host>/#<payload>

O payload viaja no fragmento da URL, a parte depois de # que os navegadores nunca enviam ao servidor, então o host HTTPS da PWA nunca o vê. Ele carrega tudo de que o celular precisa para alcançar o servidor e provar quem é: o descritor de conexão (todos os caminhos mais a impressão digital de identidade), um id de dispositivo recém-emitido, uma chave pré-compartilhada de 32 bytes e um carimbo de validade. O celular o decodifica, guarda um registro de emparelhamento localmente e remove o fragmento, para que o segredo saia da URL.

Há dois formatos de payload e eles não são intercambiáveis. O QR de celular emitido pelo painel de emparelhamento do app é o PairingPayload v2 acima, apontado para o host da PWA Remote. cc_server pair --client-url emite um link v1 diferente e mais antigo ({s, i, k} — URL do servidor, id do dispositivo, chave), destinado apenas ao cliente web.

Eles também diferem no tempo de vida, o que é fácil de errar:

Emitido por Validade da credencial
Painel de emparelhamento do app (pairing.mint) 30 dias, sempre
cc_server pair na linha de comando Nenhuma — a credencial não expira

Um dispositivo emparelhado é autenticado, mas não confiável. Vários portões independentes impedem que um emparelhamento aprovado (ou vazado) se torne controle total do servidor.

1. A associação, não o emparelhamento, é a fronteira

Seção intitulada “1. A associação, não o emparelhamento, é a fronteira”

Uma chave de emparelhamento válida prova qual dispositivo você é. O que você pode ler ou alterar é decidido pelo seu papel no espaço de trabalho, resolvido de novo a cada chamada, e por concessões por repo em tudo que expõe conteúdo de repo. Um dispositivo que tem uma chave perfeitamente válida, mas pertence a um usuário que não é membro do espaço de trabalho informado, é recusado com “Not a member of this workspace”.

O papel mínimo exigido é derivado do tipo da operação, a menos que ela declare o seu próprio: leituras exigem guest, mutações exigem member, operações destrutivas exigem admin.

Além da verificação de papel, uma sessão de celular tem uma capacidade menor do que uma sessão de desktop ou web. Operações declaradas como exclusivas de cliente completo são recusadas para um celular antes que o handler seja executado, por mais privilegiado que seja o usuário por trás dele: emitir, listar, renomear e revogar emparelhamentos de dispositivos; ler e definir a conectividade do túnel; backup do servidor e exportação/importação de espaços de trabalho; detecção e encerramento de processos; sondagens de adaptadores; configurações do servidor; administração de SSO; controle da frota; e assumir o controle de um agente em execução ou devolvê-lo.

3. Uma lista de permissão de ferramentas com negação por padrão

Seção intitulada “3. Uma lista de permissão de ferramentas com negação por padrão”

Para a superfície de ferramentas MCP especificamente (tools/call), uma lista de permissão com negação por padrão determina quais ferramentas o celular pode invocar. Tudo que não estiver na lista é rejeitado antes de ser executado. A lista é pesada em leitura e observação, mais um punhado de escritas somente locais — uma mensagem, uma atualização de ticket, um sinalizador de artigo — que não gastam orçamento de LLM, não criam processo algum nem tocam em nenhum sistema externo.

Negados, por exemplo: consultar um agente, iniciar uma revisão com IA, encerrar um agente, publicar uma revisão no GitHub, contratar ou demitir agentes, criar um espaço de trabalho.

O servidor não mantém um “espaço de trabalho atual”. Ele é sem estado por design: toda chamada com escopo de espaço de trabalho precisa informar o workspace_id de destino nos seus argumentos, e o dispatcher recusa a chamada de imediato quando ele falta ou nomeia um espaço de trabalho que o servidor não tem registrado — antes que qualquer coisa abra um banco de dados. Um celular, portanto, nunca pode herdar nem derivar para o escopo de outro cliente, e nomear um espaço de trabalho alheio também não ajuda, porque a verificação de associação do portão 1 continua valendo. Esse é o mesmo invariante de isolamento de espaço de trabalho que o resto do produto aplica.

5. Frames selados de ponta a ponta com a chave pré-compartilhada

Seção intitulada “5. Frames selados de ponta a ponta com a chave pré-compartilhada”

Cada frame entre o celular e o servidor é selado com a chave de emparelhamento; o token de admissão só coloca um cliente na sala, ele não descriptografa nada. O broker repassa texto cifrado e nunca vê a chave. Um dispositivo revogado encontra a sua credencial já removida, por isso falha fechado ao reconectar.

Uma credencial emitida pelo app tem prazo limitado de 30 dias; passado isso, o portão de conexão a rejeita e o celular precisa emparelhar de novo. A revogação é imediata: o servidor observa a tabela de dispositivos e derruba as sessões abertas de um dispositivo revogado em segundos, em vez de esperar a próxima conexão dele.

Um limitador de janela deslizante limita o tráfego de ferramentas remotas a 120 chamadas por minuto, com um subteto mais apertado de 30 por minuto para os verbos de mutação. O orçamento pertence ao principal, não à sessão: três dispositivos seus consomem uma única cota, então um cliente sequestrado não compra folga abrindo mais sessões, e um membro ruidoso não deixa os demais sem cota.

O broker de relay é deliberadamente burro: ele hospeda salas, repassa frames selados e não guarda contas, dados do app nem chaves. As salas têm entrada por convite e são transitórias. Os dados do app, o áudio, a transcrição, o código-fonte e as credenciais ficam todos no servidor.

Depois de emparelhado, o celular recebe atualizações ao vivo como frames de notificação — novas mensagens de agentes, tickets atribuídos a você, mudanças de status — espelhando as notificações do próprio desktop. O servidor transmite esses frames a todas as sessões conectadas, e o filtro de espaço de trabalho é aplicado no lado do cliente, então o que você vê tem por escopo o espaço de trabalho que está olhando.

Superfície O que mostra
Configurações → Você → Seus dispositivos (/workspaces/:workspaceId/settings/you/devices) Clientes emparelhados com status ao vivo, além de Emparelhar um novo cliente (navegador web, app de desktop ou celular) e revogar

O mesmo painel emite emparelhamentos para um segundo navegador ou app de desktop, não apenas para celulares — o fluxo é idêntico, só o deep link muda.