MAOP Meinicke Asynchronously Operation Protocol

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.

Autor: Daniel Meinicke Transporte: QUIC/TLS 1.3 Foco: Alta taxa & baixa latência Versão documentada: 1 (rascunho público)

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/1 no 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_len e rate limiting no pool global para mitigar DoS.
  • Autenticação obrigatória antes de aceitar qualquer operação aplicacional.
  • Se payload_len == 0xFFFFFFFFFFFFFFFF for proibido por política, Responder com Refuse (0x04) usando POLICY_UNDEFINED_LENGTH.

Perfil mínimo de conformidade

  • Suportar ALPN maop/1 e 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 Block antes do Proceed, por exemplo.
  • Descartar transferências com payload mas sem Block End após tempo configurado.
  • Encerrar com Disconnect Request e/ou Disconnect em 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-central ou role=ingest para 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-central ou role=ingest para 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 Message ocupa 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: sem Block/Block End.
    • Valor > 0: soma dos Blocks deve fechar exatamente em payload_len.
    • Sentinela 0xFFFFFFFFFFFFFFFF: tamanho indefinido (streaming); termina via Block 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 pode enviar Blocks após receber Proceed do destinatário.
    • Com tamanho conhecido, envie até totalizar payload_len e finalize com Block End.
    • Em modo indefinido, envie quantos Blocks forem necessários e finalize com Block End.
  • Cancelamento e integridade: Fechar a stream antes do Block End invalida a operação. O receptor deve descartar dados parciais e emitir Fail se houver efeito colateral incompleto.
  • Confirmação de execução: Ao concluir o processamento da mensagem, o destinatário deve responder com Done (sucesso) ou Fail (erro). Não há Response associada a Message.

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 Proceed antes de transmitir Blocks da requisição. A requisição termina com Block End (exceto payload_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 na Response. Se o destinatário não possuir essa operação, deve responder imediatamente com Fail (antes de executar).
  • Recepção e processamento: O destinatário 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 emitir Fail (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 End ou payload_len == 0). A Response (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 Fail com 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 da Request e ao response_msg_id informado na requisição. A resposta não carrega msg_id próprio no cabeçalho; o ID efetivo é o previamente anunciado em response_msg_id.
  • response_payload_len (u64): Idêntica semântica de tamanho dos demais corpos: regras de 0, valor > 0 e sentinela.
  • Execução vs transferência: exec_start_ms marca o exato momento em que a aplicação começou a processar a requisição (após recepção completa). exec_time_ms mede 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 de Blocks pode sobrepor a execução.
  • Fluxo de transmissão: Após o cabeçalho de Response, o emissor pode, imediatamente, enviar zero ou mais Blocks e deve finalizar com Block End (exceto quando response_payload_len == 0, onde não há corpo nem Block End). Não há etapa de Proceed na resposta: o cabeçalho autoriza o envio.
  • Ordem e unicidade: Para cada Request, deve existir exatamente uma Response ou Fail. Múltiplas respostas no mesmo stream ou respostas após Fail são inválidas e devem ser ignoradas.
  • Cancelamento e erro tardio: Se a stream for fechada antes do Block End da resposta, o conteúdo parcial deve ser ignorado. Caso um erro ocorra após o envio do cabeçalho de Response, o emissor deve abortar a stream e publicar um Fail no Pool Global indicando o motivo (ex.: erro de geração do corpo).
  • Tempo limite herdado: O destinatário deve iniciar a geração da Response dentro de timeout_ms definido 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ção Done se bem-sucedido, Fail caso 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_id se refere ao id da stream da mensagem (Message ou Request APENAS!) 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 Message ou Request pendentes no campo stream_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_length facilita 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) e Response (0x00): sempre que executada com sucesso (com ou sem corpo). Enviar Done até ~1s após o término para evitar espera desnecessária.
  • Request (0x01): NÃO recebe um Done, e sim um Response.
Implementações não devem encadear Fail de falhas do próprio Fail para evitar loops (ex.: se a publicação do Fail falhar, registre localmente).
NÃO use códigos < 1024 para erros privados; a faixa baixa é reservada ao protocolo.

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 > 1 para 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) e Response (0x00): sempre que executada com sucesso (com ou sem corpo). Enviar Done até ~1s após o término para evitar espera desnecessária.
  • Request (0x01): NÃO recebe um Done, e sim um Response.
