# Implantação de Streamable HTTP
English • Português • 中文
O Winx expõe um conjunto configurável de ferramentas MCP através de um endpoint autenticado **Streamable HTTP** para ChatGPT, agentes hospedados na nuvem, automação remota e clientes que não podem iniciar um processo stdio local. O endpoint é `/mcp`; o listener padrão é `127.0.0.1:8000` e o perfil de ferramentas padrão continua sendo `full`.
Como esse endpoint concede recursos reais de shell e acesso ao sistema de arquivos, o Winx adota a postura de segurança **fail-closed**: exige credenciais fortes, recusa conexões fora do loopback a menos que explicitamente autorizado, impõe limites de custo de requisição e isola rigorosamente cada principal autenticado.
## Visão Geral
| Propriedade | Padrão |
| :--- | :--- |
| Endpoint MCP | `/mcp` |
| Protocolo MCP | `2026-07-28` |
| Listener | `127.0.0.1:8000` |
| Autenticação | `Authorization: Bearer ` |
| Comprimento mínimo do token | 32 bytes |
| Afinidade de sessão remota | `workspace` |
| Limite de corpo da requisição | 64 MiB |
| Timeout de requisição | 120 segundos |
| Concorrência global | 32 requisições simultâneas |
| Rate limit por IP de origem | 120 requisições por minuto |
| Atraso em falha de autenticação | 100 ms |
| TTL de guardian nunca utilizado | 1.800 segundos (30 minutos) |
| TTL de guardian utilizado | 86.400 segundos (24 horas) |
| Quota de guardians ativos | 32 |
O Winx suporta chamadas MCP stateless modernas e o fluxo legado de inicialização/sessão HTTP. As mesmas ferramentas, prompts, recursos, conteúdo estruturado e MCP Tasks estão disponíveis via HTTP e stdio.
## Arquitetura
```text
Cliente MCP Remoto
│ HTTPS + bearer token
▼
Túnel privado / VPN / Proxy reverso HTTPS autenticado
│ loopback HTTP
▼
127.0.0.1:8000/mcp
│
├─ Validação de Host e corpo da requisição
├─ Limites de timeout, taxa e concorrência
├─ Autenticação de principal
├─ Resolução de afinidade de sessão
└─ Isolamento de thread_id / MCP Task
│
▼
WinxService compartilhado
│
▼
winxd (daemon de controle)
│
└─ winx-guardian por sessão lógica
│
└─ PTY real / bash ou zsh / tarefas em primeiro e segundo plano
```
No Linux, macOS e WSL2, o `winx-code-agent` atua apenas como o adaptador MCP. O `winxd` gerencia o plano de controle, enquanto cada `winx-guardian` controla um PTY independente. Encerrar a conexão HTTP ou reiniciar o adaptador não encerra o PTY.
No Windows nativo utiliza-se o runtime embutido (embedded), logo as sessões duram apenas enquanto o processo do servidor estiver ativo. Recomenda-se o WSL2 quando forem necessárias sessões remotas duráveis.
## Início Rápido
Instale os três binários para Unix:
```bash
cargo install winx-code-agent
```
Gere um token forte em um arquivo protegido (apenas leitura do usuário):
```bash
mkdir -p ~/.config
install -m 600 /dev/null ~/.config/winx-http-token
openssl rand -hex 32 > ~/.config/winx-http-token
```
Inicie o Winx no loopback:
```bash
winx-code-agent serve --http \
--bind 127.0.0.1:8000 \
--token-file ~/.config/winx-http-token
```
Configure o cliente com:
```text
URL: http://127.0.0.1:8000/mcp
Authorization: Bearer
```
Clientes na nuvem necessitam de uma URL HTTPS pública. Mantenha o Winx em loopback e coloque um túnel privado, VPN ou proxy reverso HTTPS autenticado à frente. O Winx não realiza terminação TLS diretamente.
O uso de `--token-file` é preferível a `--token`, pois segredos passados por linha de comando podem ser expostos na lista de processos do sistema (`ps`), histórico do shell e logs de automação. A variável de ambiente `WINX_HTTP_TOKEN` permanece disponível como fallback para modo single-principal.
## Coerência da Sessão com o Workspace
O `Initialize` remoto retorna dois valores que formam um único vínculo de sessão:
```text
thread_id + workspace_root
```
Copie os dois valores sem alterações em todas as chamadas stateful seguintes. Antes de selecionar um PTY ou executar
qualquer operação, o Winx valida se a afinidade da thread, a raiz canônica informada e a sessão inicializada combinam.
Vínculos ausentes ou misturados retornam um resultado estruturado `needs_initialize`/`conflict` e não chegam ao shell nem
ao filesystem.
Essa validação **não** restringe os alvos das ferramentas ao `workspace_root`. A raiz identifica o contexto de projeto
que possui terminal, cwd, histórico de leitura e estado de edição. A autoridade de caminhos continua sendo uma política
separada: por exemplo, `WINX_ALLOW_PATHS=/` ainda permite que uma sessão coerente opere em arquivos de apoio fora do
projeto quando o modo ativo autoriza. Assim, trabalhos reais entre diretórios continuam possíveis sem um chat herdar
silenciosamente o terminal de outro projeto.
Para outro projeto, chame `Initialize` com `type="first_call"` e o novo caminho, e passe a usar o novo par retornado.
Chamadas remotas `user_asked_change_workspace` falham de forma segura para que uma chave durável nunca seja redirecionada
para outro projeto no mesmo lugar.
Se um reinício do adapter remover o estado ativo do mesmo projeto, uma chamada posterior de `reset_shell` ou
`user_asked_mode_change` será recuperada com segurança como `first_call`, preservando o par de thread/workspace informado.
A resposta indica `initialize_recovered_missing_session=true`; trocas de workspace continuam falhando de forma segura.
Com estado ativo, solicite `reset_shell` somente depois que `BashCommand` retornar uma falha tipada do runtime de shell.
Um segundo reset dentro de cinco minutos, sem nova evidência de falha, retorna `reset_skipped_healthy` e preserva PTY,
cwd, modo e contexto.
## Afinidade de Sessão
### Afinidade por Workspace (Padrão)
A opção padrão é:
```bash
--session-affinity workspace
```
Para cada chamada remota `Initialize(first_call)`, o Winx deriva a sessão lógica a partir de:
```text
(principal autenticado, workspace canônico)
```
O `thread_id` enviado pelo cliente na primeira chamada não é considerado chave única durável. Variações estéticas geradas por modelos como:
```text
release_02333
release_0_2_333
```
são mapeadas para o mesmo guardian interno quando pertencem ao mesmo principal e workspace. O Winx retorna um identificador externo estável (ex: `ws_project_`) e espera que as chamadas subsequentes utilizem esse ID retornado.
Consequências:
- Reconexões stateless reanexam à sessão existente em vez de criar um novo guardian;
- Primeiras chamadas repetidas preservam o PTY, cwd, journal de saída e comandos em execução;
- Principals diferentes mantêm namespaces totalmente isolados;
- Conversas paralelas do **mesmo principal no mesmo workspace compartilham o mesmo shell** e seu lock de comandos em
foreground; uma segunda tentativa em foreground falha rapidamente com `command_already_running`, sem entrar em uma fila
oculta nem executar mais tarde, enquanto reanexos e sessões de outros projetos continuam responsivos;
- Chamadas sem workspace compartilham uma sessão scratch por principal;
- Retomadas de tarefas são indexadas pelo ID de tarefa salvo.
### Afinidade por Conversa (`conversation`)
Utilize:
```bash
--session-affinity conversation
```
quando conversas paralelas de um mesmo principal precisarem operar no mesmo repositório sem compartilhar o mesmo shell. O Winx deriva a chave a partir de:
```text
(principal autenticado, identidade da conversa, workspace canônico)
```
A ordem de preferência de identidade é:
1. `Mcp-Session-Id`, quando o transporte mantém uma sessão MCP estável;
2. `X-Winx-Conversation-Id`, quando um gateway autenticado injeta um valor estável;
3. O `thread_id` fornecido na primeira chamada;
4. Afinidade por workspace caso nenhuma identidade de conversa seja encontrada.
### Afinidade por Thread (`thread`)
Utilize:
```bash
--session-affinity thread
```
quando o cliente for responsável por gerenciar e manter identificadores de thread estáveis e únicos, controlando a criação e o encerramento explícito de cada sessão.
## Conexão ou Criação (Attach-or-create)
Os guardians com protocolo `1.3+` implementam attach-or-create para `FirstCall`:
1. Se a sessão lógica não existir, cria-se um novo PTY;
2. Se a sessão lógica já existir, retorna-se o snapshot autoritativo atual;
3. O adaptador sincroniza seu estado local a partir do snapshot;
4. O guardian preserva o processo PTY, cwd, modo de segurança, histórico de saída e comandos em execução.
## Múltiplos Principals Autenticados
Gere credenciais independentes para cada cliente ou automação:
```bash
mkdir -p ~/.config
install -m 600 /dev/null ~/.config/winx-chatgpt-token
install -m 600 /dev/null ~/.config/winx-automation-token
openssl rand -hex 32 > ~/.config/winx-chatgpt-token
openssl rand -hex 32 > ~/.config/winx-automation-token
```
Crie o arquivo TOML de configuração:
```toml
# ~/.config/winx-principals.toml
[[principals]]
name = "chatgpt"
token_file = "/home/alice/.config/winx-chatgpt-token"
tool_profile = "coding"
[[principals]]
name = "automacao"
token_file = "/home/alice/.config/winx-automation-token"
tool_profile = "terminal"
[[principals]]
name = "ci"
token_env = "WINX_CI_MCP_TOKEN"
allowed_tools = ["Initialize", "BashCommand", "ReadFiles"]
```
Inicie o servidor:
```bash
chmod 600 ~/.config/winx-principals.toml
winx-code-agent serve --http \
--principal-config ~/.config/winx-principals.toml
```
Regras dos principals:
- Nomes podem conter letras ASCII, dígitos, `_` e `-`;
- Cada entrada deve definir exatamente `token_file` ou `token_env`;
- Nomes, IDs derivados e tokens devem ser exclusivos;
- Arquivos de token devem ser arquivos regulares (sem links simbólicos) com permissão restrita (`0600`);
- Tokens devem ter no mínimo 32 bytes de comprimento;
- `tool_profile` usa `full` por padrão; `allowed_tools`, quando presente, substitui o perfil e não pode ser vazio;
- Os nomes na allowlist diferenciam maiúsculas de minúsculas, e uma ferramenta desconhecida impede a inicialização.
### Perfis do catálogo de ferramentas
Os perfis reduzem o payload de schemas de `tools/list` para clientes que não precisam de todas as capacidades. A mesma
política é validada antes do dispatch. Aliases antigos de edição permanecem chamáveis apenas com a autoridade de mutação
equivalente já concedida, preservando conversas em cache sem ampliar permissões.
| Perfil | Ferramentas anunciadas |
| :--- | :--- |
| `full` | Sete ferramentas: catálogo compacto de código mais `ContextSave` e `ReadImage` (padrão) |
| `coding` | `Initialize`, `BashCommand`, `ReadFiles`, `CodeMap` e `EditFiles` |
| `read-only` | `Initialize`, `ReadFiles`, `ReadImage` e `CodeMap` |
| `terminal` | `Initialize` e `BashCommand` |
Para um servidor de principal único, selecione o perfil na linha de comando:
```bash
winx-code-agent serve --http --token-file ~/.config/winx-http-token \
--tool-profile coding
```
Ou monte um catálogo exato repetindo `--allow-tool`; os nomes explícitos substituem `--tool-profile`:
```bash
winx-code-agent serve --http --token-file ~/.config/winx-http-token \
--allow-tool Initialize --allow-tool BashCommand --allow-tool ReadFiles
```
A política do catálogo não é um sandbox de shell. Qualquer perfil com `BashCommand` mantém os poderes de comando do
modo Winx inicializado e do usuário do sistema operacional.
## Contrato de Orquestração com LLM
O handshake inicial do MCP estabelece um contrato de orquestração sequencial determinístico: inicializar uma vez, manter o `thread_id` retornado, utilizar `CodeMap` antes de leituras extensas, agrupar leituras com `ReadFiles`, ler arquivos antes de editá-los, compor verificações relacionadas e fail-fast com `&&` e nunca repetir chamadas rejeitadas sem alterações. `Initialize` também retorna um diretório limitado `/.winx/tmp/session-…/` para helpers derivados que sejam realmente úteis. Eles permanecem não canônicos, preservam a proveniência de caminho/linha e reutilizam nomes estáveis, sem materializar código-fonte ou saída de comandos apenas para chamar `CodeMap`. Mapas de helpers aceitam um único arquivo existente, limitam o payload de navegação a 12 KiB e permitem 24 arquivos únicos / 64 chamadas por sessão ativa; mapas de fontes canônicas não compartilham essa cota agregada. O armazenamento temporário é limitado a 64 MiB / 128 arquivos por sessão e 256 MiB por workspace, com sessões inativas removidas após 24 horas. Ao atingir 96 arquivos ou 48 MiB, uma sessão ativa também remove, do mais antigo para o mais novo, apenas helpers inativos durante todo o TTL de 24 horas, buscando retornar a 64 arquivos / 32 MiB; symlinks nunca são seguidos e helpers recentes nunca são excluídos automaticamente. Helpers nunca codificam payload nos nomes nem poluem a raiz do projeto com artefatos `.winx-*`/`.winx_tmp`. Todo PTY foreground ou background exporta esse diretório exato como `WINX_TEMP_DIR`; escritas shell com destino estático que tentem contorná-lo são rejeitadas com `temporary_artifact_policy`. O Winx também audita o uso real após cada ação Bash, incluindo escritas dinâmicas: o resultado informa bytes, arquivos e contadores da limpeza de itens antigos. Se a sessão continuar acima da cota, comandos comuns ficam bloqueados até o agente inspecionar e remover explicitamente os helpers obsoletos restantes. Se `Initialize` retornar `initialize_workspace_already_bound` ou `workspace_change_requires_new_session`, a chamada é terminal para aquela conversa: mantenha o par já vinculado ao acessar caminhos absolutos permitidos ou abra outra conversa para um projeto realmente diferente. Uma verificação finita pós-edição pode ser enviada como `verify_command` em `EditFiles`, economizando uma ida e volta entre rede e modelo.
Os arquivos de um lote `ReadFiles` são processados em um pool paralelo limitado (`WINX_READ_PARALLELISM`, padrão `4`, máximo `32`). A resposta e a cobertura do guard rail continuam seguindo exatamente a ordem solicitada.
Cada ferramenta define um `outputSchema` e retorna um envelope `structuredContent`:
```json
{
"status": "needs_read",
"tool": "EditFiles",
"message": "EditFiles failed: ...",
"errorCode": "read_required",
"retryable": true,
"retrySameCall": false,
"nextAction": {
"tool": "ReadFiles",
"instruction": "Perform every required read before retrying the edit.",
"arguments": {
"file_paths": ["/workspace/README.md:231-301"],
"thread_id": "ws_project_hash"
}
},
"requiredReads": [
{ "path": "/workspace/README.md", "ranges": ["231-301"] }
]
}
```
Falhas recuperáveis retornam resposta com sucesso HTTP/JSON-RPC e `isError: true` no protocolo MCP, incluindo a próxima ação corretiva (`nextAction`).
`BashCommand.wait_policy=until_complete` só é válido para um comando finito em foreground. Seu uso com ações de
background, status, entrada, tela ou espera retorna um resultado recuperável com
`errorCode: wait_policy_incompatible_with_action`; `nextAction.arguments` já corrige a política para `return_early`.
O cancelamento de Tasks também é vinculado à geração. Se ele disputar com o lançamento, o Winx aguarda o token exato da
execução ou a confirmação de que nenhum processo iniciou. Um fallback limitado encerra o shell afetado antes de confirmar
o cancelamento, impedindo que um interrupt obsoleto atinja o próximo comando.
`ReadImage` valida o conteúdo como JPEG, PNG, GIF ou WebP em vez de confiar na extensão. A origem tem limite de 50 MiB,
com limites independentes de dimensões e alocação decodificada; a entrega fica limitada a 2 MiB e 2560 px no maior lado,
redimensionando e recomprimindo entradas maiores. Um cache limitado de fingerprints por sessão troca repetições sem
alterações por uma referência estruturada compacta; `force=true` solicita explicitamente o reenvio dos bytes.
`EditFiles` é a única superfície pública sobre o motor tipado de mutações. Seus modos explícitos por arquivo são
`replace`, `search_replace`, `line_patch` e `undo`; uma chamada de edição aceita de um a 100 alvos únicos e valida o lote antes de
gravar. Os nomes antigos permanecem como aliases ocultos e não ampliam a autoridade dos modos equivalentes de `EditFiles`.
Para um arquivo existente, o fluxo determinístico padrão é `ReadFiles` seguido por `line_patch` vinculado à revisão.
`search_replace` continua disponível para ancoragem textual exata e intencional. Após um conflito SEARCH, a recuperação
estruturada exige a ação `ReadFiles` exata e direciona a tentativa corrigida para `line_patch`; uma mutação comum de
código-fonte não deve cair para shell, `sed` ou Python.
`EditFiles` aceita `verify_command` e `verify_wait_for_seconds` opcionais (padrão `15`, máximo
`60`). A verificação só roda depois de um commit bem-sucedido, em foreground e sob a mesma allowlist de modo do
`BashCommand`. Exit code zero conclui o resultado combinado. Um exit code diferente de zero mantém o resultado da edição
em `isError: false`, com `status: completed_with_issues`, `errorCode: verification_failed` e `data.edit_applied: true`.
A ação vinculada ao recibo em `BashCommand` repete somente a verificação depois das correções, nunca a edição. Se a
verificação continuar executando ao final da espera limitada, o resultado fornece a ação `status_check` normal do
`BashCommand`. A verificação exige autoridade para `EditFiles` e `BashCommand`.
Edições confirmadas recebem por 30 minutos um recibo compacto persistido. Chamadas idênticas ficam single-flight e são
reproduzidas sem nova escrita ou verificação enquanto os hashes finais coincidirem. Mudança posterior no alvo retorna
`mutation_postcondition_changed` em vez de sobrescrever estado novo. Três conflitos SEARCH na mesma sessão/alvo escalam
para `recovery_exhausted`, removendo a repetição automática e exigindo mudança de estratégia.
## Ciclo de Vida do Guardian
O `winxd` gerencia o ciclo de vida de todos os guardians conectados ao mesmo socket de controle:
- **Relógio de Atividade Autoritativo:** Os guardians com protocolo 1.3+ registram a hora de criação, a última atividade real do terminal, o timestamp do último comando e se algum comando já foi executado.
- **TTL em Camadas:**
- `WINX_UNUSED_SESSION_IDLE_TTL_SECS=1800` (30 minutos para sessões que nunca executaram comandos);
- `WINX_SESSION_IDLE_TTL_SECS=86400` (24 horas para sessões utilizadas).
- Comandos ativos em primeiro ou segundo plano nunca são finalizados por expiração de tempo.
- **Pressão de Quota:** Sob limite de capacidade, o `winxd` limpa sockets órfãos e descarta o guardian inativo mais antigo que nunca executou comandos antes de recusar uma nova sessão.
## Comandos Operacionais
```bash
# Inspecionar sessões ativas
winx-code-agent list
# Acompanhar a saída de uma sessão em tempo real
winx-code-agent attach --follow
# Aplicar a limpeza com as regras de TTL padrão
winx-code-agent prune
# Limpar todas as sessões inativas preservando processos ativos
winx-code-agent prune --idle-seconds 0
# Encerrar uma sessão específica ou todas
winx-code-agent kill
winx-code-agent kill --all
# Reiniciar o daemon de controle mantendo os guardians e PTYs vivos
winx-code-agent restart-daemon
# Gerar relatório de diagnóstico sanitizado
winx-code-agent doctor
```
## Telemetria de Uso Persistente
Para registrar eventos estruturados `winx::usage` em formato JSONL de maneira não-bloqueante:
```bash
WINX_USAGE_LOG="$HOME/.local/state/winx/usage.jsonl" \
WINX_USAGE_LOG_ROTATION=daily \
WINX_USAGE_LOG_KEEP_DAYS=7 \
winx-code-agent serve --http --token-file ~/.config/winx-http-token
```
Nenhum comando, conteúdo de arquivo, saída de ferramenta ou credencial é gravado nesse log de telemetria; gravam-se apenas durações, status de resultado, tamanhos de resposta, quantidade de itens do lote, limite de workers e metadados de protocolo. O `request_id` correlaciona os eventos `tool_call` e `http_request`, permitindo separar tempo da ferramenta de overhead do transporte sem expor o payload.
Uma visão rápida de latência por ferramenta pode ser gerada com:
```bash
jq -s '
def pct($p): sort | .[((length - 1) * $p | floor)];
[.[] | select(.fields.event == "tool_call") | .fields]
| group_by(.tool)
| map(. as $calls | {
tool: $calls[0].tool,
calls: ($calls | length),
p50_ms: ([$calls[].duration_ms] | pct(0.50)),
p95_ms: ([$calls[].duration_ms] | pct(0.95))
})
' ~/.local/state/winx/usage.jsonl*
```
## Exposição de Rede
### Recomendado
Mantenha o listener em loopback (`127.0.0.1:8000`) e utilize:
- VPN privada (WireGuard, Tailscale);
- Túnel MCP outbound-only (como o Secure MCP Tunnel da OpenAI);
- Proxy reverso HTTPS autenticado.
Se o proxy encaminhar um cabeçalho `Host` específico, informe-o via `--allowed-host`:
```bash
winx-code-agent serve --http \
--token-file ~/.config/winx-http-token \
--allowed-host mcp.exemplo.com
```
### Ligação Direta Externa
Caso deseje expor o socket de rede diretamente (não recomendado para produção sem proxy/TLS):
```bash
winx-code-agent serve --http \
--bind 192.168.1.20:8000 \
--allow-non-loopback \
--token-file ~/.config/winx-http-token
```
## Limites de Recursos e Respostas
| Condição | Resposta | Detalhes |
| :--- | :--- | :--- |
| Token ausente ou inválido | `401 Unauthorized` | Resposta atrasada em 100 ms |
| Limite de requisições por IP excedido | `429 Too Many Requests` | Inclui cabeçalho `Retry-After: 1` |
| Concorrência global esgotada | `503 Service Unavailable` | Inclui cabeçalho `Retry-After: 1` |
| Requisição exceder 120 segundos | `408 Request Timeout` | A requisição é encerrada |
| Corpo maior que 64 MiB | `413 Payload Too Large` | Rejeitado antes do dispatch |
## Referência da CLI
| Opção | Finalidade |
| :--- | :--- |
| `serve --http` | Inicia o transporte Streamable HTTP |
| `--bind ` | Endereço de escuta (padrão: `127.0.0.1:8000`) |
| `--token-file ` | Caminho para o arquivo contendo o Bearer Token |
| `--principal-config ` | Caminho para o arquivo TOML com múltiplos principals |
| `--token ` | Token direto via CLI (visível em `ps`) |
| `--tool-profile ` | Catálogo do principal único: `full`, `coding`, `read-only` ou `terminal` |
| `--allow-tool ` | Monta um catálogo exato do principal único; opção repetível |
| `--session-affinity ` | Modo de afinidade: `workspace`, `conversation` ou `thread` |
| `--allow-weak-token` | Permite tokens com menos de 32 bytes (apenas testes) |
| `--allow-non-loopback` | Permite bind em interfaces não-loopback |
| `--allowed-host ` | Autoriza cabeçalhos `Host` adicionais |
| `--allow-query-token` | Permite passar o token via `?token=...` na URL |
| `--max-concurrency ` | Limite global de requisições concorrentes (padrão: 32) |
| `--requests-per-minute ` | Limite de requisições por IP (padrão: 120) |
## Limite de Segurança
Um principal autenticado executa comandos e acessa arquivos com os privilégios do usuário do sistema operacional que iniciou o servidor.
- O modo `wcgw` concede acesso total ao shell e sistema de arquivos;
- O modo `architect` limita a sessão a operações de leitura;
- O modo `code_writer` restringe a comandos e caminhos autorizados;
- A redação de segredos está sempre ativa por padrão;
- O sandbox Landlock (`WINX_SANDBOX=1`) adiciona proteção de kernel no Linux.
Consulte [SECURITY.md](../SECURITY.md) para detalhes adicionais de segurança.