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

Configuração

Quatro arquivos decidem como esta stack se comporta. Este capítulo diz qual deles é dono do quê, percorre o catálogo de agentes em detalhe — porque é ali que você define um agente — e lista as variáveis de ambiente que vale conhecer.

As quatro superfícies

ArquivoÉ dono deComo chega até a stack em execução
.env na raiz do repositóriosegredos, portas publicadas, referências de imagemlido pelo compose
crab/crab-shell-proxy/config.yamlo catálogo de agentes: quais agentes existem, seu harness, modelo e ciclo de vidacopiado para a imagem do proxy no momento do build
deploy/<mode>/config.*.tomlo gateway: quais rotas existem, qual papel protege cada uma, qual token é injetadomontado por bind no gateway
docker-compose*.yamlquais serviços rodam, o que eles montam, que ambiente recebemo próprio comando compose

A única coisa a internalizar: o catálogo de agentes é embutido na imagem do proxy, então adicionar ou remover um agente significa reconstruir o proxy. A configuração do gateway é montada, então uma mudança ali precisa apenas de um restart daquele serviço. Os dois têm que concordar — um agente que existe em um e não no outro é ou uma rota anunciada sem nada atrás, ou um agente que ninguém consegue alcançar.

O catálogo de agentes: config.yaml

Este é o arquivo que você edita para definir um agente. Todo o resto dele tem um padrão que funciona; o mapa agents: não.

Aqui está um agente completo, com as linhas que importam:

agents:
  alpha:
    serviceName: "alpha"                       # must match the gateway's service key
    harness: "ganglion"                        # which runtime answers
    token: { env: "MYC_PICOCLAW_ALPHA_TOKEN" } # read from the environment, never inline
    template: "alpha"                          # <dataRoot>/templates/alpha
    mode: "scale-to-zero"                      # or "continuous"
    idleTimeout: 30s
    model:
      provider: "deepseek"
      name: "deepseek-chat"
      apiKeyEnv: "PICOCLAW_ALPHA_API_KEY"      # the key lives in the environment

serviceName é o valor que o Mycelium injeta como x-mycelium-service-name quando encaminha uma requisição, e é a única coisa que diz ao proxy qual agente foi endereçado. O Mycelium pega o primeiro segmento de caminho da URL que chega como nome do serviço e o remove antes de encaminhar, então um membro que chama /alpha/v1/... alcança o agente cujo serviceName é alpha. Uma requisição que carrega um nome de serviço que nenhum agente reivindica é respondida com 404 e uma mensagem dizendo para você passar pelo gateway.

harness seleciona o runtime. Os dois valores aceitos são "ganglion" e "picoclaw"; qualquer outro faz o carregamento falhar com uma mensagem que nomeia os dois. Um agente que não declara harness nenhum recebe o ganglion, que é o que diz config.DefaultHarness. Declare o harness explicitamente em todo agente mesmo assim, como faz todo agente no catálogo deste repositório: um runtime não é algo que uma configuração deva escolher por omissão, e uma omissão aqui não degrada com elegância. Um agente ganglion sem imagem é desabilitado em vez de iniciado, então um agente que herda o padrão num host sem CRAB_GANGLION_IMAGE para de responder em vez de rodar outra coisa em silêncio. O log de boot diz isso, e nomeia as duas saídas. Veja harnesses para saber como os dois runtimes diferem.

token mostra o padrão que este arquivo usa para todo segredo: { env: "NAME" } lê o valor do ambiente do proxy no momento do carregamento, então nada confidencial é escrito aqui ou embutido na imagem. Uma string simples também é aceita, e é a escolha errada fora de um teste.

template nomeia um subdiretório de <dataRoot>/templates/. Para um agente picoclaw o proxy cria um template ausente a partir de um padrão compilado no binário. Um agente ganglion não tem template em disco: ele é configurado pelo ambiente e por arquivos escritos por usuário.