Por razões óbvias, nunca envie Done para Done ou Fail recebidos.
Em 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 Global de streams.
  • Fase graciosa; permite tempo de drenagem (drain_time_ms). Pode ser 0 se não houver drain_time_ms (não recomendado)
  • drain_time_ms deve 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_utf8 deve ser escrito em UTF-8.
O motivo enviado em Disconnect Request não deve conter informações sensíveis; trate-os como mensagens voltadas a debugging controlado.

0x0A — Disconnect

[ header comum ]
  • Essa mensagem deve ser enviada/recebida apenas no Pool Global de 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 idempotency vier 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, emitir Refuse{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 End a tempo, a implementação cancela.
    • Resposta (Request): timeout_ms conta a partir de Block End da requisição.
  • Cancelamento cooperativo: fechar o stream ⇒ runtime chama onCancel() e libera tombstone do stream_id apó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 Proceed e 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_ms com 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.

Escopo: Mensagens nativas não são operações do wire (0x00…0x01). Elas são conteúdos de aplicação identificados por 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.
Bloqueio recomendado: Implementações DEVEM bloquear o uso de 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 do content_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 o content_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 (ResponseSuccess) 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. Se type != 0, responder com Fail (ex.: UNSUPPORTED_TEST_TYPE). Para type = 0, aplicar as regras de expected_transfer.

Bitfield de expected_transfer (u8)

BitsNomeValorSemântica
7..6mode 00 = sem corpo
01 = corpo com tamanho conhecido
10 = corpo em modo chunked (tamanho indefinido; sentinela)
11 = reservado → Fail/UNSUPPORTED_TEST_MODE
Define a semântica de response_payload_len na Response.
5multi 0 = um único Block
1 = múltiplos Blocks
Quantidade de Blocks a emitir quando houver corpo.
4reservado Valor reservado sem uso por enquanto Reservado para extensão futura
3..0exp 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 = 0 na Response.
    • Não enviar Block/Block End.
  • mode = 01 (tamanho conhecido):
    • Calcular L = 1024 * 2^exp e publicar response_payload_len = L.
    • Se multi = 0: enviar um único Block de exatos L bytes, seguido de Block End.
    • Se multi = 1: enviar múltiplos Blocks cuja soma seja exatamente L. 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).
  • mode = 10 (chunked / tamanho indefinido):
    • Publicar response_payload_len = 0xFFFFFFFFFFFFFFFF (sentinela).
    • Importante — uso de exp no modo chunked:
      • multi = 0 (um único Block): não ignora exp. O único Block deve ter exatamente L bytes (onde L = 1024 * 2^exp), seguido de Block 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ários Blocks e finalizar com Block 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.
  • Fechamento: Sempre finalizar com Block End quando houver corpo; depois, encerrar a direção de envio. Em mode = 00, apenas o cabeçalho da Response.

Critérios de conformidade (lado do testador)

  • Verificar type == 0; caso contrário, aceitar Fail com erro de tipo não suportado.
  • Confirmar que a resposta é ResponseSuccess no mesmo stream da requisição e que response_msg_id = 0 foi respeitado.
  • Checar coerência entre mode e response_payload_len (0, L ou sentinela).
  • No modo conhecido: soma dos bytes exatamente L (e 1 ou N Blocks conforme multi).
  • No modo chunked:
    • Se multi = 0: exatamente 1 Block de tamanho L.
    • Se multi = 1: soma total em [L, L + 10%], finalizando com Block End.

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)

  1. Cliente envia Request (0x01) com um response_msg_id acordado (p.ex., 42) e payload_len da requisição.
  2. Servidor processa, então responde com Response (0x02) usando msg_id == response_msg_id (42) e response_payload_len = 1 + content_type_len + |body_bytes|.
  3. Servidor envia Block(s) contendo o payload Success:
    • primeiro byte: content_type_len;
    • em seguida: content_type (ASCII);
    • por fim: body_bytes.
  4. Servidor finaliza com Block End (0x06).

