Skip to Content
Guia do DesenvolvedorModo Daemon (Aprofundamento para Desenvolvedores)Modelo de Autenticação e Segurança

Modelo de Autenticação e Segurança

Visão Geral

O qwen serve é um daemon local por padrão e uma superfície exposta em configuração incorreta. Seu modelo de segurança é em camadas para que uma configuração errada falhe de forma segura:

  1. Bind — um bind fora do loopback sempre carrega um bearer: o do operador, ou um efêmero de 128 bits gerado e impresso uma única vez na inicialização. A inicialização só recusa quando uma fonte de token fornecida está explicitamente vazia/com apenas espaços, ou quando um localhost solicitado resolve para fora do loopback sem nenhuma fonte de token resolvida (o que nunca gera).
  2. Autenticação Bearer — o middleware bearerAuth com comparação SHA-256 em tempo constante protege as rotas normais da API, exceto /health em um bind de loopback comum (require_auth move esse endpoint para trás do bearer também). A entrada de webhook de canal é uma rota pré-bearer separada autenticada por x-qwen-webhook-secret. As rotas de documento e ativos do Web Shell permanecem pré-auth em qualquer modo.
  3. Lista de permissão do cabeçalho Host — no loopback, apenas localhost, 127.0.0.1, [::1], host.docker.internal ou o endereço de loopback vinculado exato (mais porta) são aceitos; as formas sem porta correspondentes também são aceitas ao escutar na 80 ou 443. A allowlist defende contra DNS rebinding. O listener LAN do Local Control é a exceção que sempre impõe sua verificação de Host por autoridade anunciada, qualquer que seja o bind primário.
  4. Controle de Origin — o app de runtime sempre instala allowOriginCors sobre uma allowlist mutável (MutableOriginAllowlist): as entradas --allow-origin <pattern> a semeiam, e o Local Control adiciona o origin da LAN enquanto ativo. Origins não correspondentes recebem o envelope de negação 403. A muralha de negação incondicional (denyBrowserOriginCors) sobrevive apenas no app de bootstrap que responde antes do runtime iniciar.
  5. Portão de mutação por rota — rotas estritas exigem autoridade do operador. Um listener primário de loopback sem token é confiado; requisições autenticadas por bearer e requisições pareadas do Local Control também se qualificam. Uma requisição primária sem token que alcança este portão sem autoridade confiável recebe o erro distinto code: 'token_required'. Credenciais configuradas ausentes ou inválidas e credenciais do Local Control não pareadas são rejeitadas antes pelo middleware bearer com escopo de listener com 401 Unauthorized simples.
  6. Autenticação via device-flow — superfície OAuth separada para provedores (POST /workspace/auth/device-flow + GET/DELETE em /:id).

Este documento percorre cada camada e as invariantes explícitas que o caminho de inicialização impõe.

Responsabilidades

  • Recusar iniciar em configurações inseguras.
  • Bloquear requisições normais da API através do bearer quando configurado, sujeito à isenção do /health no loopback; manter a entrada de webhook de canal atrás de seu gate independente de segredo compartilhado, e manter as verificações de Host de loopback e Origin de navegador antes das rotas autenticadas e isentas.
  • Fornecer um portão de mutação por rota que as rotas da Wave 4 podem ativar.
  • Hospedar o registro de device-flow que conduz os fluxos OAuth dos provedores, visíveis por meio de eventos SSE.

Arquitetura

Regras de recusa na inicialização

Em run-qwen-serve.ts:

// A non-loopback bind with neither --token nor QWEN_SERVER_TOKEN first // generates an ephemeral 128-bit base64url bearer (16 random bytes, 22 // URL-safe characters; printed once at startup, rotated per process). The // first refusal below therefore fires only when a token source was supplied // but is explicitly empty/whitespace, or when the requested hostname resolves // off-loopback (localhost pinned to a non-loopback address never generates). // Generation is loopback-suppressed, so the second refusal is a loopback-only // fail-fast: on a non-loopback bind the generated token already satisfies // --require-auth. if (!isLoopbackBind(opts.hostname) && !token) { throw new Error('Refusing to bind <host>:<port> without a bearer token. ...'); } if (opts.requireAuth && !token) { throw new Error( 'Refusing to start with --require-auth set but no bearer token configured. ...', ); }

Configuração de allow-origin sem token é limitada a origins HTTP(S) de loopback; entradas não-HTTP(S) mantêm seu tratamento existente:

