MAOP — Protocolo de operações assíncronas sobre QUIC
O Meinicke Asynchronously Operation Protocol (MAOP) é uma camada de aplicação que organiza comunicações em operações e trata cada fluxo QUIC como um pipeline confiável de metadados e blocos. Esta revisão consolida o estado da versão 1, detalhando identificação, validação e governança de streams para orientar equipes que desejam interoperar.
As diretrizes foram calibradas em bancadas com latência sub-5 ms e cenários que sustentam dezenas de milhares de operações concorrentes. Cada nota técnica sinaliza impactos em armazenamento, telemetria e segurança para facilitar decisões arquiteturais.
Princípios de desenho
Separação explícita de metadados e conteúdo
As operações de Message e Request carregam apenas metadados, deixando a carga útil exclusivamente para Blocks após confirmação. Dessa forma é possível aplicar políticas de quota, autenticação e verificação antecipada antes de qualquer byte sensível chegar à aplicação.
Streams dedicados e pool global de controle
Streams QUIC bidirecionais são criados somente para mensagens aplicacionais; sinais de controle (Confirmações, Negativas e Desconexões) utilizam um conjunto global de streams unidirecionais, minimizando overhead de gerenciamento e evitando contenção.
O pool global pode ser limitado a 2–16 streams fixos sem afetar escalabilidade, porque cada operação carrega um vetor de stream_id. A multiplexação garante que confirmações milhares de operações circulem sem abrir novos streams.
Detalhes do protocolo
Identidade & ALPN
O MAOP opera sobre QUIC/TLS 1.3 e negocia via ALPN.
O identificador da versão major 1 é maop/1.
- Cliente: PRECISA oferecer
maop/1no ClientHello. - Servidor: PRECISA selecionar
maop/1; caso não reconheça, encerrar a associação.
Negociação de versão & capacidades
A versão do MAOP é negociada no fluxo de autenticação inicial por meio de campos de SEMVER (major.minor.patch) e um bitmap de capacidades. A regra é: major incompatível → negar; minor/patch menores → suportar com degradação graciosa.
- major: alterações incompatíveis; encerrar conexão imediatamente.
- minor: extensões compatíveis; ignore bits de capacidades desconhecidos (nunca erro).
- patch: bugfixes/clarificações; sempre compatível.
Identificadores & espaço de nomes
msg_id: u16 (0…65535); 0 reservado. Recomenda-se particionar faixas (p.ex., 1–10 000 padrão; 10 001–32 767 integrações; 32 768–65 535 experimentais).op_type: u8; 0x00–0x0A definidos; 0x0B–0xFF reservados a extensões.
Endianess & alinhamento lógico
Todos os campos numéricos do MAOP são codificados em ordem de bytes de rede (big-endian), conforme a convenção da IETF para protocolos binários sobre QUIC/TLS. Essa escolha garante interoperabilidade direta entre linguagens e arquiteturas heterogêneas (ARM, x86, RISC-V), sem exigir heurísticas de byte-swap.
Alinhamento lógico significa que os campos são organizados em múltiplos de 8 bytes apenas por convenção de ordenação e não por padding real no wire. Ou seja, não existem bytes de preenchimento transmitidos entre campos — o cabeçalho é sempre compacto e contínuo.
Caso futuras versões introduzam novos campos, esses serão adicionados ao final do cabeçalho atual,
preservando a ordem atual e mantendo o layout sem quebras (backward-compatible).
Essa política torna a decodificação determinística: qualquer implementação pode ler o cabeçalho diretamente da rede, byte a byte, sem depender de alinhamento estrutural da CPU. Além disso, impede divergências entre compiladores, linguagens e arquiteturas.
Políticas de segurança & privacidade
- O motivo em
Disconnect Requesté exclusivamente para diagnóstico e não devem conter dados sensíveis. - Aplicar quotas por
payload_lene rate limiting no pool global para mitigar DoS. - Autenticação obrigatória antes de aceitar qualquer operação aplicacional.
- Se
payload_len == 0xFFFFFFFFFFFFFFFFfor proibido por política, Responder comRefuse(0x04) usandoPOLICY_UNDEFINED_LENGTH.
Perfil mínimo de conformidade
- Suportar ALPN
maop/1e fluxo de autenticação inicial. - Implementar todas as operações e suas mecânicas.
- Registrar todas as mensagens padrão.
- Respeitar gate de envio: nunca enviar
Blockantes doProceed, por exemplo. - Descartar transferências com payload mas sem
Block Endapós tempo configurado. - Encerrar com
Disconnect Requeste/ouDisconnectem caso de versão major incompatível.
Parâmetros QUIC recomendados
initial_max_data≥ 10 MB;initial_max_stream_data_bidi≥ 1 MB.initial_max_streams_bidi≈ 100 (ajustar por SLA); bidirecionais para o pool global: 2–16.- Habilitar ECN e pacing do congestion control; medir RTT para janelas deslizantes de blocos.
Estrutura fundamental
Cabeçalho comum (mínimo)
[ op_type:1 ]
- op_type: código da operação (vide enum abaixo). Valores de 0x0C–0xFF permanecem reservados para extensões.
Os cabeçalhos são alinhados a 8 bytes para garantir leitura eficiente em plataformas little-endian e big-endian. É recomendado validar payload_len (se presente no cabeçalho) contra limites configuráveis para impedir alocação de buffers gigantes em ataques de negação de serviço.
O espaço de identificadores é suficiente para rastrear operações em clusters distribuídos, desde que cada nó mantenha cache consistente de stream_id ativos.
IDs de mensagem (msg_id) ocupam 16 bits: existem 65.536 combinações possíveis, sendo prática comum reservar 0 para controle interno e mapear faixas específicas (ex.: 2–10.000 para mensagens padrão, 10.001–32.767 para integrações externas, 32.768–65.535 para experimentos).
Enumeração de operações
| Código | Nome | Direção / Stream | Função |
|---|---|---|---|
0x00 |
Message | Abre stream QUIC unidirecional | Metadados de mensagem aplicacional; exige Proceed antes de enviar blocos. |
0x01 |
Request | Abre stream QUIC bidirecional | Similar a Message, adicionando response_msg_id e timeout_ms; espera resposta (Response ou Fail). |
0x02 |
Response | Stream bidirecional aberto por Request |
Mensagem de resposta sem encadeamento adicional; confirma processamento e encerra stream. |
0x03 |
Proceed | Pool Global | Aprovação em lote de stream_id(s) de Messages e/ou Requests — até milhares por operação usando os streams de controle. |
0x04 |
Refuse | Pool Global | Negativa em lote de stream_id(s) de Messages e/ou Requests — Com códigos padronizados. |
0x05 |
Block | Mesmo stream da mensagem | Chunk de dados enviado após aprovação — Contém o payload da operação Message, Request ou Response. |
0x06 |
Block End | Mesmo stream da mensagem | Marca final de transferência, enviado SEMPRE após o fim do envio dos Blocks. |
0x07 |
Fail | Pool Global | Falha ao executar Message ou Request; inclui código (u16) e motivo em UTF-8. |
0x08 |
Done | Pool Global | Usado para indicar que uma operação foi recebida e processada com sucesso; A única operação que não recebe um Done é o Request, que deve receber um Response. |
0x09 |
Disconnect Request | Pool global bidirecional | Sinaliza intenção de encerramento com janela de drenagem (drain_time_ms). |
0x0A |
Disconnect | Pool global bidirecional | Encerramento imediato com motivo opcional, sem dados sensíveis. |
Reserve intervalos adicionais de op_type para extensões futuras (e.g., compressão, priorização, replicação).
Fluxo de autenticação inicial
Antes de qualquer operação ser aceita, os peers executam uma troca de autenticação em um stream QUIC bidirecional dedicado apenas para isso.
Esse fluxo é o primeiro a ser estabelecido após o handshake do transporte, não consome stream_id e precisa ser concluído integralmente antes da abertura do primeiro stream aplicacional.
Enviado pelo cliente:
[ auth_type_len:1 ] [ auth_type_utf8:auth_type_len ] # "Bearer", "Basic", etc... [ token_len:2 ] [ token_bytes:token_len ] # até 65.535 bytes [ metadata_pairs:1 ] # quantidade de pares chave-valor repeat metadata_pairs times: [ key_len:2 ][ key_utf8:key_len ] # cada chave até 65.535 bytes [ value_len:2 ][ value_utf8:value_len ] # cada valor até 65.535 bytes [ version_len:1 ] # Tamanho da versão SEMVER [ version:version_len ] # SEMVER negociada [ vendor_len:1 ] [ vendor:vendor_len ] # Vendor da implementação do cliente
Valide tokens, pares chave-valor e a versão antes de responder com qualquer operação.
Em caso de falha, envie Disconnect com motivo sanitizado e encerre a associação QUIC.
- Após o término do envio, o cliente deve encerrar o output, ele não enviará mais dados.
- Após o término da leitura, o servidor deve encerrar o input, ele não irá ler mais dados.
- Metadados estruturados: utilize pares como
region=eu-centralourole=ingestpara aplicar políticas de aprovação, limites de throughput e roteamento. - Negociação de versão: a SEMVER permite rejeitar clientes antigos explicitamente. Se a combinação não for suportada, sinalize na resposta e registre auditoria antes de desconectar.
- O stream utilizado pela autenticação precisa bidirecional e o PRIMEIRO a ser aberto. Assim que a resposta da autenticação for recebida/enviada, a stream deve ser imediatamente fechado por ambas as partes.
Enviado pelo servidor, como resposta:
[ approved:1 ] # Booleano indicando se a conexão foi aprovada ou não if approved (==1): [ session_id:16 ] # aleatório; p/ correlação e auditoria [ identifier_len:1 ] # tamanho da string de identificação [ identifier:identifier_len ] # string de identificação else: [ error_code:2 ] # enum estável [ retry_after_ms:4 ] # Em millisegundos. opcional; se <= 0 não há um tempo para reconexão. [ sanitized_reason_len:2 ] [ sanitized_reason_utf8:reason_len ] # Razão da rejeição [ version_len:1 ] # Tamanho da versão SEMVER [ version:version_len ] # SEMVER negociada [ vendor_len:1 ] [ vendor:vendor_len ] # Vendor da implementação do servidor
Valide tokens, pares chave-valor e a versão antes de responder com qualquer operação.
Em caso de falha, envie Disconnect com motivo sanitizado e encerre a associação QUIC.
- Após o término do envio, o servidor deve encerrar o output, ele não enviará mais dados.
- Após o término da leitura, o cliente deve encerrar o input, ele não irá ler mais dados.
- Metadados estruturados: utilize pares como
region=eu-centralourole=ingestpara aplicar políticas de aprovação, limites de throughput e roteamento. - Negociação de versão: a SEMVER permite rejeitar clientes antigos explicitamente. Se a combinação não for suportada, sinalize na resposta e registre auditoria antes de desconectar.
- O stream utilizado pela autenticação precisa bidirecional e o PRIMEIRO a ser aberto. Assim que a resposta da autenticação for recebida/enviada, a stream deve ser imediatamente fechado por ambas as partes.
- Se aprovado, é necessário o servidor prover um identificador para esse cliente, apenas para fins de auditoria e correlação interna. O tamanho recomendado é de no máximo 36 caracteres.
Operações detalhadas
0x00 — Message
[ header comum ] [ msg_id:2 ] # ID da mensagem de aplicação (u16) [ payload_len:8 ] # u64: tamanho do payload total [ priority:1 ] # u8: prioridade
-
Stream dedicada (unidirecional):
Cada
Messageocupa uma QUIC stream unidirecional exclusiva. Um envio por stream. -
msg_id(u16): Espaço 0…65535 com wrap-around. Reserve faixas (ex.:0x8000–0xFFFF) para extensões privadas/experimentais. O receptor deve aplicar janela de rejeição a IDs “recentes” para evitar reexecução acidental. -
payload_len(u64): Define a semântica de corpo.0: semBlock/Block End.- Valor > 0: soma dos
Blocks deve fechar exatamente empayload_len. - Sentinela
0xFFFFFFFFFFFFFFFF: tamanho indefinido (streaming); termina viaBlock End.
-
Prioridade:
priority(0..255) onde 0 = mais alta, 255 = mais baixa. O emissor deve mapear isto para a prioridade de stream do QUIC quando disponível; caso contrário, use escalonador interno. Padrão recomendado: 128. -
Gate de envio:
O emissor só pode enviar
Blocks após receberProceeddo destinatário.- Com tamanho conhecido, envie até totalizar
payload_lene finalize comBlock End. - Em modo indefinido, envie quantos
Blocks forem necessários e finalize comBlock End.
- Com tamanho conhecido, envie até totalizar
-
Cancelamento e integridade:
Fechar a stream antes do
Block Endinvalida a operação. O receptor deve descartar dados parciais e emitirFailse houver efeito colateral incompleto. -
Confirmação de execução:
Ao concluir o processamento da mensagem, o destinatário deve responder com
Done(sucesso) ouFail(erro). Não há Response associada aMessage.
0x01 — Request
[ header comum ] [ msg_id:2 ] # u16 único por requisição [ payload_len:8 ] # u64: tamanho do corpo da requisição [ priority:1 ] # u8: prioridade [ response_msg_id:2 ] # u16 da mensagem de resposta esperada [ timeout_ms:4 ] # u32: prazo máximo para resposta
-
Stream dedicada (bidirecional):
O par requisição→resposta ocorre na mesma QUIC stream bidirecional. O lado que inicia envia a requisição; o oposto devolve a
Response(0x02) nesse mesmo stream. -
Protocolo de envio:
O iniciador deve aguardar
Proceedantes de transmitirBlocks da requisição. A requisição termina comBlock End(excetopayload_len == 0, onde não háBlock/Block End). -
response_msg_id(u16): Informa a priori o ID de aplicação que a outra ponta deve usar naResponse. Se o destinatário não possuir essa operação, deve responder imediatamente comFail(antes de executar). -
Recepção e processamento:
O destinatário só inicia a execução após ler o último byte da requisição (ou detectar
payload_len == 0). A execução deve ocorrer uma vez; reentregas com mesmo contexto devem ser detectadas via chaves de idempotência do nível de aplicação. -
Resposta no mesmo stream:
Concluída a leitura da requisição, o destinatário emite
Response(0x02) no mesmo stream. Em falhas, deve emitirFail(0x07) no Pool Global (assíncrono), garantindo que o solicitante seja notificado mesmo que o stream já tenha sido fechado pelo emissor. -
Timeout de resposta (
timeout_ms): O temporizador do solicitante inicia no momento em que a requisição é finalizada (Block Endoupayload_len == 0). AResponse(cabeçalho + eventual corpo) deve começar a ser enviada dentro desse prazo.timeout_ms <= 0: sem timeout (não recomendado).- Se o prazo expirar, o destinatário deve emitir
Failcom código de timeout.
-
Prioridade:
Mesmo significado de
Message; aplique ao escalonamento do worker e/ou prioridade de stream.
Importante: Request não também é finalizada com Done (0x08), mas logo em seguida também deve receber um Response (0x02) no mesmo stream. Exceto em caso de falhas, por Fail (0x07).
0x02 — Response
[ header comum ] [ response_payload_len:8 ] # u64: bytes previstos para o corpo de resposta [ exec_start_ms:8 ] # UNIX ms do instante em que a execução iniciou (após ler o último byte da requisição) [ exec_time_ms:4 ] # Duração da execução em ms (do início da execução até o término do processamento)
-
Correlação:
A
Responseé implicitamente correlacionada ao mesmo stream daRequeste aoresponse_msg_idinformado na requisição. A resposta não carregamsg_idpróprio no cabeçalho; o ID efetivo é o previamente anunciado emresponse_msg_id. -
response_payload_len(u64): Idêntica semântica de tamanho dos demais corpos: regras de0, valor > 0 e sentinela. -
Execução vs transferência:
exec_start_msmarca o exato momento em que a aplicação começou a processar a requisição (após recepção completa).exec_time_msmede apenas o tempo de computação (até a conclusão lógica do processamento), podendo ser menor que o tempo total de transferência do corpo de resposta. Em respostas que fazem streaming ativo, a emissão deBlocks pode sobrepor a execução. -
Fluxo de transmissão:
Após o cabeçalho de
Response, o emissor pode, imediatamente, enviar zero ou maisBlocks e deve finalizar comBlock End(exceto quandoresponse_payload_len == 0, onde não há corpo nemBlock End). Não há etapa deProceedna resposta: o cabeçalho autoriza o envio. -
Ordem e unicidade:
Para cada
Request, deve existir exatamente umaResponseouFail. Múltiplas respostas no mesmo stream ou respostas apósFailsão inválidas e devem ser ignoradas. -
Cancelamento e erro tardio:
Se a stream for fechada antes do
Block Endda resposta, o conteúdo parcial deve ser ignorado. Caso um erro ocorra após o envio do cabeçalho deResponse, o emissor deve abortar a stream e publicar umFailno Pool Global indicando o motivo (ex.: erro de geração do corpo). -
Tempo limite herdado:
O destinatário deve iniciar a geração da
Responsedentro detimeout_msdefinido na requisição. O receptor da resposta pode aplicar também um tempo máximo de transferência adicional (política local), encerrando por timeout se excedido. -
Fechamento:
Após enviar
Block End(ou apenas o cabeçalho, se sem corpo), o emissor deve fechar sua direção de envio. O solicitante deve enviar a operaçãoDonese bem-sucedido,Failcaso contrário e encerrar a stream.
0x03 — Proceed
[ header comum ] [ count:2 ] repeat count times: [ stream_id:8 ]
- Aprovação em lote aumenta eficiência em conexões com alto paralelismo.
- Cada
stream_idse refere ao id da stream da mensagem (MessageouRequestAPENAS!) aprovada. - Pode ser enviado de forma independente da ordem de chegada das mensagens. O receptor deve manter tabela para cruzar confirmações tardias.
- Tamanho recomendado: até 256 streams por operação.
0x04 — Refuse
[ header comum ] [ count:2 ] repeat count times: [ stream_id:8 ] [ retry_after_ms:4 ] [ reason_code:2 ]
- Permite política granular (ex.: recusar operações desconhecidas, tamanho indefinido ou falta de recursos). Recomendado usar códigos padronizados como
POLICY_UNDEFINED_LENGTH. - O campo
retry_after_msé usado para indicar que a operação deve ser reenviada após o tempo em millisegundos especificado. - Só devem ser inseridos id's de streams com operações
MessageouRequestpendentes no campostream_id, caso contrário será ignorado. - Ao recusar uma mensagem/requisição, deve-se encerrar a leitura do stream responsável pela mesma.
- Tamanho recomendado: até 256 operações por operação.
0x05 — Block
[ header comum ] [ payload:4 ] [ chunk_bytes:payload ]
- Disponível apenas após aprovação (
Proceed). payload(u32) descreve quantos bytes a operação carrega.
Se o stream for fechado por qualquer uma das pontas (FIN, RST ou erro de transporte) antes do Block End, os bytes recebidos devem ser descartados e a operação marcada como cancelada.
0x06 — Block End
[ header comum ] [ total_length:8 ]
- Deve ser enviado sempre após a finalização de transferência dos blocos, até mesmo se nenhum bloco foi transferido (no caso de
payload_len == 0 total_lengthfacilita validação e políticas de tamanho.- Encerrar o stream antes do envio do
Block End, mesmo após confirmação, finaliza a transferência prematuramente e a contraparte deve descartar o conteúdo parcial.
0x07 — Fail
[ header comum ] [ target_stream_id:8 ] # Identifica o stream alvo da operação que falhou [ error_code:2 ] # Código de erro (0..1023 reservados; >=1024 privados) [ reason_len:2 ] [ reason_utf8:reason_len ]
- Finalidade: sinalizar falhas de execução/transferência/tempo-limite. Deve ser publicado no Pool Global.
error_code: usar códigos padronizados; valores privados apenas ≥1024.reason_utf8: texto curto, em UTF-8, sem dados pessoais/segredos. Se > 512 bytes, truncar e referenciar um ID de auditoria.
Operações que podem receber Fail
Message(0x00) eResponse(0x00): sempre que executada com sucesso (com ou sem corpo). EnviarDoneaté ~1s após o término para evitar espera desnecessária.Request(0x01): NÃO recebe umDone, e sim umResponse.
0x08 — Done
[ header comum ] [ count:2 ] repeat count times: [ target_stream_id:8 ] # Operação concluída com sucesso [ exec_start_ms:8 ] # UNIX ms no início da execução (após leitura completa) [ exec_time_ms:4 ] # Duração da execução em ms
- Finalidade: confirmação assíncrona de sucesso com telemetria de execução; publicado no Pool Global.
- Lote: permite
count > 1para reduzir overhead quando múltiplas operações terminam em janelas curtas. - Telemetria: timestamps são da execução (não incluem tempo de transferência). Úteis para SLO e tracing.
Operações que podem receber Done
Message(0x00) eResponse(0x00): sempre que executada com sucesso (com ou sem corpo). EnviarDoneaté ~1s após o término para evitar espera desnecessária.Request(0x01): NÃO recebe umDone, e sim umResponse.
Done para Done ou Fail recebidos.
Request, e apenas nesse caso, não se envia Done no sucesso: a contraparte deve emitir Response (0x02).
0x09 — Disconnect Request
[ header comum ] [ drain_time_ms:4 ] [ reason_len:2 ] [ reason_utf8:reason_len ]
- Essa mensagem deve ser enviada/recebida apenas no
Pool Globalde streams. - Fase graciosa; permite tempo de drenagem (
drain_time_ms). Pode ser0se não houverdrain_time_ms(não recomendado) drain_time_msdeve ser interpretado como valor máximo de espera em millisegundos: a contraparte pode encerrar antes se todos os fluxos concluírem.- Se o stream atual for encerrado durante a transmissão, considere a solicitação cancelada e finalize a conexão o mais rápido possível.
- O motivo da desconexão
reason_utf8deve ser escrito em UTF-8.
0x0A — Disconnect
[ header comum ]
- Essa mensagem deve ser enviada/recebida apenas no
Pool Globalde streams. - Término imediato; pode carregar motivo para telemetria.
- O motivo deve ser especificado anteriormente por um
Disconnect Request. - Antes da autenticação: apenas
Disconnecté permitido.
Fluxo de aprovação e transferência
1. Iniciação
O emissor abre o stream apropriado (Message para uni, Request para bi) enviando apenas metadados. stream_id identifica unicamente a operação na sessão e deve ser armazenado em tabela de lookup com timeout configurável.
Nesta fase o emissor também informa payload_len, permitindo ao receptor verificar quotas antes de alocar memória.
2. Decisão do destinatário
A contraparte avalia a operação (políticas, recursos, limites) e responde via pool global com Proceed ou Refuse. Sem confirmação, nenhum byte de dados deve ser transmitido.
Caso o destinatário necessite de metadados adicionais, pode enviar Refuse com código específico fora de banda para reenviar o pedido.
3. Transferência de dados
Após aprovação, o emissor envia Blocks no mesmo stream, podendo fragmentar conforme necessário.
Implementações com janelas deslizantes obtêm throughput maior ao calibrar o número de blocos em voo com base no RTT medido.
4. Encerramento da carga
O emissor finaliza com Block End, informando tamanho consolidado. Interrupções voluntárias do stream antes do Block End invalidam a transferência e a contraparte deve descartar o material parcial.
5. Resposta (apenas Request)
A contraparte processa e responde com Response (e eventual mensagem) ou Fail. O envio deve ocorrer antes de timeout_ms; caso contrário, sinalizar Fail com código TIMEOUT.
Um Response válido envia primeiro o cabeçalho da resposta, seguido por zero ou mais Blocks e finalizado por Block End caso haja payload, reproduzindo a mesma disciplina de integridade das mensagens aprovadas, porém sem precisar de confirmação adicional.
Utilize response_payload_len para informar o volume esperado e permitir prefetch de buffers pelo solicitante.
Se qualquer lado interromper o stream antes do Block End, os dados parciais devem ser descartados e a operação cancelada.
Fluxo de desconexão ordenada
1. Solicitação de desligamento
O participante que deseja encerrar a sessão pode enviar Disconnect Request informando tempo de drenagem opcional (drain_time_ms) e motivo não sensível para auditoria.
Essa etapa é recomendada para desligamentos graciosos, mas não obrigatória: transmitir Disconnect diretamente força a finalização imediata, abortando todos os streams pendentes.
O receptor pode responder com novo Disconnect Request para sincronizar expectativas ou negar temporariamente a drenagem caso possua operações críticas em andamento.
Se ambos enviarem o pedido gracioso, a primeira extremidade que despachar Disconnect assume a responsabilidade de encerrar a associação.
2. Janela de drenagem
Durante o período indicado, é preferível que nenhuma nova stream de Message ou Request seja iniciada, exceto as fundamentais para processar as que já existem. Streams ativos podem concluir transferências vigentes (respeitando os Block End), mas encerramentos antecipados abortam definitivamente os dados restantes.
Operações sem progresso podem ser finalizadas com Fail usando código SHUTDOWN para sinalizar fechamento gracioso às aplicações clientes.
3. Confirmação ou recusa
A outra ponta, assim que receber o Disconnect Request deve ajustar o cronograma ou negar a drenagem enviando Disconnect imediato, invalidando streams pendentes sem completar as transferências parciais.
Este é o momento para consolidar métricas finais de throughput e descartar buffers temporários antes do desligamento definitivo.
4. Desligamento definitivo
Expirado o período de drenagem (ou por decisão antecipada), é transmitido um Disconnect. A conexão deve ser encerrada logo após esvaziar o pool global; motivos permanecem livres de dados sensíveis e servem apenas para logs.
Registre o número de streams terminados e o tempo de drenagem consumido para alimentar painéis de observabilidade.
Semântica de Execução & Handlers
Cada Message ou Request mapeia para um handler de aplicação identificado por msg_id.
O runtime da implementação agenda, executa, cancela e observa esses handlers, aplicando cotas e prioridades.
Mapeamento de mensagens
- Registro de handlers: tabela
msg_id → handler(nome simbólico opcional p/ telemetria). - Contrato mínimo do handler:
beforeExecute(ctx)— valida quotas, autenticação e dependências.execute(ctx, input)— lógica principal (sincrona/assíncrona).onCancel(ctx, reason)— liberação de recursos quando o stream fecha cedo.
- Idempotência: se
idempotencyvier preenchido, consultar/atualizar store de deduplicação (TTL ≥ janela de retry).
Agendamento & Pool
- Prioridade: campo
priority:u8(0=mais alta, 255=mais baixa). A implementação pode normalizar por plano/SLA. - Fila por classe: {crítica, alta, normal, baixa, best-effort} + algoritmo WFQ/DRR com aging.
- Cotas por identidade (tenant/role):
max_inflight_ops,max_buffer_bytes,max_block_rate. - Backpressure: bloquear aprovação (sem
Proceed) quando a cota estourar, emitirRefuse{CAPACITY_LIMIT}.
Garantias de processamento
- At-least-once por padrão (com idempotência → efetivo “exactly-once” por chave).
- Ordenação: garantida por stream, não global. Para ordem de domínio, usar particionamento (ver seção de cluster).
- Timeout:
- Requisição: controle no cliente; se não enviar
Block Enda tempo, a implementação cancela. - Resposta (
Request):timeout_msconta a partir deBlock Endda requisição.
- Requisição: controle no cliente; se não enviar
- Cancelamento cooperativo: fechar o stream ⇒ runtime chama
onCancel()e libera tombstone dostream_idapós TTL.
Observabilidade canônica
- Logs sugeridos por operação:
msg_id,bytes,lat_ms,priority,result. - Métricas:
ops_inflight,confirms_total,denies_total{reason},blocks_rx_bytes_total,handler_latency_ms{msg_id, p50,p95,p99}.
Pool de streams globais unidirecionais
O pool global é controlado pela implementação MAOP e serve exclusivamente a operações de controle (Proceed, Refuse, Done, Fail, Disconnect Request, Disconnect). Recomenda-se dimensionar entre 2 e 16 streams fixos, reutilizados para todas as operações de controle.
- Justificativa: evita explosão de streams para pequenos sinais e facilita multiplexação.
- Proibição: QUIC Datagrams não são utilizados em nenhuma circunstância no MAOP v1. Todo tráfego é por streams
- Monitoramento: registre métricas de backlog por stream do pool para detectar bursts de confirmações e acionar escalonamento de recursos.
- Redundância: mantenha buffers circulares independentes por stream de controle para evitar que um motivo extenso bloqueie confirmações críticas.
Algumas implementações expõem políticas de retry dedicadas ao pool para minimizar falsa percepção de perda de confirmação.
Se a primeira operação de uma stream for alguma das operações de controle, essa stream será automaticamente considerada parte do Pool Global.
Códigos de erro reservados
| Código | Nome | Descrição | Cenários típicos |
|---|---|---|---|
| 0 | INVALID_FORMAT | Frame/operação malformada ou campos incoerentes. | Payload menor/maior que o declarado; tipos fora do intervalo; bytes extras no fim. |
| 1 | UNAUTHORIZED | Política de autenticação/autorização negou a operação. | Envio de BLOCK/BLOCK_END sem PROCEED; token inválido/expirado; channel binding ausente. |
| 2 | CONTENT_TOO_LARGE | Tamanho do conteúdo excedeu limites acordados. | Soma dos BLOCKs ultrapassa limite; metadados/tokens acima do máximo. |
| 3 | INVALID_HEADER | Header com semântica inválida para o contexto. | Sentinela de tamanho usada fora do modo chunked; timestamps incoerentes em Response. |
| 4 | PAYLOAD_LENGTH_MISMATCH | Soma dos BLOCKs não bate com o payload_len/response_payload_len. |
Transferência com tamanho conhecido que termina com bytes a mais/menos. |
| 5 | TIMEOUT | Prazo esgotado para início/entrega da resposta. | Response não começou dentro de timeout_ms; streaming parado por tempo excessivo. |
| 6 | DUPLICATE_OPERATION | Sinalização repetida ou reuso inválido de identificadores. | PROCEED/REFUSE duplicados; reenvio do mesmo msg_id sem janela de proteção. |
| 7 | ORDER_VIOLATION | Ordem do fluxo violada. | Fechar stream antes de BLOCK_END; enviar BLOCK_END antes do primeiro BLOCK. |
| 8 | ID_MISMATCH | Identificadores esperados não conferem. | Response não honra response_msg_id; correlação com stream incorreta. |
| 9 | PARSE | Falha ao decodificar o frame/payload. | Varints mal codificados; UTF-8 inválido em campos textuais. |
| 10 | ILLEGAL_STREAM | Operação enviada em tipo de stream incorreto. | RESPONSE em stream não-REQUEST; PROCEED/FAIL fora do Pool Global; primeira operação não é GLOBAL_STREAM_MARKER/MESSAGE/REQUEST. |
| 11 | CANCELLED | Operação abortada localmente. | Aplicação encerra por shutdown; política de retry interrompe fluxo atual. |
| 12 | PROTOCOL_VIOLATION | Regras de alto nível quebradas. | REQUEST finalizada com DONE em vez de RESPONSE; direcionalidade da stream incompatível. |
| 13 | UNSUPPORTED_TEST_TYPE | Test recebido com type não suportado. |
type != 0 e implementação não implementa o teste solicitado. |
| 14 | CAPACITY_LIMIT | Recursos insuficientes ou cota atingida. | Buffers/CPU esgotados; limite de operações concorrentes; fair-use/rate limiting. |
| 15 | POLICY_UNDEFINED_LENGTH | Política exige comprimento conhecido e a transferência é indefinida. | Receptor requer payload_len exato e recebeu sentinela (chunked). |
| 16 | INCOMPATIBLE_MESSAGE | msg_id desconhecido/indisponível. |
Cliente envia mensagem não implementada no servidor (ou vice-versa). |
| 17 | EXECUTION_ERROR | Falha na execução da operação após validações. | Erro de negócio; exceção durante processamento do handler. |
| 18 | RESPONSE_MISMATCH | A resposta gerada não é compatível com a solicitada pela requisição. | Requisição |
| 19 | RESPONSE_CONSUME | Quando houve um erro ao processar a resposta de uma requisição. | Requisição |
| 49 | UNKNOWN_ERROR | Falha desconhecida | Erro não justificado, não conhecido pela implementação. |
Utilize o intervalo >= 50 para códigos específicos do produto e documente o mapeamento em contratos internos. Replique os códigos em dashboards de telemetria para acompanhar a evolução de falhas ao longo do tempo.
Vulnerabilidades potenciais & mitigações
Exaustão de streams (DoS)
Um emissor malicioso pode abrir muitos streams Message/Request sem concluir o fluxo, mantendo-os pendentes de aprovação.
Mitigação
- Limitar streams concorrentes aguardando confirmação.
- Aplicar timeouts curtos para aguardar
Proceede negar automaticamente. - Monitorar stream_id repetidos ou padrões comportamentais suspeitos.
- Coletar índice de aprovação para detectar aumento de tentativas sem confirmação.
Flood de confirmações/negações
O pool global pode ser congestionado por quantidade massiva de operações Proceed/Refuse, degradando controle.
Mitigação
- Implementar rate limiting no pool global.
- Agrupar (lote) confirmações para diminuir frequência.
- Aplicar priorização (ex.: denies por política primeiro).
Leak de motivos extensos
Motivos em Disconnect Request podem revelar informações sensíveis se logados ou expostos.
Mitigação
- Sanear mensagens antes de logar.
- Definir política de privacidade para motivos, truncando ou codificando.
- Fornecer mecanismo de classificação (debug vs produção).
Uso incorreto do timeout
timeout_ms muito alto reduz responsividade em falhas; muito baixo causa Fail prematuro.
Mitigação
- Negociar
timeout_mscom base em SLA e tipo de operação. - Permitir timeout dinâmico (p.ex., exponencial) com limites.
- Monitorar métricas de latência e ajustar automaticamente.
Mensagens nativas embutidas
Mensagens nativas são IDs reservados no espaço de msg_id que padronizam
formatos de payload e semânticas comuns entre implementações. Elas reduzem ambiguidade e
simplificam a interoperabilidade em respostas.
msg_id, carregados pelos fluxos normais
de Message/Request/Response.
Catálogo (v1)
| msg_id | Nome | Direção | Onde pode aparecer | Descrição |
|---|---|---|---|---|
0 |
Success | Somente resposta | Response (0x02) |
Facilitador de resposta: encapsula content-exceptionType + bytes da resposta.
Deve ser usado somente como payload de resposta de um Request.
|
1 |
Test | Message (0x00) ou Request (0x01) |
Qualquer lugar | Utilizado para verificações em ambientes de testes e métricas de desempenho, nativo de toda a implementação. |
msg_id = 0 (Success) em requisições (Message 0x00 ou Request 0x01).
Seu uso é reservado exclusivamente para o payload de Response (0x02).
Success (msg_id = 0)
Finalidade: padronizar a forma como um Request devolve bytes de aplicação com
um content-exceptionType explícito, sem exigir que cada par cliente/servidor negocie formatos ad hoc.
Wire dentro do payload (header nativo + corpo)
O payload da mensagem Success possui um header próprio seguido dos bytes do corpo:
# Payload de aplicação quando msg_id == 0 (Success) [ content_type_len:1 ] # u8 (1..255) [ content_type:content_type_len ] # US-ASCII, IANA media exceptionType (ex.: "application/json") [ body_bytes: ... ] # bytes arbitrários, tamanho = response_payload_len - (1 + content_type_len)
content_type_len(u8): tamanho em bytes docontent_type(intervalo recomendado: 1..255).content_type: precisa obedecer ao template de media types da IANA (ex.:application/json,application/cbor,image/png). Deve ser US-ASCII e case-insensitive quanto a comparação de tipos/subtipos.body_bytes: o corpo bruto da resposta, interpretado conforme ocontent_type.
Compatibilidade: O header nativo (content exceptionType) já faz parte do payload
e portanto entra no cômputo de response_payload_len.
A implementação precisa ler este layout exatamente nesta ordem.
Test (msg_id = 1)
Finalidade: Permitir que um testador universal valide, de forma determinística, a transferência de respostas
(Response → Success) sem conhecer regras específicas da implementação. O solicitante envia um
Request com msg_id = 1 e response_msg_id = 0. O respondente deve devolver uma Success com corpo
gerado conforme o byte de controle descrito abaixo.
Wire dentro do payload (header nativo)
# Payload de aplicação quando msg_id == 1 (Test) [ type:1 ] # u8: tipo do teste; 0 = TRANSFER. Outros valores: responder Fail (UNSUPPORTED_TEST_TYPE) [ expected_transfer:1 ] # u8: bitfield que define modo/tamanho/pacing da transferência da Success
-
Compatibilidade futura: Implementações devem verificar
type. Setype != 0, responder comFail(ex.:UNSUPPORTED_TEST_TYPE). Paratype = 0, aplicar as regras deexpected_transfer.
Bitfield de expected_transfer (u8)
| Bits | Nome | Valor | Semântica |
|---|---|---|---|
7..6 | mode |
00 = sem corpo01 = corpo com tamanho conhecido10 = corpo em modo chunked (tamanho indefinido; sentinela)11 = reservado → Fail/UNSUPPORTED_TEST_MODE
|
Define a semântica de response_payload_len na Response. |
5 | multi |
0 = um único Block1 = múltiplos Blocks |
Quantidade de Blocks a emitir quando houver corpo. |
4 | reservado |
Valor reservado sem uso por enquanto | Reservado para extensão futura |
3..0 | exp |
0..15 |
Classe de tamanho: L = 1024 * 2^exp bytes (1 KiB, 2 KiB, 4 KiB, …, 32 MiB). |
Regras de geração da Success (resposta)
-
Header da Success (msg_id = 0): usar
content_type = "application/octet-stream". O conteúdo não é semanticamente verificado; apenas o protocolo de transferência (medidas, contagem de Blocks, etc.). -
mode = 00(sem corpo):- Publicar
response_payload_len = 0na Response. - Não enviar
Block/Block End.
- Publicar
-
mode = 01(tamanho conhecido):- Calcular
L = 1024 * 2^expe publicarresponse_payload_len = L. - Se
multi = 0: enviar um únicoBlockde exatosLbytes, seguido deBlock End. - Se
multi = 1: enviar múltiplosBlocks cuja soma seja exatamenteL. Particionamento determinístico recomendado:N = 2^(2 + (exp mod 4))⇒ N ∈ {4, 8, 16, 32}; tamanho por bloco =ceil(L / N)(o último pode ser menor).
- Calcular
-
mode = 10(chunked / tamanho indefinido):- Publicar
response_payload_len = 0xFFFFFFFFFFFFFFFF(sentinela). -
Importante — uso de
expno modo chunked:multi = 0(um único Block): não ignoraexp. O únicoBlockdeve ter exatamenteLbytes (ondeL = 1024 * 2^exp), seguido deBlock End. A diferença para o modo conhecido é apenas o cabeçalho da Response (sentinela), que testa a capacidade do receptor de lidar com comprimento indefinido no cabeçalho mesmo quando o corpo real tem tamanho preciso.multi = 1(múltiplos Blocks): enviar váriosBlocks e finalizar comBlock End; a soma total deve ficar no intervalo[L, L + 10%]. Particionamento recomendado idêntico ao do modo conhecido, permitindo que o último bloco feche dentro da margem.
- Publicar
-
Fechamento:
Sempre finalizar com
Block Endquando houver corpo; depois, encerrar a direção de envio. Emmode = 00, apenas o cabeçalho da Response.
Critérios de conformidade (lado do testador)
- Verificar
type == 0; caso contrário, aceitarFailcom erro de tipo não suportado. - Confirmar que a resposta é Response → Success no mesmo stream da requisição e que
response_msg_id = 0foi respeitado. - Checar coerência entre
modeeresponse_payload_len(0, L ou sentinela). - No modo conhecido: soma dos bytes exatamente
L(e 1 ou N Blocks conformemulti). - No modo chunked:
- Se
multi = 0: exatamente 1Blockde tamanho L. - Se
multi = 1: soma total em[L, L + 10%], finalizando comBlock End.
- Se
Equivalência com os quatro casos clássicos:
0 ⟶ mode=00; 1 ⟶ mode=01, multi=0; 2 ⟶ mode=01, multi=1; 3 ⟶ mode=10, multi=1.
O caso mode=10, multi=0 agora é especificado: um único Block de L bytes, exercitando a semântica de “comprimento indefinido” no cabeçalho.
Uso correto com Request (0x01) → Response (0x02)
- Cliente envia
Request(0x01) com umresponse_msg_idacordado (p.ex., 42) epayload_lenda requisição. - Servidor processa, então responde com
Response(0x02) usandomsg_id == response_msg_id(42) eresponse_payload_len = 1 + content_type_len + |body_bytes|. - Servidor envia
Block(s) contendo o payload Success:- primeiro byte:
content_type_len; - em seguida:
content_type(ASCII); - por fim:
body_bytes.
- primeiro byte:
- Servidor finaliza com
Block End(0x06).
Regras de validação (DEVE/NAO DEVE)
- DEVE validar que
content_type_len∈ [1, 255]. - DEVE validar
content_typecontra o template IANA (ex. regex aproximada:^[A-Za-z0-9!#$&^_.+-]+\/[A-Za-z0-9!#$&^_.+-]+$). - NAO DEVE aceitar
msg_id = 0em requisições (Message/Request). Se detectado, useRefusecomINVALID_FORMAT. - DEVE rejeitar
content_type_lencujo conteúdo extrapole o payload ou contenha bytes não ASCII. - DEVE tratar
response_payload_lencomo o total incluindo o header nativo.
Erros comuns e ações sugeridas
- content_type inválido (não segue IANA): responder
FailcomINVALID_FORMAT. - payload truncado (string maior que o buffer):
INVALID_FORMAT. - uso indevido em requisição (msg_id=0 no pedido):
INVALID_FORMATouPOLICY_UNDEFINED_LENGTH(conforme política), e auditoria.
Exemplos de content_type aceitos
application/json— corpo JSON UTF-8.application/cborouapplication/msgpack— binário estruturado.application/octet-stream— bytes arbitrários.image/png,text/plain, etc.
application/octet-stream
ao invés de um valor ad hoc. Isso mantém a semântica compatível com a IANA.
Telemetria mínima sugerida
maop_success_count_totalmaop_success_bytes_total{content_type}maop_invalid_content_type_total
Expansão futura: novas mensagens nativas poderão ocupar outros msg_id reservados.
Mantenha uma tabela de compatibilidade por minor version para ativação progressiva.
Recomendações de implementação
Gestão de estado e memória
- Armazene metadados de cada operação/stream em tabela de hash com eviction baseado em timeout.
- Libere buffers assim que
Block Endfor validado, evitando vazamentos. - Para
Request, mantenha relógio de alta resolução (>= 1ms) para respeitartimeout_ms. - Buffers circulares podem reduzir pressão de GC quando o número de
Blocks em voo é alto.
Logs e observabilidade
- Registre eventos-chave: criação de operação, proceed/refuse, blocos recebidos, respostas.
- Correlacione logs usando id's de stream.
- Capture métricas (histogramas) para latência, taxa de negação, volume de dados.
- Adicione percentis para identificar regressões na aprovação de operações.
Mapas de uso (“Receitas”)
Três receitas práticas para implementar MAOP com segurança e previsibilidade. Cada receita traz: quando usar, pré-requisitos, sequência de wire, passo a passo, tratamento de erro e métricas.
Arquivo grande conhecido (payload_len = N)
Envio de objeto/binário de tamanho conhecido com fragmentação em Blocks e encerramento por Block End.
Quando usar
- Uploads/downloads de artefatos, backups, mídia, datasets.
- Retomadas e validação estrita de integridade no destino.
Pré-requisitos
- Autenticação concluída e versão/capacidades negociadas.
- Política do receptor permite
payload_len >= 0com limite N definido. - Gate:
Message Proceedobrigatório antes de qualquerBlock.
Sequência (wire)
TX → RX: 0x00 Message [header comum: payload_len = N] RX → TX: 0x03 Proceed [stream_id(s)] TX → RX: 0x05 Block [payload, bytes] × K ... TX → RX: 0x06 Block End [total_length = N] (encerra stream)
Passo a passo (emissor)
- Abra stream unidirecional e envie
Messagecompayload_len = Neidempotencyde 128 bits. - Aguarde
Proceed. - Fatie o arquivo em
Blocks:payloadatémax_block_payload(ex.: 128 KiB).
- Envie todos os
Blocks até somar N bytes. - Finalize com
Block Ende feche o stream.
Passo a passo (receptor)
- Ao receber
Message, validepayload_len = Ncontra quotas; respondaProceedouRefuse. - Ao receber
Block End, verifique:total_length == Ne soma de bytes recebidos == N.
Erros & políticas
- Stream encerrado antes de
Block End⇒ descarte; marque como cancelada. - Excesso de bytes (> N) ⇒
INVALID_FORMAT.
Métricas sugeridas
maop_blocks_rx_bytes_totalmaop_upload_latency_ms{p50,p95,p99}maop_confirms_per_sec,maop_denies_per_sec
Streaming indefinido (payload_len = 0xFFFFFFFFFFFFFFFF)
Produção contínua de dados sem tamanho prévio (telemetria rica, export de logs, transcodificação em tempo real), concluída por Block End.
Quando usar
- Fluxos em que o produtor não conhece o tamanho final no início.
- Transmissões que podem durar minutos/horas e precisam de integridade ao final.
Pré-requisitos
- Política do receptor permite comprimento indefinido; caso contrário, responderá
RefusecomPOLICY_UNDEFINED_LENGTH. - Janelas de backpressure configuradas (limitar
Blocksem voo).
Sequência (wire)
TX → RX: 0x00 Message [header comum: payload_len = 0xFFFFFFFFFFFFFFFF] RX → TX: 0x03 Proceed TX → RX: 0x05 Block [payload, bytes] × variável ... TX → RX: 0x06 Block End [total_length = bytes_enviados] (encerra stream)
Passo a passo (emissor)
- Abra stream unidirecional e envie
Messagecompayload_len = 0xFFFFFFFFFFFFFFFF. - Após
Proceed, produzaBlocks conforme disponibilidade; mantenha contagem cumulativa. - Quando finalizar, envie
Block Endcomtotal_length = bytes_enviadosdo total; feche o stream.
Passo a passo (receptor)
- Ao receber
Message, verifique políticas; se não permitir comprimento indefinido, respondaRefusecomPOLICY_UNDEFINED_LENGTH. - Agregue os
Blocks. - Em
Block End, comparetotal_lengthcom o acumulado e libere à aplicação.
Erros & políticas
- Ausência de
Block End⇒ descarte e marque cancelada. - Exigência de tamanho prévio ⇒
RefusecomPOLICY_UNDEFINED_LENGTH(e opcionalretry_after). - Estouro de cota ⇒
TOO_LARGEouCAPACITY_LIMIT.
Métricas sugeridas
maop_streaming_sessions_open,maop_blocks_rx_bytes_totalmaop_streaming_duration_ms{p50,p95,p99}maop_denies_policy_undefined_length_total
total_length aceitável por sessão.
RPC leve (Request sem payload + resposta curta)
Chamada/retorno compacta no mesmo stream: requisição sem payload, processamento rápido e resposta pequena.
Quando usar
- RPCs binários simples (consultas, metadados, ping extendido).
- Respostas até alguns KiB, com tempo máximo definido (
timeout_ms).
Pré-requisitos
- Autenticação concluída;
timeout_msnegociado/padrão. - Semântica:
payload_lenda requisição =0.
Sequência (wire)
TX ⇄ RX: 0x01 Request [header comum: payload_len = 0] RX → TX: 0x03 Proceed [gate] (sem Blocks na requisição) RX → TX: 0x02 Response [response_payload_len = M] RX → TX: 0x05 Block [payload, bytes] × J (opcional) RX → TX: 0x06 Block End [total_length = M] (encerra stream)
Passo a passo
- Cliente abre stream bidirecional e envia
Requestcompayload_len = 0(semBlock). - Servidor avalia e envia
Proceed. Processa a requisição. - Servidor envia
Responsecomresponse_payload_len = M(0 se sem corpo). - Se
M > 0, enviaBlock(s) seguido deBlock End. Encerra o stream. - Cliente valida e consome a resposta.
Erros & timeouts
- Sem resposta no tempo: destinatário envia
FailcomTIMEOUT; receptário fecha stream. - Falha de execução:
Failcomerror_code(u16) ereasonsanitizado. - Encerramento antecipado: descarte dados parciais de resposta.
Métricas sugeridas
maop_request_latency_ms{p50,p95,p99}maop_response_fail_total{error_code}maop_rpc_payload_bytes
Observação: Em todas as receitas, strings são UTF-8 normalizadas (NFC); bytes inválidos resultam em INVALID_FORMAT.
O emissor NÃO DEVE enviar Block antes do Proceed.
Implementações do protocolo (SDKs)
Acompanhe os SDKs oficiais e comunitários para acelerar integrações. As referências abaixo sugerem pontos de entrada e status esperado para cada linguagem.
Cada card referencia uma variação distinta apontada no menu de navegação superior. Utilize o combo language.variant ao registrar feedback ou solicitar features, mantendo o catálogo consistente entre equipes.
SDK Java • maop-java
Referência oficial construída sobre Kwik, aproveitando backpressure para manter buffers MAOP estáveis. Contempla otimização moderada e ainda está em fase experimental, permitindo multiplexação segura e análise de telemetria por meio de logs.
Status: Design de API & experimentação Blueprint (em breve)Erros comuns de implementação
Esta seção lista violações frequentes do wire/semântica do MAOP, como detectá-las e como corrigi-las. Os códigos abaixo são exemplos de application errors recomendados; ajuste-os para o seu enum, preservando os já reservados (1 = UNAUTHORIZED, 10 = ILLEGAL_STREAM).
Primeira operação inválida na stream (código 10: ILLEGAL_STREAM)
- Descrição: Enviar uma operação desconhecida como primeira mensagem de uma stream recém-criada (exceto a de autenticação) ou qualquer coisa diferente de
GLOBAL_STREAM_MARKER,MESSAGEouREQUEST. - Por que é erro: A primeira operação define o tipo da stream; sem ela, a contraparte não consegue classificar o fluxo.
- Correção: Sempre iniciar a stream com
GLOBAL_STREAM_MARKER(quando aplicável),MESSAGE(0x00) ouREQUEST(0x01).
Operações de Pool Global na stream (código 10: ILLEGAL_STREAM)
- Descrição: Emitir
PROCEED,REFUSE,DONE,FAIL,DISCONNECTouDISCONNECT_REQUESTdentro de uma stream que não pertence ao Pool Global, ou o inverso. - Correção: Respeitar o escopo: operações de pool só no Pool Global; operações de fluxo (
MESSAGE/REQUEST/RESPONSE/BLOCK/BLOCK_END) apenas em streams.
RESPONSE em stream que não é de REQUEST (código 10: ILLEGAL_STREAM)
- Descrição: Emitir
RESPONSE(0x02) em uma stream que foi aberta porMESSAGEou que ainda não foi classificada comoREQUEST. - Correção: Só enviar
RESPONSEno mesmo stream que recebeu umaREQUESTválida.
BLOCK/BLOCK_END fora de contexto (código 10: ILLEGAL_STREAM)
- Descrição: Emitir
BLOCKouBLOCK_ENDem uma stream sem transferência ativa (semMESSAGEouREQUEST/RESPONSEque as habilite). - Correção: Garantir que a transferência esteja “armada”: cabeçalho enviado,
PROCEEDrecebido e semBLOCK_ENDanterior.
Transferência sem PROCEED (código 1: UNAUTHORIZED)
- Descrição: Enviar
BLOCKouBLOCK_ENDantes doPROCEEDcorrespondente. - Correção: Aguardar autorização explícita via
PROCEEDantes de qualquer dado.
Soma de BLOCKs ≠ payload_len (código 4: PAYLOAD_LENGTH_MISMATCH)
- Descrição: Em transferências com tamanho conhecido (
payload_len/response_payload_len > 0), a soma dosBLOCKs não bate exatamente. - Correção: Emitir
BLOCKs cuja soma seja idêntica ao valor anunciado; validar antes deBLOCK_END.
Falta de BLOCK_END (código 7: ORDER_VIOLATION)
- Descrição: Encerrar a direção de envio/fechar a stream sem emitir
BLOCK_ENDquando havia corpo a ser enviado. - Correção: Sempre finalizar a transferência com
BLOCK_END(exceto quando o tamanho anunciado é0).
Modo chunked com cabeçalho incorreto (código 3: INVALID_HEADER)
- Descrição: Usar sentinela de tamanho indefinido e ainda assim enviar
payload_len/response_payload_len≠0xFFFFFFFFFFFFFFFF, ou vice-versa. - Correção: Em modo chunked, sempre publicar a sentinela; fora dele, nunca publicar a sentinela.
Finalizar REQUEST com DONE (código 12: PROTOCOL_VIOLATION)
- Descrição: Após processar uma
REQUEST, enviarDONEem vez deRESPONSEno mesmo stream. - Correção:
REQUESTbem-sucedida sempre resulta emRESPONSE; erros usamFAIL(no Pool Global).
response_msg_id não honrado (código 8: ID_MISMATCH)
- Descrição: A resposta não corresponde ao
response_msg_idanunciado na requisição. - Correção: Validar suporte local ao
response_msg_ide, se ausente, retornarFAILimediato (antes da execução).
PROCEED/REFUSE duplicados (código 6: DUPLICATE_OPERATION)
- Descrição: Emitir múltiplos
PROCEEDou alternarPROCEED/REFUSEpara a mesma operação. - Correção: Cada transferência aceita exatamente um
PROCEEDou umREFUSE.
Estouro de timeout de resposta (código 5: TIMEOUT)
- Descrição: Não iniciar o envio da
RESPONSEdentro detimeout_msapós o término da requisição. - Correção: Cronometrar a partir do
BLOCK_END(oupayload_len==0) e emitirFAILcom razão de timeout se não for possível responder em tempo.
Fechamento prematuro do stream (código 7: ORDER_VIOLATION)
- Descrição: Encerrar a stream antes do
BLOCK_ENDda requisição ou da resposta. - Correção: Consumir/enviar até o
BLOCK_END; do contrário, tratar como operação cancelada e descartar bytes parciais.
Prioridade ignorada (código 12: PROTOCOL_VIOLATION)
- Descrição: Desconsiderar o campo
priorityna ordenação de execução/stream quando o runtime suporta mapeamento. - Correção: Propagar
prioritypara o scheduler e/ou prioridade de stream quando disponível; documentar fallback.
Test type não suportado (código 13: UNSUPPORTED_TEST_TYPE)
- Descrição: Receber
Test(msg_id=1) comtype != 0e ainda assim tentar executar. - Correção: Validar
type; para valores diferentes de0, retornarFAILcom erro de tipo não suportado.
expected_transfer mal interpretado (código 3: INVALID_HEADER)
- Descrição: No
Test(transfer), ignorar bits de mode/multi/exp ou usá-los de forma inconsistente com o cabeçalho (response_payload_len). - Correção: Implementar exatamente as regras do bitfield: sentinela para chunked, soma exata para tamanho conhecido, pacing determinístico, partições conforme especificado.
Header nativo de Success ausente/inválido (código 3: INVALID_HEADER)
- Descrição: Enviar Success (
msg_id=0) sem o header nativo (content_type_len+content_type) antes do corpo. - Correção: Preceder sempre o corpo com o header nativo e contabilizá-lo em
response_payload_len.
Ordem/stream de autenticação violada (código 12: PROTOCOL_VIOLATION)
- Descrição: Autenticar fora da primeira stream bidirecional ou manter a stream de auth aberta após resposta.
- Correção: Usar a primeira stream bidirecional para auth e fechá-la imediatamente após a resposta de autenticação.
Metadados/tokens fora dos limites (código 9: CONTENT_TOO_LARGE)
- Descrição: Exceder limites máximos acordados para token, quantidade de pares ou tamanho total de metadados.
- Correção: Aplicar validação de limites antes de qualquer chamada externa de verificação; recusar sanetizando o motivo.
Falta de channel binding quando exigido (código 1: UNAUTHORIZED)
- Descrição: Políticas que exigem channel binding (ex.: TLS exporter/CID) mas o cliente não envia o campo correspondente.
- Correção: Exigir/validar o binding nas políticas e recusar com
UNAUTHORIZEDquando ausente/inválido.
Reuso incorreto de identificadores (código 6: DUPLICATE_OPERATION)
- Descrição: Reaproveitar
msg_idpara uma nova operação sem janela de proteção contra reexecução. - Correção: Implementar janela deslizante para
msg_ide rejeitar reusos recentes.
Direcionalidade incorreta (código 12: PROTOCOL_VIOLATION)
- Descrição: Enviar
MESSAGEouREQUEST/RESPONSEem streams com direcionalidade incompatível (ex.: tentar responder em stream unidirecional). - Correção:
MESSAGE: stream unidirecional;REQUEST↔RESPONSE: stream bidirecional.
FAIL no lugar errado (código 10: ILLEGAL_STREAM)
- Descrição: Publicar
FAILdentro do stream em vez de no Pool Global, ou publicarFAILapós já ter emitido umaRESPONSE. - Correção: Erros de execução/negociação devem ser sinalizados no Pool Global; não misturar com a stream de dados.
Proceed após finalização (código 7: ORDER_VIOLATION)
- Descrição: Emitir
PROCEEDdepois que a contraparte já encerrou a transferência (BLOCK_ENDou fechamento). - Correção: Verificar estado antes de autorizar; operações concluídas devem ignorar sinais tardios.
exec_start_ms/exec_time_ms incoerentes (código 3: INVALID_HEADER)
- Descrição: Em
RESPONSE, publicar timestamps ondeexec_time_ms < 0,exec_start_msno futuro ou duração incompatível com o tempo de vida do stream. - Correção: Medir a execução a partir do último byte da requisição; clamp/tornar monotônicos os relógios.
Configuração sugerida (exemplo)
{
"protocol_version": 1,
"global_control_pool_size": 4,
"refuse_undefined_length_by_default": true,
"max_block_chunk_size": 131072,
"max_blocks_per_message": 4096,
"default_request_timeout_ms": 5000
}
Ajuste parâmetros conforme tipos de carga, SLA e infraestrutura. Configure telemetria para revisar adequação periodicamente.
Calibração periódica garante equilíbrio entre throughput e consumo de memória. Considere persistir histórico das últimas configurações aprovadas para auditoria.
Changelog — revisão atual
- Autenticação inicial: especificação de stream dedicado com tokens de até 65.550 bytes, metadados chave-valor e negociação SEMVER + bitmap de capacidades.
- IDs de mensagem 16-bit: formalização do limite de 65.536 identificadores e reserva do valor 0 para controle interno.
- Declaração explícita de payload: inclusão de
payload_len(u64) emMessageeRequestpara facilitar quotas. - Regras de encerramento: definição de que streams abortados antes do
Block Endinvalidam transferências, mesmo após confirmação. - Resposta estruturada:
Responsepassa a declararresponse_payload_lene seguir o mesmo fluxo de blocos sem confirmação. - Motivos não sensíveis: reforço da proibição de dados confidenciais em
Disconnect Request. - Fluxo de desconexão detalhado: documentação de etapas de drenagem, cancelamento e métricas finais.
- Diretórios SDK aprimorados: catálogo com múltiplas variantes por linguagem e navegação em dois níveis.