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:

  1. An HTTP server and public router.
  2. A wippy/facade dependency connected to the server and router, with custom branding.
  3. The facade shell at / and its configuration at /api/public/facade/config.

Prerequisites

  • Wippy runtime v0.3.32a and a project created with wippy init or 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

  1. 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.
  2. On load it fetches GET /api/public/facade/config.
  3. It reads @wippy_token_info from localStorage, redirecting to login_path only when the item is absent or cannot be parsed as JSON.
  4. It imports the Web Host bundle from the CDN (facade_url + '/module.js') and calls initWippyApp(...) 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 to login_path. Pair it with userspace/users or 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.html means the current origin has no parseable @wippy_token_info item. Complete the real login flow on the same origin. A parseable object with a missing or empty token suppresses this redirect but still fails when the Host reaches a protected API.
  • HTTP 404 from /api/public/facade/config means the router prefix is not /api/public or the router dependency 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