const parsed = parseAllowOriginPatterns(opts.allowOrigins); if (parsed.allowAny && !token) { throw new Error( "Refusing to start with --allow-origin '*' but no bearer token configured. ...", ); } if (findNonLoopbackHttpOrigin(parsed) && !token) { throw new Error( 'Refusing to start with a non-loopback HTTP(S) --allow-origin but no bearer token configured. ...', ); }

Estas recusas são falhas explícitas de inicialização (visíveis em stderr / lançadas para o embedder), nunca silenciosas. O modelo de ameaça do #3803 proíbe explicitamente deixar um daemon se ligar além do loopback sem proteção.

runQwenServe() resolve localhost uma vez, fixa o listener nesse endereço e verifica o endereço real do listener antes de publicar autoridade de loopback confiável; se o resultado estiver fora de 127.0.0.0/8 ou ::1, a inicialização sem token falha e fecha o listener. createServeApp() não possui um socket, então seu chamador continua responsável por garantir que um hostname de loopback declarado seja vinculado apenas ao loopback. Um embed não-loopback declarado mantém rotas estritas, session shell e material de pareamento do Local Control em fail closed. Também rejeita requireAuth: true sem um token não vazio na construção para que rotas não estritas não possam acidentalmente permanecer abertas sob uma configuração endurecida inválida.

Cadeia de middlewares (ordem das requisições HTTP)

mutationGate é uma fábrica de middlewares por rota (createMutationGate retorna mutate()); as rotas chamam mutate() ou mutate({strict: true}) no momento do registro. Não é um middleware global app.use(). O log de acesso e a captura de trace-id de entrada são registrados antes da muralha de origin e da verificação de credencial de mesma origem, então esses curtos-circuitos 403/401 são registrados como qualquer outra rejeição e a linha de log ainda se junta ao trace id do chamador; ambos também precedem bearerAuth, então rejeições 401 ainda são registradas. A allowlist de Host de loopback é registrada antes da rota de health pré-auth para que a defesa contra DNS rebinding a cubra. O /health pré-auth fica abaixo da muralha de origin (sondagens cross-origin correspondidas carregam cabeçalhos CORS); o log de acesso isenta GET /health e POST */heartbeat por caminho antes de anexar seu finish logger, então sondagens exatas GET /health e POST */heartbeat permanecem sem log em qualquer posição de montagem e rejeições da muralha nesses caminhos isentos também ficam sem log (HEAD /health e GET /health/ são registrados como qualquer requisição). O rate limiting normal da API executa depois de bearerAuth e antes de express.json(), para que apenas requisições autenticadas sejam contabilizadas e corpos grandes sejam rejeitados antes do parsing quando um limite é excedido. A entrada de webhook de canal faz branch antes do bearer auth e aplica sua própria verificação de segredo compartilhado, verificação de rate de tier de mutação e parser de 1 MiB.