mode e idleTimeout são o ciclo de vida. scale-to-zero para o container depois da janela de ociosidade e o inicia de novo na próxima mensagem, com tudo preservado. continuous nunca o para, que é o que os conectores nativos do picoclaw precisam — eles discam para fora de dentro do container, então o proxy não enxerga essa atividade para manter o agente vivo. Para um agente ganglion não existe essa porta lateral e o modo é uma simples decisão de custo. idleTimeout precisa ser maior que zero quando o modo é scale-to-zero; nos outros casos é ignorado.

model fixa o provedor e o nome do modelo e, o que é crucial, nomeia a variável de ambiente que guarda a chave em vez da chave em si. Cada agente tem a sua, então dois agentes podem usar provedores diferentes e credenciais independentes. Um baseUrl opcional sobrescreve o endpoint que o proxy resolveria a partir do provedor — o que você quer quando roda um gateway ou um endpoint regional na frente do provedor.

O que acontece quando um agente ganglion não está totalmente configurado

Este é o comportamento com maior chance de surpreender você, e ele é deliberado.

Um agente ganglion se remove do catálogo, no boot, quando este ambiente não consegue rodá-lo: sem referência de imagem, sem token, ou com um valor vazio atrás do seu model.apiKeyEnv. Isso não derruba o proxy, e não é silencioso — o log de boot traz uma linha nomeando o agente e a configuração que falta, e as rotas do agente passam a responder 404:

agent "alpha" disabled: PICOCLAW_ALPHA_API_KEY is unset (the agent's model apiKeyEnv) — its routes will answer 404

O raciocínio é que um arquivo de configuração deve conseguir descrever vários deploys. Um agente ganglion que chega a um host sem chave para ele degrada para “esse agente não existe” em vez de “o proxy não vai subir”, o que derrubaria todos os outros agentes junto.

Um agente picoclaw se comporta de outro jeito de propósito: um token que ele não consegue resolver é fatal, porque descartá-lo em silêncio removeria o acesso de um membro sem nenhum sinal além de uma linha de log que ninguém lê até ficar trancado do lado de fora. A chave de modelo dele, em contraste, pode estar vazia — ela é escrita na configuração do próprio membro no momento do provisionamento e aparece como um erro de autenticação na primeira mensagem.

O resto do config.yaml tem forma de máquina e em geral é fornecido pelo compose. Os valores que vale conhecer:

  • hostDataRoot é o caminho absoluto no host da árvore de dados. O proxy o entrega ao daemon do Docker como origem do bind-mount dos containers que cria, então um caminho que só existe dentro do proxy não vai resolver. É isso que CRAB_HOST_DATA_ROOT sobrescreve.
  • containerDataRoot é onde essa mesma árvore é montada dentro do proxy. O padrão é /data.
  • network é a rede Docker em que os containers criados entram. O arquivo compose fixa o nome real da rede em zombie_net para que ele fique estável independentemente do nome do projeto compose.
  • startupDeadline (padrão 35 segundos) limita uma partida a frio. turnIdleTimeout (padrão 120 segundos, definido como 600 no arquivo que vem pronto) limita quanto tempo o harness pode ficar em silêncio — não quanto tempo um turno pode levar. Um turno longo que usa ferramentas narra o tempo todo e o reinicia a cada frame.
  • containerPrefix (padrão crabshell) prefixa todo container que o proxy gerencia. O nome é <prefix>-<agent>-<hash>; o harness é registrado nas labels do container, não no nome dele.
  • mediaMaxBytes (padrão 10 MiB) é a única coisa contra a qual um upload é checado.

Vários campos podem ser sobrescritos pelo ambiente para que o arquivo versionado continue portátil: CRAB_HOST_DATA_ROOT, CRAB_CONTAINER_DATA_ROOT, CRAB_NETWORK, CRAB_LISTEN, CRAB_PICOCLAW_IMAGE, CRAB_PICOCLAW_USER, CRAB_PICOCLAW_HOME, CRAB_GANGLION_IMAGE, CRAB_MCP_BASE_URL e GANGLION_OTLP_ENDPOINT.

A configuração do gateway

deploy/standalone/config.standalone.toml e deploy/prod/config.base.toml configuram o Mycelium. Por agente, o formato é um bloco de serviço nomeando o downstream, um bloco de segredo guardando o token bearer, e um bloco de caminho por rota:

[[alpha]]
host = "crab-shell-proxy:8080"
healthCheckPath = "/healthz"

[[alpha.secret]]
name = "alpha-authorization-header"
authorizationHeader = { headerName = "Authorization", prefix = "Bearer", token = { env = "MYC_PICOCLAW_ALPHA_TOKEN" } }

[[alpha.path]]
group = { protectedByRoles = [{ name = "alpha", permission = "write" }] }
path = "/v1/chat/completions"
secretName = "alpha-authorization-header"
methods = ["POST"]

Três coisas decorrem desse bloco. A chave de serviço (alpha) é ao mesmo tempo o primeiro segmento de caminho que quem chama usa e o serviceName que o proxy compara. O papel nomeado em protectedByRoles é criado automaticamente no boot a partir dessa declaração — mas conceder o papel a uma conta é uma ação humana, e até lá toda requisição é recusada. E o token é resolvido do ambiente a cada requisição, então o arquivo versionado não guarda segredo nenhum.

Adicionar uma rota significa adicionar um bloco de caminho. Uma rota que o gateway não conhece é recusada antes de o proxy sequer vê-la, com uma mensagem sobre o caminho não corresponder a nenhum serviço — o que parece um bug de roteamento e é na verdade um bloco faltando.

Variáveis de ambiente

A lista completa e comentada é deploy/standalone/.env.example; copie esse arquivo em vez de escrever um do zero. Estas são aquelas cujo comportamento não é óbvio pelo nome:

VariávelEfeito
MYC_PICOCLAW_<AGENT>_TOKENO bearer que o gateway injeta e o proxy verifica, por agente.
PICOCLAW_<AGENT>_API_KEYA chave de LLM do próprio agente, referenciada pelo nome a partir do config.yaml.
MYC_STANDALONE_BOOTSTRAP_SECRETControla a reivindicação única da conta Staff. Vazio deixa esses endpoints respondendo 404.
CRAB_WEBHOOK_SECRETAutentica no proxy o webhook de conta criada do Mycelium.
CRAB_MCP_TOKEN_SECRETVazio desabilita o grafo de memória — o endpoint não é registrado e nenhum bloco de servidor é escrito em workspace nenhum. Nada avisa você; as telas de memória simplesmente ficam vazias.
CRAB_TELEMETRY_TOKENVazio significa que a rota de inventário de workspaces não é registrada de jeito nenhum — 404, não 401. Nunca reutilize um token de agente aqui: um token de agente libera conversar como qualquer membro, e um componente de monitoramento não pode ter um.
CRAB_GANGLION_IMAGEA imagem do ganglion. Ela não tem padrão no proxy, de propósito; o arquivo compose de desenvolvimento fornece uma que ele mesmo constrói.
CRAB_HOST_DATA_ROOTO caminho no host da árvore de dados. Precisa ser absoluto e precisa ser um caminho que o daemon do Docker consiga enxergar.
START_AT_SIGNINDefina como 1 e a landing page do cliente de chat nunca é servida: / vira a tela de login.
COMPOSE_FILEFaz de um overlay o padrão para todo comando compose. O overlay de observabilidade na prática exige isso — veja observabilidade.

Duas dessas merecem ser repetidas como regra, porque compartilham uma: um segredo não definido significa que a funcionalidade está ausente, não desprotegida. Um deploy que esqueceu um valor não ganha superfície nova, em vez de ganhar um endpoint atrás de uma guarda adivinhável.

Um workspace ganglion não tem diretório .secrets/. As credenciais chegam a esse harness como variáveis de ambiente no container, e é por isso que o proxy faz bind de cada workspace separadamente e de nada acima dele. Sob o picoclaw a visão mesclada de segredos é montada somente leitura em workspace/.secrets. Veja agentes e workspaces.

Para onde ir agora

Criando um agente customizado percorre de ponta a ponta a edição do catálogo e o bloco correspondente no gateway. Modelos e provedores cobre cadeias de modelos e sobrescritas por membro. Deploy cobre o que muda em produção.