10/Documentation
O protocolo de rede durable (experimental)
O que trafega entre o Collie e um durable agent, para desenvolvedores: as rotas, hello e a época, a vida útil de uma solicitação e os IDs de requisição, os transportes por socket e token, os limites e a regra de versão, com um link para WIRE.md para ver as regras exatas
Experimental. O protocolo pode mudar ou o recurso pode ser removido em uma versão minor posterior. Versões e a saudação de identificação explica como uma alteração chega até você.
O protocolo durável é a forma como o Collie se comunica com um agente durável. Esta página explica seu funcionamento para quem cria um agente durável sem o SDK ou deseja saber o que o SDK faz. As regras exatas estão em WIRE.md e, onde esta página e esse arquivo divergirem, WIRE.md está correto. O SDK durável implementa tudo isso para você.
O que trafega
O Collie disca e o agente durável escuta, e cada troca é uma requisição HTTP com corpo JSON.
O agente durável escuta em um socket Unix privado, ou atrás de TLS em uma URL (Transportes). O Collie faz polling nele: não há push, nem WebSocket e nem stream. Cada caminho começa com /session/v1, que é um nome fixo, não a versão. Todas as chamadas, exceto a primeira hello, carregam a versão no cabeçalho X-Collie-Session. O corpo da requisição é um objeto JSON de no máximo 64 KiB, e um corpo POST vazio é interpretado como {}.
| método e caminho | o que faz | limite de resposta |
|---|---|---|
GET /hello | Nome, intervalo de versões, época, prontidão e recursos do agente durável | 16 KiB |
GET /snapshot | O layout: workspaces, abas, painéis com seus estados, turnos e solicitações abertas, e inicializadores. Carrega uma ETag e responde 304 quando nada foi alterado | 4 MiB |
GET /pane/:id/events?after=N&epoch=E | Eventos do painel após um cursor | 8 MiB |
POST /pane/:id/answer | Uma resposta para uma pergunta | 16 KiB |
POST /pane/:id/send | Texto para o painel, para direcionar o turno atual ou aguardar o próximo | 16 KiB |
POST /pane/:id/abort | Interromper o turno atual | 16 KiB |
POST /pane/:id/action | Um clique em um dos botões da faixa do painel | 16 KiB |
POST /rename | Um novo título para um workspace ou uma aba | 16 KiB |
POST /close | Fechar um painel, uma aba ou uma workspace | 16 KiB |
POST /starter/:id | Executar um inicializador. A resposta nomeia o novo painel | 16 KiB |
GET /conversations?after=C&limit=N | Conversas do agente durável, abertas e fechadas, as mais recentes primeiro | 1 MiB |
GET /conversation/:id/items?after=C&limit=N | Itens de uma conversa, os mais antigos primeiro | 8 MiB |
POST /reopen | Exibir uma conversa fechada novamente | 16 KiB |
Todo erro é um corpo JSON com um code, como 409 stale ou 404 no-such-pane. Qualquer outro caminho responde 404 not-found. Uma resposta acima do seu limite não é lida, e um POST com resposta truncada tem resultado desconhecido (IDs de requisição).
Hello e época
hello informa quem é o agente durável, e a época informa com qual inicialização dele o Collie está se comunicando.
campo de hello | o que ela contém |
|---|---|
service | Exatamente collie-durable. Sem isso, o Collie interpreta o agente durável como outro servidor e não faz outras chamadas |
protocol | O intervalo de versões de comunicação que o agente durável suporta, { min, max }. Hoje { min: 1, max: 1 } |
host.name | O nome do próprio agente durável, apenas texto para exibição, no máximo 64 bytes |
epoch | Novo e aleatório a cada inicialização do agente durável, pelo menos 128 bits, nunca reutilizado |
ready | False enquanto o agente durável organiza seus painéis após uma inicialização |
caps | O que o agente durável pode fazer, cada um como booleano. Uma funcionalidade ausente é false |
O Collie primeiro chama hello sem o cabeçalho de versão, escolhe a versão mais alta aceita por ambos os lados e a envia em todas as chamadas posteriores. Enquanto ready for false, todas as rotas exceto hello respondem 503 starting, e o celular mostra o agente durável como iniciando. Um agente durável define ready somente depois que todas as conversas forem exibidas novamente e todas as solicitações abertas forem feitas de novo, de modo que um reinício nunca mostre um layout pela metade.
O ETag do snapshot é "<epoch>-<rev>". rev aumenta a cada alteração visível para o leitor, portanto um 304 quase não consome recursos do agente durável. Uma nova época indica ao Collie que o agente durável reiniciou. O Collie continua exibindo os painéis que já tem até ler o novo snapshot. Em seguida, ele lê cada painel com o cursor antigo e obtém a visualização atual completa do painel em uma única resposta.
Uma leitura com falha após uma bem-sucedida mantém o último snapshot para aquela sondagem. Duas falhas seguidas mostram o agente durável como inacessível. Um caminho de socket que falhe na verificação de permissões fica inacessível imediatamente.
Perguntas e respostas
Uma pergunta é um card de dúvida, e fica aberta a partir do evento ask do agente durável até o seu ask-closed.
| estado | quem define | o que o telefone exibe |
|---|---|---|
| Aberto | O agente durável faz a pergunta. O painel exibe waiting enquanto houver perguntas abertas | Um card em "Precisa da sua resposta", com o nome do agente durável e o tempo restante quando a pergunta tiver expiresAt |
| Alterado | O agente durável altera a pergunta no local, e seu revision aumenta | O card é redesenhado. Um toque no formulário antigo recebe 409 stale, e o card diz "Esta pergunta mudou. Leia novamente e toque em uma resposta." |
| Respondido | O celular responde, e o agente durável encerra a pergunta com by: "phone" | O card desaparece, e a ferramenta é executada ou negada conforme a opção indicar |
| Decidido no agente durável | O agente durável encerra a pergunta por conta própria, decidedBy: "program". Uma interrupção do turno é contabilizada | Uma linha no Chat que começa com "Decidido no agente durável", com o título da pergunta |
| Tempo esgotado | O tempo ultrapassa expiresAt, decidedBy: "timeout" | Uma linha no Chat que começa com "Tempo esgotado antes de uma resposta", com o título da pergunta |
Um toque que chega após a decisão recebe 409 decided com o primeiro resultado, e nada é executado. O card então indica quem decidiu primeiro: "Decidido no agente durável.", "Tempo esgotado antes de uma resposta." ou, após outro toque, "Já respondido. Este card sumirá na próxima atualização.". Uma pergunta que o agente durável não conhece mais responde 200 too-late. O card diz então "Não processado. A pergunta pode reaparecer em instantes.", porque após um reinício o agente durável pode fazê-la novamente sob o mesmo id.
| regra | por que |
|---|---|
| O id da solicitação é um hash do id do painel e do id da chamada de ferramenta | A mesma chamada de ferramenta feita após um reinício tem o mesmo id, permitindo que uma resposta enviada durante o reinício seja entregue |
Um card permission é gerado a partir do nome da ferramenta e de seus argumentos, nunca das palavras do modelo | Um modelo que busca aprovação descreveria a chamada de forma favorável |
Um agente durável protegido por TLS não deve oferecer allow-always | O token é o único controle de acesso, portanto uma única resposta de quem o possuir liberaria todas as chamadas seguintes |
O celular não oferece allow-always em um card cujas palavras foram cortadas ou limpas, ou que não nomeia nenhuma ferramenta | A pessoa não consegue ver o que a concessão cobre |
expiresAt é ISO 8601 com fuso horário | Após esse prazo, o pedido é decidido por timeout, e um snapshot não o lista mais como aberto |
| O durable agent mantém o resultado de pelo menos seus últimos 500 pedidos decididos por painel | Um toque atrasado recebe decided e o primeiro resultado, não um too-late simples |
Um pedido é de um de quatro tipos: permission, choice, text ou form. Cada um tem seu próprio formato de resposta, listado com o Perguntas do SDK.
IDs de requisição
Cada ação do celular tem um request id, portanto é executada no máximo uma vez.
O celular gera um request id por intenção, e o Collie grava seu próprio recibo antes de repassar o id. O durable agent mantém pelo menos os últimos 500 ids de sua época. Uma repetição de um id finalizado recebe a primeira resposta novamente com replayed: true e não executa nada. O mesmo id com outro payload recebe 409 request-id-reused.
O Collie nunca repete um POST por conta própria. Um POST que expira, perde a conexão ou recebe um 5xx tem resultado desconhecido. Para uma resposta, o card diz então "Não há certeza de que a resposta chegou. Tentar novamente envia mais uma vez.", e Tentar novamente envia o mesmo request id, para que a memória do durable agent responda a repetição.
Uma nova tentativa que encontra o pedido fechado ou a vez alterada não consegue saber se a primeira tentativa teve efeito. O Collie então responde ao celular "talvez aplicado", o card diz "Pode já ter sido aplicado. Verifique o chat.", e a ação nunca é enviada uma terceira vez.
Transportes
Um durable agent usa um de dois transportes: um socket privado na máquina do Collie ou TLS com um token.
Os bytes são os mesmos em ambos: as rotas, os corpos, o cabeçalho de versão, o ETag e os códigos de erro. Apenas quem pode alcançar o durable agent difere.
| pelo socket | a regra |
|---|---|
| Caminho | Absoluto, no máximo 103 bytes. Ambos os lados o leem de COLLIE_DURABLE_SOCKET |
| Modos | O socket tem modo 0600, em uma pasta com modo 0700. O durable agent remove um arquivo de socket obsoleto antes de escutar |
| A verificação do caminho | Antes de cada conexão, o Collie verifica cada parte do caminho com lstat. Nenhuma parte é um link simbólico. A pasta do socket é sua com modo 0700. Nenhuma pasta acima dela tem permissão de escrita para grupo ou outros, exceto uma com sticky bit como /tmp. O socket é seu, sem permissão para grupo ou outros |
| Uma verificação com falha | O Collie não conecta e mostra o durable agent como inacessível com socket-not-private e a parte que falhou |
| Token | Nenhuma. O socket privado é o controle de acesso |
| Quem pode agir | Qualquer processo do seu usuário. O durable agent pode recusar seus próprios descendentes com 403 caller-refused |
| Timeouts | 1000 ms para hello, snapshot e events. 3000 ms para answer, send, stop, action, rename ou close. 30000 ms para um starter |
| Com que frequência o Collie consulta | A cada 1 s enquanto um celular monitora, caso contrário a cada 3 s. Um durable agent fora do ar é consultado uma vez por verificação |
| Sistemas | Linux e macOS. O Windows não tem transporte por socket |
| via TLS | a regra |
|---|---|
| URL | https://, ou http://127.0.0.1:<port> ou http://localhost:<port> para um teste local. No máximo 2048 bytes, sem nome de usuário ou senha, sem query, sem fragmento. O Collie adiciona /session/v1 a isso |
| Token | Pelo menos 32 bytes, ASCII imprimível sem espaços. O Collie envia Authorization: Bearer <token> em cada requisição, hello incluído |
| A verificação do agente durável | O token primeiro, antes da versão, hello e ready, comparado em tempo constante. Um token ausente ou incorreto recebe um 401 { "code": "unauthorized" } simples e nada mais |
| Redirecionamentos | O Collie não segue nenhum. Um 3xx é repassado como status, para que o token nunca chegue a outro host |
| ETag | Um proxy que compacta uma resposta pode enfraquecer a tag para W/"<epoch>-<rev>". Ambos os lados a comparam com o W/ removido |
| Uma recusa | Um 401 ou 403 mostra o agente durável como inacessível com token-refused |
| Timeouts | 5000 ms para cada leitura e gravação, pois um agente durável que dormiu pode acordar frio. 30000 ms para um starter |
| Com que frequência o Collie consulta | A cada 2 s enquanto um telefone monitora, caso contrário a cada 10 s. Um agente durável inativo ou recusado é consultado no máximo uma vez a cada 10 s |
Cada tempo limite é um prazo para toda a troca, da conexão até o último byte. Via TLS, cada sondagem é uma requisição que seu host pode cobrar, inclusive um 304.
Limites
O Collie corta ou recusa qualquer coisa além desses limites, portanto um agente durável que permaneça dentro deles não perde nada.
| o que | limite |
|---|---|
| Workspaces por snapshot | 64 |
| Abas por snapshot | 256 |
| Painéis por snapshot | 256 |
| Perguntas abertas por painel | 50, as mais antigas primeiro |
Opções de um permission | 6 |
Opções de um choice e de um campo de escolha | 8 |
Campos de um form | 6 |
| Starters por snapshot | 50 |
| Botões da barra por painel | 4, um rótulo de 16 caracteres |
| Um id | 1 a 128 bytes, sem caractere de controle, sem espaço em branco, nenhum entre /, ? ou # |
| Um corpo de requisição | 64 KiB, caso contrário 413 too-large |
| Respostas | hello 16 KiB, snapshot 4 MiB, events 8 MiB, conversations 1 MiB, items 8 MiB, um POST 16 KiB |
| Eventos por resposta | 1000. Um leitor que ficou para trás recebe a visualização atual, no máximo os 2000 itens mais recentes |
Texto de um item user, reply, thinking ou notice | 64 KiB |
Texto de um item markdown ou code | 32 KiB |
| Texto que o telefone envia | 32 KiB |
Um item link | Apenas https://, no máximo 2048 caracteres |
| Uma página de histórico | 100 conversas, ou 1000 itens |
| Um cursor de histórico | 512 bytes de ASCII imprimível, sem espaço |
Uma string acima do limite é truncada, e seus caracteres de controle e invisíveis são removidos. Uma lista acima do limite mantém suas primeiras entradas. Uma página de histórico acima do limite não é lida.
Versões e a saudação de identificação
Hoje existe uma versão de protocolo, 1, e o Collie fala 1..1.
Quando a versão 2 for lançada, o Collie aceitará a versão 1 por uma versão minor seguinte, e a minor posterior a essa deixará de aceitá-la. Um leitor ignora um campo, um tipo de evento, um tipo de item ou um código que não reconhece. Um campo adicionado dentro da versão 1, como expiresAt ou decidedBy, é opcional. Um novo campo obrigatório, um novo significado para um campo antigo ou um campo removido exige uma nova versão.
Um ask de um tipo que o telefone não conhece aparece como um que ele não pode responder a partir do telefone, sem botões.
Quando os dois intervalos não se encontram, o Collie não lê mais nada do durable agent. Uma vez por época do durable agent, ele envia mais um hello, desta vez com sua própria versão, para que o durable agent possa registrar o desencontro do seu lado. O durable agent responde 409 incompatible com um refusal que lista ambos os intervalos. Esse é o telling hello.
O Collie grava uma linha no seu log para cada intervalo que recusa, e collie doctor mostra ambos os intervalos. A tela inicial do durable agent no telefone exibe a mesma frase, por exemplo "The durable agent speaks wire v2, this Collie speaks v1. Update Collie, or the agent's SDK." Atualize o lado mais antigo.
Onde estão as regras exatas
WIRE.md é o contrato, com cada formato, código e regra.
As decisões por trás dele são registros no repositório:
| registro | o que ele decide |
|---|---|
| ADR 0099 | Um agente durável é um multiplexador sem tela |
| ADR 0100 | Uma solicitação viaja como dados e é respondida por id |
| ADR 0101 | Send e stop nomeiam o turno que o telefone viu |
| ADR 0102 | Starters são linhas na página New |
| ADR 0103 | Os dois transportes |
| ADR 0104 | Um agente durável é uma sessão, e a saída |
| ADR 0105 | O SDK é um pacote npm |