A superfície de mesma origem consiste em dois middlewares em server/self-origin.ts, montados separadamente: a remoção de Origin apenas para loopback é instalada antes do log de acesso (apenas remove um Origin correspondente, nunca rejeita), e installRemoteSelfOriginMiddleware executa imediatamente antes de allowOriginCors em um listener primário fora do loopback com um token configurado. O remoto corresponde um Origin canônico contra o esquema direto do socket mais a autoridade Host normalizada — cabeçalhos forwarded nunca são consultados — e autentica com bearer a correspondência antes de remover Origin, então as mutações HTTP de mesma origem do Web Shell integrado não precisam de --allow-origin. Seu predicado pré-auth (web-shell-preauth.ts) isenta os pontos de entrada do shell (/, //, /assets*, /mcp-app-sandbox, navegações exatas de documento /session/:id) da verificação de credencial porque fetches de module-script do navegador carregam Origin sem Authorization. Upgrades de WebSocket e origins https de proxy TLS frontal não são cobertos: o gate de upgrade mantém sua própria política CSWSH (origin de loopback, entrada --allow-origin, ou o próprio origin do listener do Local Control), e o origin https de um proxy nunca pode corresponder ao esquema do socket simples.

bearerAuth

  • Nenhum token configurado → o middleware é um no-op (padrão de desenvolvimento em loopback). Exceção: o listener LAN do Local Control tem escopo de listener e sempre exige sua credencial pareada (CredentialStore.isOpen nunca é true para local-control), então nunca está aberto mesmo em um daemon sem token.
  • Token configurado → calcula SHA-256 do token configurado uma vez na construção; em cada requisição, calcula o hash do candidato e compara com timingSafeEqual. Sem short-circuit de comparação de strings; sem vazamento de tempo.
  • Parsing do esquema: Bearer case-insensitive conforme RFC 7235 §2.1; tolerante a SP\tHTAB entre esquema e credenciais conforme RFC 7230 §3.2.6 BWS; rejeita HTAB puro como separador.
  • Hardening CodeQL: parsing manual com indexOf em vez de regex com \s+ / .+ sobrepostos (sem risco de regex polinomial).

hostAllowlist

Apenas loopback. Mantém um Set<string> indexado por porta. Hosts permitidos:

  • localhost:<port>, 127.0.0.1:<port>, [::1]:<port>, host.docker.internal:<port> e o endereço de loopback vinculado exato com a mesma porta. A última forma cobre o intervalo completo de loopback IPv4 suportado (127.0.0.0/8) sem admitir Hosts não relacionados.
  • Além disso, as formas sem porta correspondentes apenas quando vinculado à porta 80 ou 443 (conforme RFC 7230 §5.4 omissão de porta padrão).

A comparação de Host é case-insensitive — o Express normaliza nomes de cabeçalho, mas não valores, então proxies Docker que capitalizam Hosts (Localhost:4170, HOST.docker.internal) receberiam 403 com uma comparação exata de string.

Binds fora do loopback ignoram o portão primário (o operador escolheu a superfície de exposição; o token bearer protege contra spoofing de Host). O listener LAN do Local Control é a exceção: sempre impõe sua verificação de Host por autoridade anunciada, qualquer que seja o bind primário.

denyBrowserOriginCors (apenas app de bootstrap)

Rejeita qualquer requisição com cabeçalho Origin. CLI/SDK nunca definem Origin; apenas navegadores o fazem. Retorna 403 { error: 'Request denied by CORS policy' } deterministicamente, em vez do 500 HTML que o callback de erro do pacote cors produziria. O app de runtime não instala mais esta muralha — ele executa allowOriginCors sobre a allowlist mutável (abaixo); o comportamento de negação sobrevive lá como o branch de origin não correspondido. A muralha permanece no app de bootstrap (run-qwen-serve.ts) que serve requisições antes do runtime iniciar.

Exceção: as XHRs de mesma origem do Web Shell em um bind de loopback são tratadas por um middleware separado (em server/self-origin.ts) que remove Origin quando coincide com um dos self-origins canônicos de loopback (127.0.0.1, localhost, [::1], host.docker.internal) ou o endereço de loopback vinculado exato. Origins sem porta com esquema correspondente são aceitos apenas para sua porta padrão (http na 80, https na 443). Em binds fora do loopback, um segundo middleware (installRemoteSelfOriginMiddleware, no mesmo arquivo) cobre as XHRs do shell: ele autentica com bearer uma requisição cujo Origin canônico é igual ao esquema direto do socket mais a autoridade Host normalizada — cabeçalhos forwarded nunca são confiados — e remove esse Origin antes da muralha, então essas requisições não precisam de uma entrada --allow-origin. Origins cross-origin e null permanecem rejeitados, e as rotas de upgrade de WebSocket mais um origin https de proxy TLS frontal ainda exigem uma (veja Cadeia de middlewares).

allowOriginCors (app de runtime, sempre instalado)

O app de runtime instala allowOriginCors(originAllowlist) incondicionalmente; a allowlist é uma MutableOriginAllowlist semeada pelas entradas --allow-origin <pattern> (possivelmente nenhuma) e estendida em runtime enquanto o Local Control está ativo (o origin da LAN é adicionado/removido com o listener):

  • Valores de Origin correspondentes recebem Access-Control-Allow-Origin, Access-Control-Allow-Headers e Access-Control-Allow-Methods; o preflight OPTIONS retorna 204.
  • Valores de Origin não correspondentes recebem o mesmo 403 { error: 'Request denied by CORS policy' } determinístico do modo de negação.
  • --allow-origin '*' exige um token bearer; em binds de loopback a inicialização recusa quando nenhum está configurado, enquanto em binds fora do loopback o token efêmero gerado satisfaz a guarda (a recusa é apenas no loopback).
  • Sem um token, valores HTTP(S) de --allow-origin são limitados a hosts de loopback em binds de loopback; em binds fora do loopback o token gerado satisfaz a mesma guarda. Um origin de navegador fora do loopback autentica com o bearer em toda rota da API porque caso contrário poderia exercer a API completa do operador, incluindo execução de código como o usuário do daemon; as exceções pré-auth são as rotas de documento/ativo do Web Shell e o sandbox do MCP App (/, /assets*, /mcp-app-sandbox, navegações exatas /session/:id), mais a entrada de webhook de canal, que faz branch antes de bearerAuth e autentica com seu próprio x-qwen-webhook-secret em vez do bearer, e — apenas em binds de loopback — /health (protegido por bearer em outros lugares).
  • Origins de extensão de navegador explícitos mantêm seu caminho de automação local sem token. Logs de inicialização indicam que qualquer origin de navegador permitido sem token recebe autoridade completa do operador.
  • parseAllowOriginPatterns() valida a sintaxe dos padrões na inicialização.
  • A tag de capability allow_origin é anunciada apenas quando este modo está configurado.

createMutationGate

Portão opt-in por rota. Matriz de comportamento:

Autoridade do daemon/requisiçãoOpções da rotaResultado
token configuradoqualquerpassthrough¹
listener primário trusted-loopbackqualquerpassthrough
listener pareado do Local Controlstrict: truepassthrough
requisição primária sem token sem autoridade trusted-loopbackstrict: true401 { code: 'token_required' }
qualquer deployment sem tokenstrict: falsepassthrough

¹ Qualquer configuração de token faz o bearerAuth global impor autenticação bearer antes do portão nas rotas normais da API, exceto /health no loopback a menos que --require-auth esteja definido. A entrada de webhook de canal autentica com seu próprio segredo compartilhado antes deste middleware. O portão é redundante, mas inofensivo nas rotas que protege. --require-auth não é em si autenticação e é válido apenas com um token.

O modo trusted-loopback é derivado uma vez de loopback bind && no configured token && !requireAuth. Ele autoriza apenas requisições que chegam pelo listener primário. Não marca o marcador interno de autenticação bearer, então credenciais de listener e autoridade de deployment permanecem fatos distintos. O formato code: 'token_required' permanece para daemons mais antigos e embeds não confiáveis sem token cujas requisições alcançam o portão estrito, para que clientes SDK possam exibir uma dica de configuração em vez de um 401 genérico. Falhas de credencial de token configurado e do Local Control mantêm a resposta anterior 401 Unauthorized simples.

As respostas de status e habilitação do Local Control expõem sua URL de pareamento e QR apenas para chamadores com autoridade do operador: chamadores confiáveis do listener primário, chamadores primários autenticados por bearer e clientes LAN já pareados. Chamadores LAN não pareados e embeds não confiáveis não podem recuperá-la. A habilitação ainda exige o listener primário; clientes LAN podem acessar após o pareamento ou solicitar desabilitação sob as regras existentes.

Rotas estritas da Wave 4+: /workspace/memory, /workspace/agents/*, /workspace/agents/generate, /file/write, /file/edit, /workspace/tools/:name/enable, /workspace/mcp/:server/restart, /workspace/mcp/:server/{enable,disable,authenticate,clear-auth}, /workspace/mcp/servers (POST/DELETE), /workspace/auth/device-flow, /workspace/init, /session/:id/approval-mode, /session/:id/rewind e /session/:id/shell.

O rewind permanece apenas REST no SDK TypeScript mesmo quando um transporte ACP está configurado. Isso preserva o portão de mutação estrito e os cabeçalhos de identidade bearer/cliente; a tabela de rotas ACP intencionalmente não possui mapeamento de rewind. O roteamento de proprietário também reverifica a confiança do workspace antes que o rewind ou o shell alcancem uma ponte de runtime secundária. IDs de sessão ativa duplicados falham de forma segura como ambiguous_session_owner em vez de fazer fallback para o runtime primário.

Isenção do /health

Em binds de loopback, /health é registrado antes do middleware bearer para que sondas de liveness dentro do pod não precisem portar o token. Binds fora do loopback protegem /health com bearer como qualquer outra rota. --require-auth remove a isenção: /health exige Authorization: Bearer <token> mesmo no loopback. A entrada de webhook de canal permanece fora do bearer auth em qualquer modo e exige seu próprio x-qwen-webhook-secret.

Identidade de cliente v1 (X-Qwen-Client-Id) é auto-declarada

O daemon valida apenas o formato de X-Qwen-Client-Id ([A-Za-z0-9._:-]{1,128}) e rastreia os IDs de cliente conectados por sessão. Atualmente não realiza prova de posse. Um cliente que observar originatorClientId no SSE pode registrar novamente o mesmo ID e se passar por esse originador em requisições posteriores.

Impacto:

  • designated — um chamador remoto pode se passar pelo originador e votar em uma requisição destinada apenas ao originador do prompt.
  • consensus — se o ID falsificado já estava no snapshot votersAtIssue, ele pode votar.
  • local-only não é afetado porque usa fromLoopback como portão, que o daemon preenche a partir do endereço remoto da conexão.
  • first-responder não é afetado porque é agnóstico em relação à identidade.

Um mecanismo futuro de pair-token emitirá um segredo por sessão a partir de POST /session; votos designated / consensus precisarão apresentá-lo. Até lá, deployments que precisam de uma política designated reforçada devem usar bind em loopback ou executar atrás de um proxy reverso autenticado. Veja 04-permission-mediation.md para detalhes no nível de política.

Autenticação via device-flow

Superfície OAuth separada para autenticação de provedores. O identificador de provedor v1 é qwen-oauth, mas o tier gratuito do Qwen OAuth foi descontinuado em 2026-04-15; novas configurações devem usar um provedor de autenticação atualmente suportado quando disponível.

  • POST /workspace/auth/device-flow — inicia um fluxo; retorna {deviceFlowId, providerId, expiresAt, verificationUrl, userCode}.
  • GET /workspace/auth/device-flow/:id — consulta o estado.
  • DELETE /workspace/auth/device-flow/:id — cancela.
  • GET /workspace/auth/status — snapshot da conta / provedor atual.

Os eventos SSE auth_device_flow_{started, throttled, authorized, failed, cancelled} distribuem o estado do fluxo para todos os assinantes, mantendo interfaces multi-cliente sincronizadas. Veja 09-event-schema.md.

Implementação: packages/cli/src/serve/auth/device-flow.ts + qwen-device-flow-provider.ts.

Defesa contra injeção de log / Trojan Source: sanitizeForStderr(value) (device-flow.ts) substitui caracteres de controle ASCII e caracteres de controle Unicode por ?. Um IdP malicioso poderia forjar linhas de log ou ocultar payloads:

IntervaloMotivo da remoção
\x00–\x1f, \x7f, \x80–\x9fControles ASCII C0 / DEL / C1, escapes de terminal e falsificação de linhas de log.
U+200B-U+200FCaracteres de largura zero mais LRM / RLM; invisíveis, mas podem alterar a renderização do terminal.
U+2028-U+2029LINE / PARAGRAPH SEPARATOR; muitos terminais com suporte a Unicode os tratam como quebras de linha.
U+202A-U+202EControles de EMBEDDING / OVERRIDE bidirecionais.
U+2066-U+2069Controles de ISOLATE bidirecionais (LRI / RLI / FSI / PDI), o principal vetor do CVE-2021-42574 “Trojan Source” . Um IdP usando U+2066 (LRI) em vez de U+202D (LRO) pode contornar filtros apenas de EMBEDDING/OVERRIDE com reordenação visual similar.
U+FEFFBOM / zero-width no-break space.

O comprimento é preservado substituindo cada code point removido por ? em vez de deletá-lo, para que operadores ainda possam ver que algo estava presente naquele índice. Ambas as camadas usam o sanitizador: qwenDeviceFlowProvider sanitiza oauthError do IdP, e o observador de late-poll do registro sanitiza valores controlados pelo provedor interpolados em hints de auditoria (latePollResult.kind / lateErr.name).

A tag de capability auth_device_flow é anunciada incondicionalmente; as próprias rotas retornam 400 unsupported_provider se o daemon não puder atender a um provedor específico. A lista de provedores suportados está em /workspace/auth/status em vez de /capabilities para manter o formato do descritor uniforme.

Fluxo de Trabalho

Requisição bem-sucedida com autenticação bearer

Modos de falha da autenticação bearer

Todos retornam 401 { error: 'Unauthorized' } (uniforme entre missing header / wrong scheme / wrong token para que sondagens não consigam distinguir).

Sombra do --require-auth

Após autenticar, caps.features.includes('require_auth') confirma que o deployment está reforçado.

Mutação estrita no loopback confiável

Estado e Ciclo de Vida

  • O token bearer é lido na inicialização e sofre trim (newlines de cat token.txt quebrariam a comparação silenciosamente).
  • O modo --open-with-auth (exclusivo da CLI) executa antes da inicialização: após verificações determinísticas de loopback/Web Shell, aplica a mesma seleção de opção sobre ambiente e preenche ServeOptions.token com 32 bytes aleatórios codificados em base64url somente quando nenhum token selecionado não vazio existe. A credencial gerada tem tempo de vida do processo, não é escrita em process.env nem persistida pelo daemon, e chega ao navegador através do fragmento de URL existente. O Web Shell retém sua cópia no navegador em sessionStorage por aba. --open simples e chamadores diretos de runQwenServe() nunca a geram.
  • O Set de Hosts permitidos é cacheado por porta; reconstruído na mudança de porta (efêmera 0 → porta real após listen).
  • O portão de mutação constrói passthrough e strictDenier uma vez por build do app; a chamada por rota retorna o closure cacheado (sem alocação por requisição).
  • O registro de device-flow é descartado na Fase 1 do shutdown() para que fluxos pendentes resolvam como cancelled antes do teardown HTTP.

Dependências

  • node:cryptocreateHash, timingSafeEqual.
  • packages/cli/src/serve/loopback-binds.tsisLoopbackBind.
  • packages/cli/src/serve/auth/device-flow.ts — máquina de estado do device-flow.
  • @qwen-code/acp-bridge — expõe eventos de device-flow no barramento SSE por sessão.

Configuração

OrigemParâmetroEfeito
EnvQWEN_SERVER_TOKENToken bearer (com trim).
Flag--tokenToken bearer (sobrescreve o env).
CLI flags--open-with-authReutiliza ou gera um bearer do Web Shell no loopback antes da inicialização do daemon.
Flag--require-authEstende bearer para loopback + /health. Inicia apenas com um token.
Flag--hostnameBind fora do loopback sempre carrega um bearer — --token, QWEN_SERVER_TOKEN, ou um efêmero gerado; uma fonte explicitamente vazia recusa.
Flag--allow-origin <pattern>Alterna para modo de lista de permissão CORS. Wildcard e origins HTTP(S) fora do loopback exigem um token.
Tags de capabilityrequire_auth (condicional), auth_device_flow (sempre), allow_origin (condicional)Veja 11-capabilities-versioning.md.

Observações e Limitações Conhecidos

  • --require-auth oculta o preflight de recursos. Clientes não autenticados não podem descobrir a tag require_auth; sua superfície de descoberta é o próprio corpo 401.
  • Ordenação do body-parser no portão de mutação: respostas 401 do mutationGate({strict: true}) são disparadas depois que express.json() faz o parsing do corpo. Pior caso em um listener saturado: --max-connections × express.json({limit: '10mb'}) ≈ 2,5 GB transitórios. Entrypoints de produção fora do loopback já exigem autenticação bearer antes do parser normal da API; a entrada de webhook de canal verifica seu segredo compartilhado antes de seu parser separado de 1 MiB. Embeds diretos não confiáveis são responsáveis por sua exposição de listener.
  • Remoção de Origin de mesma origem em server.ts ocorre antes de allowOriginCors. Se uma mudança futura mover a remoção para outro lugar, o Web Shell quebrará.
  • A comparação de token é sobre o digest SHA-256, não sobre o token bruto. Reduz o vazamento de tempo ao colapsar comparações de token de tamanho variável em uma comparação de digest de tamanho fixo.
  • O daemon não possui mTLS, assinatura de requisições ou prova de posse via pair-token atualmente. --rate-limit fornece rate limiting HTTP por chave de client-id / IP; não é autenticação de identidade de cliente.

Referências

  • packages/cli/src/serve/auth.ts (arquivo inteiro)
  • packages/cli/src/serve/run-qwen-serve.ts (regras de recusa)
  • packages/cli/src/serve/loopback-binds.ts
  • packages/cli/src/serve/auth/device-flow.ts
  • packages/cli/src/serve/auth/qwen-device-flow-provider.ts
  • Modelo de ameaça para usuários: ../../users/qwen-serve.md.
  • Referência do protocolo: ../qwen-serve-protocol.md.
Last updated on