Execução é local, coordenação é em rede
O control plane de agents mais seguro não tem endpoint "execute isto", por design.
Apollo Space Research
Apollo Space
Há uma requisição que nosso control plane nunca vai servir, e ela não é bloqueada por uma checagem de permissão. Ela está ausente. Vasculhe toda a superfície da API, cada rota, cada handler, cada versão, e você não vai encontrar um endpoint que signifique execute este comando naquela máquina. Não desabilitado. Não protegido atrás de um scope de admin. Ausente, do jeito que uma porta que você nunca abriu numa parede está ausente.
As pessoas presumem que isso é uma feature que ainda não construímos. É a que deliberadamente nos recusamos a construir.
O control plane de agents mais seguro não tem endpoint “execute isto”, por design.
Este post é sobre por que um sistema que coordena dezenas de agents em várias máquinas nunca deveria conseguir dizer a uma máquina o que fazer, e o que você constrói em vez disso quando tira esse poder da mesa de propósito.
O control plane ingênuo: um servidor que dá ordens
A forma óbvia de rodar uma frota de agents é colocar um cérebro no meio. Um coordenador, alcançável pela rede, segurando a lista de cada worker e cada máquina. Uma tarefa chega. O coordenador escolhe um worker, abre uma conexão e envia a ordem: você, ali, execute isto.
É a forma que todo tutorial de orquestração desenha, e por um fim de semana é maravilhosa. Você tem um lugar para olhar, um lugar para clicar, uma garganta para apertar. O coordenador sabe de tudo e comanda tudo.
Agora siga o que esse coordenador de fato é. É um servidor, exposto a uma rede, cujo trabalho legítimo é fazer outras máquinas executarem trabalho arbitrário sob comando. Isso não é a descrição de um orquestrador. Essa é a definição de manual de remote code execution, a classe mais catastrófica de vulnerabilidade que existe, só que você a construiu de propósito e a chamou de feature.
O perigo não é hipotético, e não é sobre um bug no seu código. O perigo é a capacidade. No momento em que um endpoint de rede pode fazer código rodar numa máquina onde ele não mora, cada camada de autenticação na frente dele agora é a única coisa entre um atacante e um shell na sua frota inteira. Um token roubado, uma requisição de confused deputy, um agent convencido a chamar o endpoint errado, e o raio de explosão é toda máquina que o coordenador consegue alcançar. Você não adicionou um risco. Você construiu um risco e o tornou o centro da arquitetura.
A resposta certa não é um muro mais grosso ao redor da porta perigosa. É não abrir a porta.
A regra: execução é local, coordenação é em rede
Então dividimos o sistema ao longo da única costura que importa. Fazer trabalho e concordar sobre trabalho são tarefas diferentes, e recebem regras diferentes.
Execução é local. Uma máquina roda apenas o que sua própria configuração diz para rodar. A decisão de iniciar um worker, reivindicar uma tarefa ou spawnar um sub-agent é tomada na própria máquina, por um supervisor lendo sua própria config local, e é executada como um processo local comum, nunca como um comando que chegou por um fio. Nada na rede consegue alcançar e fazer uma máquina trabalhar. A máquina decide, a máquina age.
Coordenação é em rede. A camada compartilhada, a parte que toda máquina consegue ver, é um registro do que está acontecendo, não uma alavanca que faz as coisas acontecerem. As máquinas publicam nela: heartbeats, resultados, “comecei isto”, “terminei aquilo”. As máquinas leem dela para ficar em sincronia. Mas os dados fluem em uma direção quando se trata de causar qualquer coisa. Uma máquina escreve seu próprio status para fora; nada escreve comandos para dentro.
A rede pode ver tudo e comandar nada.
Essa é a regra inteira, e ela inverte o design ingênuo exatamente. No modelo de dar ordens, o trabalho da rede é causar execução. No nosso, o trabalho da rede é observá-la. Execução é local, coordenação é em rede, e a linha entre elas é a linha que um atacante precisaria cruzar, desenhada de forma que não há cruzamento a fazer.
Como as máquinas concordam sem ninguém dar ordens
Aqui está a pergunta que soa como um muro: se nenhuma máquina pode comandar outra, como duas máquinas evitam fazer a mesma tarefa ao mesmo tempo? Uma frota que não pode receber ordens soa como uma frota que tropeça em si mesma.
A resposta ingênua busca direto de volta um chefe. Deixe o coordenador atribuir cada tarefa a exatamente um worker. E lá está de novo, atribuir é comandar, e comandar é abrir a porta que acabamos de nos recusar a abrir. O instinto de adicionar um dispatcher é o instinto que reconstrói a superfície de remote code execution uma conveniência de cada vez. Resista a ele, e você tem que achar outra forma de as máquinas concordarem.
A outra forma é uma reivindicação, não uma atribuição. A camada compartilhada segura a lista de trabalho disponível. Uma máquina que quer uma tarefa não espera ser avisada, ela estica a mão para a tarefa e tenta reivindicá-la. A reivindicação é atômica: o store compartilhado deixa exatamente uma máquina ganhar. Dois supervisores agarram a mesma tarefa no mesmo instante, um pega, e o outro é simplesmente avisado tomada e segue para a próxima.
Leia isso com cuidado, porque a direção do controle inverteu. Ninguém empurrou trabalho para uma máquina. A máquina puxou trabalho para si mesma, e a única coisa que a rede fez foi atuar como árbitro de quem chegou primeiro. A coordenação aconteceu, exatamente um worker roda a tarefa, e nem uma ordem foi dada. A camada compartilhada nunca disse “você, faça isto”. Ela só disse “aquela ali está tomada”. Pull, não push, é o que deixa a execução ficar local enquanto a coordenação fica em rede.
É também por isso que os modos de falha são suaves. Uma máquina que escurece não encalha uma fila, porque ela nunca estava segurando uma ordem que agora não pode seguir, ela simplesmente para de reivindicar, e suas reivindicações não renovadas se liberam para quem ainda estiver acordado. Não há coordenador para falhar, porque não há coordenador. A frota degrada um worker de cada vez em vez de tudo de uma vez atrás de um único cérebro.
Por que “só travar tudo” é a correção errada
O reflexo de todo engenheiro com mentalidade de segurança que ouve “sem endpoint de execução” é argumentar o outro lado. Mantenha o dispatcher, só o proteja direito. Auth forte, tokens com scope, uma allowlist, rate limits, um audit log. Poderíamos fazer tudo isso. É boa prática. É também a altitude errada para este problema.
Aqui está a distinção que importa. Proteger o endpoint que dá ordens é uma aposta de que seus controles nunca falham, de que nenhum token vaza, nenhum agent é jamais socialmente engenheirado para chamar a rota errada, nenhuma dependência entrega uma falha, nenhum colega futuro silenciosamente alarga um scope para entregar uma feature numa sexta. Remover o endpoint é uma aposta de que você consegue viver sem a capacidade. A primeira aposta você tem que ganhar todo santo dia, para sempre, em todos que algum dia tocarão o sistema. A segunda aposta você ganha uma vez, na arquitetura, e ela fica ganha.
Há um ditado em segurança de que o único código verdadeiramente seguro é o código que você não escreveu. A versão do control plane é mais afiada: o único endpoint que não pode ser explorado é o que não existe. Uma porta defendida por um guarda é só tão segura quanto o pior dia do guarda. Um muro sem porta não tem pior dia.
É por isso que tratamos “poderíamos adicionar um endpoint de dispatch por conveniência?” como uma pergunta com resposta permanente. Não não por enquanto. Não, estruturalmente, do jeito que uma parede estrutural não é um lugar onde você coloca uma janela porque a vista seria boa. A conveniência que ela compraria é real. A capacidade que ela entregaria a qualquer um que algum dia comprometesse uma credencial é a frota inteira, e essa troca não é nem perto.
A virada: o poder mais seguro é o que você escolheu não ter
Você geralmente dá para perceber quanto alguém já rodou infraestrutura real pelo quão nervoso fica perto dos próprios botões de modo-deus.
O instinto júnior é construir a alavanca poderosa e se orgulhar dela, olha quanto este endpoint consegue fazer. O instinto que vem de ter sido paginado às 3 da manhã porque uma alavanca poderosa foi puxada pela mão errada é o oposto. Você começa a ver cada capacidade que você tem como uma capacidade que alguém eventualmente pode tirar de você. E a forma mais durável de garantir que um poder nunca seja abusado é não tê-lo em primeiro lugar, não para você, não para o seu futuro eu, não para o atacante vestindo as credenciais do seu futuro eu.
Essa é a disciplina sob o endpoint ausente. Não é que não confiamos nos nossos próprios controles; é que nos recusamos a fazer a frota inteira depender de eles serem perfeitos para sempre. Abrimos mão do botão central satisfatório para que nenhuma chave roubada jamais destranque o prédio. Um sistema que você não consegue comandar totalmente de fora é um sistema que não pode ser totalmente comandado contra você de fora, e numa plataforma feita para rodar as operações reais de uma empresa real, com agents tocando trabalho real, essa troca é a única que deixa você dormir.
A parte difícil disso nunca foi o código. A reivindicação atômica são poucas linhas. A parte difícil foi a contenção, olhar para um poder que teria deixado uma dúzia de coisas mais fáceis e decidir, de propósito, nunca construí-lo.
Essa é a linha que mantemos na Apollo Space: a execução fica na máquina que a possui, a coordenação fica uma coisa que as máquinas observam em vez de obedecer, e o botão mais perigoso no painel é o que nunca conectamos. Se você já encarou uma ferramenta interna que poderia fazer qualquer coisa e sentiu o estômago afundar um pouco, você já entende por que o control plane mais seguro é o que educadamente se recusa a receber ordens.
A Apollo cuida da operação repetitiva da sua empresa pro seu time não precisar.
Entre na lista de espera: acesso antecipado, preço de usuário fundador e um lugar na primeira fila enquanto a gente constrói.
Entrar na lista de esperaO imposto oculto dos agents em paralelo é um diamante de migrations
Seis agents escrevendo para um schema conflitam no banco de dados, não no código, e a CI morre em "multiple heads".
EngenhariaUm orchestrator que não sobrevive ao próprio crash não é um
Um crash que apaga o raciocínio do orchestrator perde a única coisa que você não consegue reconstruir.
EngenhariaColoque um portão determinístico na frente do seu revisor mais esperto
A pega-defeito mais barata é um script burro que checa se duas branches mergeadas ainda sobem antes de qualquer julgamento.