Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

crab-shell-proxy

Esta página descreve o orquestrador como componente: do que ele é dono, do que ele deliberadamente não é, e como o código dele está organizado. Leia antes de abrir o repositório pela primeira vez.

O que é

crab-shell-proxy é um pequeno serviço em Go que fica entre o gateway Mycelium e os containers dos agentes. Ele lê para qual agente é a requisição e qual membro a fez, garante que o container daquele membro está rodando, e repassa a conversa para ele. O repositório é crab/crab-shell-proxy, o módulo Go dele é github.com/LepistaBioinformatics/crab-shell-proxy, e o serviço no compose tem o mesmo nome do diretório.

É o componente que segura o socket do Docker, e ele roda como root. O estágio de runtime do Dockerfile diz o porquê com todas as letras: ele precisa alcançar o socket (root:docker, modo 0660), ler arquivos de template pertencentes ao root, e escrever diretórios de dados por usuário. Todo o resto da stack está arranjado para não precisar desses privilégios, porque este componente já os tem. A nota de segurança do README diz sem rodeios: o proxy é o plano de controle confiável, e os agentes que ele cria são a parte sem root, em sandbox.

Um harness é o programa dentro do container de um agente que de fato conversa com o modelo e roda ferramentas. O proxy orquestra harnesses; ele não é um. Veja Harnesses.

Pelo que ele é responsável

Resolver identidade em container. O gateway verifica o token de quem chama e injeta um cabeçalho de perfil. O proxy tira o agente do nome de serviço injetado e o membro do accId do perfil, e garante que existe um container e um diretório para a tupla (tenant, subscription, agent, user) resultante. Usa-se o id da conta em vez do e-mail porque um e-mail pode mudar; o e-mail fica só como marcador legível em .crab-owner.json.

Ciclo de vida. Um agente é declarado como scale-to-zero, em que o container sobe do zero na primeira requisição do membro e é parado depois de uma janela de ociosidade, ou como continuous, em que ele nunca é parado automaticamente. Os dois são configurados por agente no config.yaml.

Escolher o harness. Cada agente declara qual runtime responde por ele:

agents:
  alpha:
    serviceName: "alpha"
    harness: "ganglion"
    template: "alpha"
    mode: "scale-to-zero"
    idleTimeout: 30s

internal/pico roda um turno contra o picoclaw pelo protocolo WebSocket dele; internal/ganglion roda um contra o ganglion por HTTP com SSE. Qual dos dois é usado é decidido por essa única chave. Um agente que não declara a chave harness: recebe o ganglion: o carregador de configuração em internal/config/config.go preenche um harness: vazio com DefaultHarness antes de validar qualquer coisa, e requireHarnessFeature em internal/httpapi/harness_gate.go lê a mesma constante. Ainda assim, declare a chave explicitamente em todo agente que você escrever — um agente ganglion sem imagem é desabilitado em vez de iniciado, então herdar o padrão em um host despreparado tira esse agente de serviço.

Dizer a verdade sobre o que um harness não consegue fazer. harness_gate.go mantém uma tabela de recursos que não são universais, e um recurso que o harness do agente não consegue atender responde 501 nomeando o harness, em vez de fingir sucesso. O arquivo registra o incidente que gerou a regra: um harness certa vez aceitou uma criação de projeto que ele não implementava, então um projeto podia ser criado, guardado e listado sem mudar nada no agente que respondia.

Tudo que é feito com um container. Provisionamento e posse de volumes, materialização de segredos, o registro de modelos, o servidor MCP do grafo de memória, as tarefas agendadas e a API de administração vivem todos aqui. A especificação do harness coloca isso como uma fronteira permanente: um harness dentro de um container não tem nada que iniciar, parar ou provisionar coisa alguma, nem a si mesmo.

A superfície HTTP. A parte voltada ao membro tem o formato da OpenAI — POST /v1/chat/completions, GET /v1/models, GET /v1/sessions/history — com GET /healthz para liveness e GET /doc/openapi.json para o documento OpenAPI embutido no binário. A administração fica em /v1/admin/..., e GET /v1/instances é um inventário somente leitura das instâncias em execução, que existe para que nada mais na stack precise perguntar ao próprio Docker.

Pelo que ele não é responsável

Ele não autentica ninguém. A identidade chega já verificada do gateway, e o trabalho do proxy é confiar nesse cabeçalho, não repetir a verificação.

Ele não roda o laço do agente. Decidir qual ferramenta chamar, quando parar e qual é a resposta pertence ao harness.

Ele não desenha nada. A interface voltada ao membro é o crab-exoskeleton-webapp, que chega ao proxy através do gateway e nunca fala direto com um container de agente.

Ele não coleta métricas sobre a stack. Isso é o harness-sphere, e a divisão sustenta peso: como o proxy já segura um socket do Docker, nada mais na stack ganha um.

Como é construído e testado

O build é o portão de testes. O estágio de build do Dockerfile roda go vet ./... e go test ./... antes de linkar o binário, então um teste que falha significa que nenhuma imagem é produzida e, portanto, nada é publicado. release-image.yml é o único workflow do repositório; ele constrói e publica essa imagem em um push para main ou em uma tag de versão. Não há um workflow separado de pull request, o que significa que as verificações que um contribuidor roda localmente são as mesmas que barram a imagem:

go vet ./... && go test ./...

Uma segunda suíte conversa com um daemon Docker de verdade e fica atrás de uma build tag, então ela não roda no comando acima e também não roda no build da imagem:

go test -tags integration ./internal/docker -run TestIntegration -v

config.yaml é embutido na imagem em /etc/crab-shell-proxy/config.yaml e guarda nomes de variáveis de ambiente, não valores, de modo que uma mesma imagem continua utilizável em vários deploys. Veja Configuração.

Como o código está organizado

cmd/crab-shell-proxy/main.go é o ponto de entrada; todo o resto está em internal/. Os pacotes que vale conhecer antes de começar a ler:

PacoteO que vive ali
configo catálogo de agentes, os padrões e a validação, e os helpers de caminho para o diretório de um membro
httpapitoda rota, incluindo o portão de recursos do harness
dockero cliente da API do Docker Engine escrito à mão e tudo que é feito com um container ou com o volume dele
picorodar um turno contra o picoclaw
ganglionrodar um turno contra o crab-ganglion-harness
registryos modelos, a cascata deles, e quem pode usar qual
historyler as transcrições de volta do diretório de um membro
memgraph, mcpserver, mcptokeno grafo de memória e o endpoint MCP pelo qual os agentes chegam nele
cron, projects, restart, authz, identity, turntarefas agendadas, projetos, controle de restart, autorização, o cabeçalho de perfil, e os tipos de turno compartilhados

internal/docker é, com larga margem, o maior pacote, o que é um bom sinal de onde está o trabalho: a maior parte do que este serviço faz é manipulação cuidadosa de sistema de arquivos e de containers em nome de alguém cuja requisição ele já decidiu confiar.

Para onde ir agora

Harnesses explica os dois runtimes e como um deles é escolhido. Agentes, workspaces e projetos descreve o layout de diretórios que este serviço lê e escreve. Se você está prestes a mexer no código, Trabalhando na stack tem os comandos de build e de teste de cada repositório em um só lugar.