Regras de validação (DEVE/NAO DEVE)

  • DEVE validar que content_type_len ∈ [1, 255].
  • DEVE validar content_type contra o template IANA (ex. regex aproximada: ^[A-Za-z0-9!#$&^_.+-]+\/[A-Za-z0-9!#$&^_.+-]+$).
  • NAO DEVE aceitar msg_id = 0 em requisições (Message/Request). Se detectado, use Refuse com INVALID_FORMAT.
  • DEVE rejeitar content_type_len cujo conteúdo extrapole o payload ou contenha bytes não ASCII.
  • DEVE tratar response_payload_len como o total incluindo o header nativo.

Erros comuns e ações sugeridas

  • content_type inválido (não segue IANA): responder Fail com INVALID_FORMAT.
  • payload truncado (string maior que o buffer): INVALID_FORMAT.
  • uso indevido em requisição (msg_id=0 no pedido): INVALID_FORMAT ou POLICY_UNDEFINED_LENGTH (conforme política), e auditoria.

Exemplos de content_type aceitos

  • application/json — corpo JSON UTF-8.
  • application/cbor ou application/msgpack — binário estruturado.
  • application/octet-stream — bytes arbitrários.
  • image/png, text/plain, etc.
Dica: caso a aplicação não saiba o tipo real, prefira 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_total
  • maop_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 End for validado, evitando vazamentos.
  • Para Request, mantenha relógio de alta resolução (>= 1ms) para respeitar timeout_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 >= 0 com limite N definido.
  • Gate: Message Proceed obrigatório antes de qualquer Block.

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)

  1. Abra stream unidirecional e envie Message com payload_len = N e idempotency de 128 bits.
  2. Aguarde Proceed.
  3. Fatie o arquivo em Blocks:
    • payload até max_block_payload (ex.: 128 KiB).
  4. Envie todos os Blocks até somar N bytes.
  5. Finalize com Block End e feche o stream.

Passo a passo (receptor)

  1. Ao receber Message, valide payload_len = N contra quotas; responda Proceed ou Refuse.
  2. Ao receber Block End, verifique:
    • total_length == N e 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_total
  • maop_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á Refuse com POLICY_UNDEFINED_LENGTH.
  • Janelas de backpressure configuradas (limitar Blocks em 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)

  1. Abra stream unidirecional e envie Message com payload_len = 0xFFFFFFFFFFFFFFFF.
  2. Após Proceed, produza Blocks conforme disponibilidade; mantenha contagem cumulativa.
  3. Quando finalizar, envie Block End com total_length = bytes_enviados do total; feche o stream.

Passo a passo (receptor)

  1. Ao receber Message, verifique políticas; se não permitir comprimento indefinido, responda Refuse com POLICY_UNDEFINED_LENGTH.
  2. Agregue os Blocks.
  3. Em Block End, compare total_length com o acumulado e libere à aplicação.

Erros & políticas

  • Ausência de Block End ⇒ descarte e marque cancelada.
  • Exigência de tamanho prévioRefuse com POLICY_UNDEFINED_LENGTH (e opcional retry_after).
  • Estouro de cotaTOO_LARGE ou CAPACITY_LIMIT.

Métricas sugeridas

  • maop_streaming_sessions_open, maop_blocks_rx_bytes_total
  • maop_streaming_duration_ms{p50,p95,p99}
  • maop_denies_policy_undefined_length_total
Boas práticas: imponha idle timeout de produção (sem novos blocos por X segundos ⇒ encerrar) e teto para 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_ms negociado/padrão.
  • Semântica: payload_len da 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

  1. Cliente abre stream bidirecional e envia Request com payload_len = 0 (sem Block).
  2. Servidor avalia e envia Proceed. Processa a requisição.
  3. Servidor envia Response com response_payload_len = M (0 se sem corpo).
  4. Se M > 0, envia Block(s) seguido de Block End. Encerra o stream.
  5. Cliente valida e consome a resposta.

Erros & timeouts

  • Sem resposta no tempo: destinatário envia Fail com TIMEOUT; receptário fecha stream.
  • Falha de execução: Fail com error_code (u16) e reason sanitizado.
  • 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
Dica de design: padronize códigos (u16) e anatomia das respostas para múltiplos handlers. Isso simplifica SDKs e painéis.

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, MESSAGE ou REQUEST.
  • 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) ou REQUEST (0x01).

