Ponto de Entrada da Facade
O módulo de backend wippy/facade é o ponto de entrada que entrega o Web Host aos usuários. Ele serve uma página HTML que carrega o módulo JS do Web Host, trata redirecionamentos de autenticação, expõe um endpoint /facade/config e leva a configuração específica do deployment para o bundle de frontend hospedado em CDN. Nenhuma configuração é gravada no próprio bundle — cada deployment fornece sua própria configuração através desse mecanismo.
A Página HTML
Quando um usuário navega até uma aplicação Wippy, o wippy/facade serve uma página HTML. Essa página é fina: ela carrega um módulo JS do Web Host da CDN e inicializa o host com a configuração retornada por /facade/config. O módulo assume a página inteira — incluindo seu histórico de navegador — de modo que o host roda como a aplicação toda, e não dentro de um iframe.
A facade carrega uma de duas entradas de módulo JS, dependendo do fe_mode configurado:
module.js— o shell compat (padrão): o layout padrão de barra de navegação + área de página + painel direito de chat.managed-layout.js— o shell managed (adesão opcional, acesso antecipado): o layout multi-painel declarativo.
Uma versão simplificada da página é assim:
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<div id="app"></div>
<script src="https://web-host.wippy.ai/<release-tag>/module.js"></script>
<script>
fetch('/facade/config')
.then(r => r.json())
.then(config => {
window.initWippyApp(config, '#app')
})
</script>
</body>
</html>
A página busca sua configuração e a entrega à função de init do módulo. O host monta na página, assume o roteamento e o histórico do navegador, e prossegue com a inicialização completa.
Nota sobre o caminho do fetch.
/facade/configé o caminho que a facade registra no router público; a URL efetivamente buscada pela sua página inclui o prefixo desse router. Com o prefixo de exemplo/api/public, ela é/api/public/facade/config— exatamente o que a página de facade entregue busca. Os trechosfetch('/facade/config')aqui estão encurtados para facilitar a leitura.
O Fluxo de Configuração
O fluxo de configuração tem duas etapas:
- O JavaScript inline da página chama
GET /facade/configna mesma origem da página. Esse endpoint é registrado pelowippy/facadeno router público. - Ao receber a resposta, a página passa o objeto de configuração completo à função de init do módulo JS carregado (
window.initWippyApp(config, rootContainer?)).
O Web Host extrai o payload AppConfig do objeto de configuração e prossegue com a inicialização completa. A partir desse ponto, o script da página é passivo — toda interação do usuário acontece dentro do host montado.
Esse padrão significa que o bundle hospedado em CDN nunca contém URLs, tokens ou branding específicos do deployment. O bundle é idêntico para todo deployment. Apenas o payload de configuração difere.
Campos de shell vs
AppConfigdo filho. A resposta de/facade/configcarrega os dois. Campos comofacade_url,iframe_origin,iframe_urlelogin_pathsão campos de nível de shell, consumidos pela página de embedding para construir a si mesma — eles não fazem parte doAppConfigdo filho. OAppConfigcom o qual o host de fato inicializa éauth,env,theming,hostConfig,contexte os demais campos documentados abaixo.
A Resposta de /facade/config
O endpoint de configuração retorna um objeto JSON contendo tanto os campos de nível de shell quanto o AppConfig do filho. A página da facade o passa à função de init do módulo do host; um embedding manual em iframe entrega, em vez disso, a porção AppConfig via PostMessage (veja abaixo). Todos os campos são montados pelo wippy/facade a partir dos parâmetros do módulo e do ambiente em execução:
{
"$schema": "wippy-context-2.0",
"facade_url": "https://web-host.wippy.ai/<release-tag>",
"iframe_origin": "https://web-host.wippy.ai",
"iframe_url": "https://web-host.wippy.ai/<release-tag>/iframe.html?waitForCustomConfig",
"login_path": "/login.html",
"auth": {
"token": "eyJ...",
"expiresAt": "2026-06-01T12:00:00Z"
},
"env": {
"APP_API_URL": "https://api.example.com",
"APP_AUTH_API_URL": "https://api.example.com",
"APP_WEBSOCKET_URL": "wss://api.example.com"
},
"routePrefix": "https://api.example.com",
"axiosDefaults": {},
"apiRoutes": {},
"tanstack": { "lists": { "refetchOnWindowFocus": true } },
"theming": {
"global": {
"customCSS": "@import url('https://fonts.googleapis.com/...');",
"cssVariables": { "--p-primary": "#6366f1" },
"iconSets": {}
},
"host": {
"customCSS": ".wippy-host-app .chat-container { background: var(--p-content-background); }",
"cssVariables": {},
"iconSets": {},
"i18n": {
"app": {
"title": "My App",
"icon": "wippy:logo",
"appName": "My Application"
}
}
},
"children": {
"customCSS": "",
"cssVariables": {}
}
},
"hostConfig": {
// valores de exemplo — os padrões estão na tabela abaixo
"session": { "type": "non-persistent" },
"history": "hash",
"showAdmin": true,
"allowSelectModel": false,
"startNavOpen": false,
"hideNavBar": false,
"disableRightPanel": false,
"hideSessionSelector": false,
"additionalNavItems": [],
"stateCache": {},
"allowAdditionalTags": [],
"chat": {}
},
"context": {
"resourceId": "root",
"resourceType": "page",
"route": "/"
}
}
Referência de Campos
Campos de nível de shell — consumidos pela página de embedding para construir a si mesma; não fazem parte do AppConfig do filho:
| Campo | Descrição |
|---|---|
facade_url |
URL base de CDN para o bundle do Web Host. Usada para resolver a entrada do módulo e os scripts de vendor. |
iframe_origin |
Valor do header Origin da CDN. Usado como targetOrigin para PostMessage em embeddings manuais de iframe (veja abaixo). |
iframe_url |
src completo do iframe, incluindo ?waitForCustomConfig. Usado apenas por embeddings manuais de iframe sem facade (veja abaixo). |
login_path |
Caminho na origem da página para onde redirecionar usuários não autenticados. |
Campos do AppConfig do filho — passados à função de init do host e consumidos pelo host em execução:
| Campo | Descrição |
|---|---|
$schema |
Versão do contrato de configuração ("wippy-context-2.0"). |
auth |
Bearer token de runtime e expiração, injetados como AppConfig.auth. |
env |
URLs de runtime injetadas como AppConfig.env de nível superior. |
routePrefix |
Prefixo de URL de API encaminhado aos apps filhos. |
axiosDefaults |
Padrões da instância Axios encaminhados aos apps filhos. |
apiRoutes |
Sobrescreve caminhos individuais de endpoints de API (campo de nível superior do AppConfig). |
tanstack |
Padrões do TanStack Query — global + por categoria baseada em papel (content/lists); campo de nível superior do AppConfig. O padrão do host é refetchOnWindowFocus:false. |
theming |
Customização de CSS dividida em três escopos. |
hostConfig |
Feature flags e configuração de UI do Web Host. |
context |
Contexto inicial de página ou artefato para o host. |
Campos de env:
| Campo | Origem | Descrição |
|---|---|---|
APP_API_URL |
Variável de ambiente PUBLIC_API_URL |
URL base para todas as chamadas HTTP ao backend |
APP_AUTH_API_URL |
Igual a APP_API_URL |
URL do endpoint de autenticação (pode diferir em setups customizados) |
APP_WEBSOCKET_URL |
Derivada de APP_API_URL |
http:// → ws://, https:// → wss:// |
Escopos de theming:
| Escopo | Aplicado a |
|---|---|
global |
Tanto o chrome do host quanto todos os iframes filhos |
host |
Apenas o chrome do host. Também carrega i18n.app para o título, ícone e nome do app exibidos na barra lateral. |
children |
Apenas iframes filhos (injetado pelo script de proxy) |
Campos de hostConfig:
| Campo | Tipo | Padrão | Descrição |
|---|---|---|---|
session.type |
"non-persistent" | "cookie" |
"non-persistent" |
Modo de armazenamento do token |
history |
"hash" | "browser" |
"hash" |
Modo de history do Vue Router |
showAdmin |
boolean | true |
Exibe recursos de administração na UI |
allowSelectModel |
boolean | false |
Exibe o seletor de modelo de LLM |
startNavOpen |
boolean | false |
Expande a barra lateral de navegação ao carregar |
hideNavBar |
boolean | false |
Oculta completamente a barra lateral de navegação esquerda |
disableRightPanel |
boolean | false |
Desabilita o painel direito de artefatos |
hideSessionSelector |
boolean | false |
Oculta o seletor de sessões de chat |
additionalNavItems |
array | [] |
Itens extras injetados na barra lateral |
stateCache |
object | {} |
Configuração de cache LRU para o estado de iframes filhos |
allowAdditionalTags |
object | {} |
Whitelist de tags do sanitizador de HTML (Record<string, string[]>, tag → atributos permitidos) |
chat |
object | {} |
Sobrescritas da UI de chat (comportamento de colar-para-arquivo etc.) |
Fluxo de Autenticação
Se o usuário não estiver autenticado ao carregar a página, o wippy/facade redireciona para login_path antes de servir a página HTML. Após o login bem-sucedido, o usuário retorna à URL original. Nenhum estado de autenticação é passado pela própria configuração do Web Host — o Web Host confia no token de autenticação embutido em auth/env pela resposta autenticada da página.
Como o endpoint de configuração é servido pela mesma sessão autenticada que serviu a página HTML, APP_API_URL e a URL de WebSocket derivada refletem automaticamente o backend correto para aquele usuário.
A Função de Init do Módulo
A entrada de módulo JS registra window.initWippyApp na página. A página da facade a chama com o objeto de configuração buscado de /facade/config. O fe_mode seleciona qual módulo a facade carrega — module.js para compat, managed-layout.js para managed — e ambos expõem a mesma função de entrada initWippyApp. A escolha do módulo diz respeito a qual shell renderiza; é independente do estilo de embedding (página de módulo JS vs iframe manual).
initWippyApp(config, rootContainer?) retorna um emissor de eventos simples:
const events = window.initWippyApp(config, '#app')
events.on('ready', () => console.log('Wippy loaded'))
events.on('error', err => console.error('Failed to load:', err))
Quando chamada sem um container raiz, o host monta em um elemento padrão. O host assume a página e seu histórico de navegador a partir desse ponto.
Embedding manual em iframe (sem facade)
A página de módulo JS acima é o caminho padrão e recomendado, e o que a facade atual usa. Existe também um segundo mecanismo de embedding para casos em que você queira rodar o host completo dentro de um iframe — por exemplo, para ocupar apenas parte de uma página com isolamento mais forte da aplicação ao redor. Nesse modo você mesmo incorpora o host; a facade não produz essa página.
Você ainda pode reutilizar o endpoint /facade/config da facade para obter as URLs e a configuração: seu iframe_url (a entrada iframe.html do host, com ?waitForCustomConfig já anexado) e iframe_origin (o targetOrigin para PostMessage) existem exatamente para esse caminho. Você então cria o iframe por conta própria e completa o handshake de configuração.
Diferente do caminho de módulo JS, o host dentro do iframe solicita sua configuração: ele inicializa e posta uma mensagem get-config ao pai, e o pai responde com set-config. Assim, o pai escuta a requisição em vez de empurrar a configuração cegamente no load:
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<iframe id="wippy" style="width:100%;height:100vh;border:none"></iframe>
<script>
fetch('/facade/config')
.then(r => r.json())
.then(config => {
const iframe = document.getElementById('wippy')
// Escuta a requisição de configuração @gen2-chat do filho e a responde.
window.addEventListener('message', (event) => {
if (event.origin !== config.iframe_origin) return
const msg = event.data
if (msg?.type === '@gen2-chat' && msg.action === 'get-config') {
iframe.contentWindow.postMessage(
{ type: '@gen2-chat', action: 'set-config', ...config },
config.iframe_origin
)
}
})
// iframe_url já inclui ?waitForCustomConfig
iframe.src = config.iframe_url
})
</script>
</body>
</html>
O parâmetro de query ?waitForCustomConfig (já presente em iframe_url) é o sinal-chave. Ele diz ao Web Host para pausar a inicialização — o app monta, mas deliberadamente não tenta resolver a autenticação nem carregar rotas até receber uma mensagem set-config. Sem ele, o Web Host tentaria ler tokens de autenticação de parâmetros de URL ou de padrões, o que não é apropriado para deployments incorporados.
O handshake usa o protocolo PostMessage @gen2-chat:
- O pai busca
GET /facade/config(ou fornece ele mesmo um payloadAppConfigequivalente) e cria o iframe apontando paraiframe_url. - O iframe em inicialização posta
{ type: '@gen2-chat', action: 'get-config' }ao pai. - O listener de
messagedo pai responde com{ type: '@gen2-chat', action: 'set-config', ...config }, direcionado aiframe_origin.
O Web Host extrai o payload AppConfig e prossegue com a inicialização completa. Para o protocolo completo de mensagens (envelope @gen2-chat e o enum IFrameMessageType), veja Proxy e Isolamento. Esse handshake de SetConfig é específico do embedding manual sem facade; o módulo wippy/facade carrega o Web Host como um módulo JS.
Configurando o Módulo da Facade
Os parâmetros de wippy/facade que produzem a resposta de configuração acima são definidos no seu _index.yaml. Um exemplo real do app-template:
- name: facade
kind: ns.dependency
component: wippy/facade
version: '>=v0.5.37'
parameters:
- name: server
value: app:gateway
- name: router
value: app:api.public
- name: app_title
value: Wippy App
- name: app_name
value: Wippy App
- name: app_icon
value: "wippy:logo"
- name: show_admin
value: "false"
- name: hide_nav_bar
value: "true"
- name: login_path
value: /app/login.html
- name: session_type
value: non-persistent
- name: history_mode
value: browser
- name: custom_css
value: "@import url('https://fonts.googleapis.com/css2?family=Poppins...');
body { font-family: 'Poppins', sans-serif; }"
- name: css_variables
value: '{"--p-primary":"#6366f1"}'
- name: host_custom_css
value: ".wippy-host-app .chat-container { background: var(--p-content-background); }"
- name: tanstack
value: '{"lists":{"refetchOnWindowFocus":true}}'
Para a lista completa de parâmetros disponíveis e seus padrões, veja a referência do módulo Facade.