Frontend Facade
Use wippy/facade to serve the Wippy Web Host from a backend application. The facade loads the frontend bundle from a CDN and configures it through a JSON endpoint served by the application, without requiring a frontend build step. Dependency parameters control branding, theming, and feature flags.
Classification: Partial integration recipe. It completely configures and verifies the facade shell and config endpoint, but it does not invent an authentication system or the application APIs consumed by the Web Host.
What You'll Build
A backend app that serves the Wippy UI:
- An HTTP server and public router.
- A
wippy/facadedependency connected to the server and router, with custom branding. - The facade shell at
/and its configuration at/api/public/facade/config.
Prerequisites
-
Wippy runtime
v0.3.32aand a project created withwippy initor the Wippy application template. -
For browser rendering, a same-origin login flow that obtains a real backend token and stores
{"token":"..."}under the localStorage key@wippy_token_info. The facade does not issue or validate that token. -
The facade installed:
wippy add wippy/facade@0.6.37 wippy install
How It Works
- The shell is rendered from the facade's template and served at
/by your HTTP server; its assets and the deep-link fallback come from a static mount on the same server. - On load it fetches
GET /api/public/facade/config. - It reads
@wippy_token_infofromlocalStorage, redirecting tologin_pathonly when the item is absent or cannot be parsed as JSON. - It imports the Web Host bundle from the CDN (
facade_url + '/module.js') and callsinitWippyApp(...)with the config.
The application serves the shell and its configuration; the UI bundle comes from the CDN.
Dependencies
The facade requires an http.service for the shell and an http.router for its configuration endpoint. Other parameters customize branding and behavior.
version: "1.0"
namespace: app
entries:
- name: gateway
kind: http.service
addr: ":8087"
lifecycle:
auto_start: true
- name: api.public
kind: http.router
meta:
server: app:gateway
prefix: /api/public
- name: facade
kind: ns.dependency
component: wippy/facade
version: "*"
parameters:
- name: server
value: app:gateway
- name: router
value: app:api.public
- name: app_title
value: Verify App
The shell requests its config, theme script, and CSS variables under
/api/public/facade/, so the public router's prefix must be /api/public.
Run It
wippy run
The shell is served at the server root, and the config endpoint returns the runtime configuration:
curl http://localhost:8087/api/public/facade/config
Selected fields from the response are shown below:
{
"facade_url": "https://web-host.wippy.ai/webcomponents-1.0.58",
"iframe_origin": "https://web-host.wippy.ai",
"iframe_url": "https://web-host.wippy.ai/webcomponents-1.0.58/iframe.html?waitForCustomConfig",
"module_file": "/module.js",
"mode": "compat",
"login_path": "/login.html",
"themeMode": "auto",
"themePersist": "none",
"themeStorageKey": "@wippy-theme-mode",
"env": { "APP_API_URL": "", "APP_AUTH_API_URL": "", "APP_WEBSOCKET_URL": "" },
"themeMode": "auto",
"themePersist": "none",
"themeStorageKey": "@wippy-theme-mode",
"theming": {
"host": { "i18n": { "app": { "title": "Verify App", "icon": "wippy:logo", "appName": "Wippy AI" } } }
},
"hostConfig": {
"showAdmin": true, "allowSelectModel": false, "hideNavBar": false,
"disableRightPanel": false, "startNavOpen": false, "hideSessionSelector": false,
"renderEngine": "iframe", "session": { "type": "non-persistent" }, "history": "hash"
}
}
The app_title parameter appears as theming.host.i18n.app.title in the response.
Also fetch the root document:
curl http://localhost:8087/
It should return an HTML shell that fetches the config endpoint and checks
@wippy_token_info. These two HTTP checks verify the recipe without bypassing auth.
Browser Authentication and Rendering
The facade's localStorage contract is origin-scoped. A login page on another port or
hostname cannot populate the token for http://localhost:8087. After a successful
same-origin token exchange, the login page writes the real token and returns to the
shell:
localStorage.setItem('@wippy_token_info', JSON.stringify({token: result.token}));
window.location.assign('/');
The shell reads the token, imports
https://web-host.wippy.ai/webcomponents-1.0.56/module.js, and passes the token to the
Host. Rendering is complete only when the browser shows the Host without redirecting
and its API requests authenticate successfully. Do not use a placeholder token merely
to suppress the redirect: the shell does not validate it, so the failure only moves to
the first protected API request.
Configuration
Parameters are passed as dependency parameters (values are strings; JSON values are
JSON-encoded strings). Common ones:
| Parameter | Purpose |
|---|---|
server / router |
(required) HTTP server and public router |
app_title / app_name / app_icon |
Branding (icon is an Iconify ref) |
show_admin / hide_nav_bar |
Feature flags ("true" / "false") |
login_path |
Where the shell redirects when no auth token is present |
session_type |
non-persistent or cookie |
history_mode |
hash or browser |
css_variables |
JSON string of CSS custom properties, e.g. '{"--p-primary":"#6366f1"}' |
fe_facade_url |
CDN bundle URL (pinned per facade release; leave default unless overriding) |
Two values are derived at runtime from PUBLIC_API_URL rather than parameters: the API
base URL and the WebSocket URL (http→ws, https→wss). The facade reads it through
the env registry, so declare it as an env.variable in your app. If unset, the browser
falls back to window.location.origin.
Limitations
- The facade does not provide authentication. It expects an auth flow that writes a
token to
localStorage; without one it redirects tologin_path. Pair it withuserspace/usersor your own auth. - The UI bundle loads from the CDN (
fe_facade_url), so the user's browser must be able to reach that URL.
Troubleshooting
- A redirect loop to
/login.htmlmeans the current origin has no parseable@wippy_token_infoitem. Complete the real login flow on the same origin. A parseable object with a missing or emptytokensuppresses this redirect but still fails when the Host reaches a protected API. - HTTP 404 from
/api/public/facade/configmeans the router prefix is not/api/publicor therouterdependency parameter points at another entry. - A config response with the right values but a blank shell usually means the browser
cannot load
facade_url + module_file; check the browser network panel and CDN policy. - Authenticated API errors after the Host renders belong to the application's API and token validation layer, not to the facade shell.
Next Steps
- Hello World — Minimal project layout
- Authentication — Add the login flow expected by the shell
- HTTP Endpoints — Routers, static files, and handlers