Operações de Pool Global na stream (código 10: ILLEGAL_STREAM)

  • Descrição: Emitir PROCEED, REFUSE, DONE, FAIL, DISCONNECT ou DISCONNECT_REQUEST dentro 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 por MESSAGE ou que ainda não foi classificada como REQUEST.
  • Correção: Só enviar RESPONSE no mesmo stream que recebeu uma REQUEST válida.

BLOCK/BLOCK_END fora de contexto (código 10: ILLEGAL_STREAM)

  • Descrição: Emitir BLOCK ou BLOCK_END em uma stream sem transferência ativa (sem MESSAGE ou REQUEST/RESPONSE que as habilite).
  • Correção: Garantir que a transferência esteja “armada”: cabeçalho enviado, PROCEED recebido e sem BLOCK_END anterior.

Transferência sem PROCEED (código 1: UNAUTHORIZED)

  • Descrição: Enviar BLOCK ou BLOCK_END antes do PROCEED correspondente.
  • Correção: Aguardar autorização explícita via PROCEED antes 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 dos BLOCKs não bate exatamente.
  • Correção: Emitir BLOCKs cuja soma seja idêntica ao valor anunciado; validar antes de BLOCK_END.

Falta de BLOCK_END (código 7: ORDER_VIOLATION)

  • Descrição: Encerrar a direção de envio/fechar a stream sem emitir BLOCK_END quando 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_len0xFFFFFFFFFFFFFFFF, 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, enviar DONE em vez de RESPONSE no mesmo stream.
  • Correção: REQUEST bem-sucedida sempre resulta em RESPONSE; erros usam FAIL (no Pool Global).

response_msg_id não honrado (código 8: ID_MISMATCH)

  • Descrição: A resposta não corresponde ao response_msg_id anunciado na requisição.
  • Correção: Validar suporte local ao response_msg_id e, se ausente, retornar FAIL imediato (antes da execução).

PROCEED/REFUSE duplicados (código 6: DUPLICATE_OPERATION)

  • Descrição: Emitir múltiplos PROCEED ou alternar PROCEED/REFUSE para a mesma operação.
  • Correção: Cada transferência aceita exatamente um PROCEED ou um REFUSE.

Estouro de timeout de resposta (código 5: TIMEOUT)

  • Descrição: Não iniciar o envio da RESPONSE dentro de timeout_ms após o término da requisição.
  • Correção: Cronometrar a partir do BLOCK_END (ou payload_len==0) e emitir FAIL com 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_END da 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 priority na ordenação de execução/stream quando o runtime suporta mapeamento.
  • Correção: Propagar priority para 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) com type != 0 e ainda assim tentar executar.
  • Correção: Validar type; para valores diferentes de 0, retornar FAIL com 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 UNAUTHORIZED quando ausente/inválido.

Reuso incorreto de identificadores (código 6: DUPLICATE_OPERATION)

  • Descrição: Reaproveitar msg_id para uma nova operação sem janela de proteção contra reexecução.
  • Correção: Implementar janela deslizante para msg_id e rejeitar reusos recentes.

Direcionalidade incorreta (código 12: PROTOCOL_VIOLATION)

  • Descrição: Enviar MESSAGE ou REQUEST/RESPONSE em 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 FAIL dentro do stream em vez de no Pool Global, ou publicar FAIL após já ter emitido uma RESPONSE.
  • 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 PROCEED depois que a contraparte já encerrou a transferência (BLOCK_END ou 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 onde exec_time_ms < 0, exec_start_ms no 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) em Message e Request para facilitar quotas.
  • Regras de encerramento: definição de que streams abortados antes do Block End invalidam transferências, mesmo após confirmação.
  • Resposta estruturada: Response passa a declarar response_payload_len e 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.