Eximia docs
WebView scheme
How an SLMEximia app's WebView loads its HTML, JavaScript, CSS, and assets. The summary lives in ADR-005; this doc is the implementation contract.
The scheme
The WebView in an SLMEximia app loads:
slm-eximia://app/index.html
app is the fixed authority for app source code. res is reserved
for runtime-bundled resources (icons, the dev console). No other
authorities are valid; the scheme handler MUST return HTTP 404 for any
authority it doesn't recognise.
Allowed paths
| Authority | Maps to | Use |
|---|---|---|
app | assets/www/ (Android) · bundle www/ (iOS) | App source: HTML, JS, CSS, images, fonts. |
res | runtime-bundled resources | Internal: dev-mode console, error pages. |
A plugin that wants to expose its own assets at a custom authority MUST
declare it in its plugin.json (future work — not v1.0).
Android implementation
Component
androidx.webkit.WebViewAssetLoader, configured via the runtime in
SLMEximiaWebView.kt.
Wiring
val assetLoader = WebViewAssetLoader.Builder()
.setDomain("app.slm-eximia.local") // Used internally by WebViewAssetLoader
.setHttpAllowed(false)
.addPathHandler("/", AssetsPathHandler(context))
.build()
webView.webViewClient = object : WebViewClientCompat() {
override fun shouldInterceptRequest(
view: WebView, request: WebResourceRequest
): WebResourceResponse? = assetLoader.shouldInterceptRequest(request.url)
}
WebViewAssetLoader only supports https:// schemes natively. The SLMEximia
runtime extends this by intercepting slm-eximia://app/* URLs and
translating them to internal https://app.slm-eximia.local/* for
WebViewAssetLoader, then translates back outbound. This keeps the
public URL clean while reusing the well-tested handler.
Cookies and origin
The browser sees slm-eximia://app as the origin for all requests from
the loaded page. Cookies are scoped to that origin; localStorage,
indexedDB, and cacheStorage are isolated likewise.
CORS
Cross-origin requests (fetch('https://api.example.com/x') from inside
the WebView) are subject to the standard CORS rules. The remote server
must respond with Access-Control-Allow-Origin: slm-eximia://app or *.
Apps that need to call non-CORS endpoints route them through a plugin —
the plugin makes the request natively.
iOS implementation
Component
WKURLSchemeHandler, registered on the WKWebViewConfiguration in
SLMEximiaSchemeHandler.swift.
Wiring
let cfg = WKWebViewConfiguration()
cfg.setURLSchemeHandler(SLMEximiaSchemeHandler(bundle: .main),
forURLScheme: "slm-eximia")
let webView = WKWebView(frame: .zero, configuration: cfg)
webView.load(URLRequest(url: URL(string: "slm-eximia://app/index.html")!))
SLMEximiaSchemeHandler reads the requested URL, walks the requested path
relative to the bundle's www/ directory (or Resources/), and emits a
URLResponse with the right MIME type plus the file contents.
MIME type detection
The handler maps file extension → MIME type via a static table
(html → text/html, js → application/javascript, css → text/css,
png/jpg/gif/webp → image/*, woff/woff2 → font/*, etc.). Unknown
extensions get application/octet-stream.
Range requests
For media files (audio, video), the handler MUST honour Range request
headers and return 206 Partial Content with Accept-Ranges: bytes.
Without this, large media playback stalls on iOS.
Cookies and origin
WKWebView's cookie store is per WKHTTPCookieStore instance, and we use
the default. Origin is slm-eximia://app — consistent with Android.
Disallowed behaviours
The scheme handler MUST NOT:
- Serve any path outside the app's
www/directory (no../traversal). - Serve
/etc/,/var/, or any absolute filesystem path. - Return responses for any URL whose scheme is not
slm-eximia. - Honor
javascript:URLs inside the page (configured via webview options).
The runtime MUST also configure:
webView.allowFileAccess = false(Android)webView.allowContentAccess = false(Android)webView.allowFileAccessFromFileURLs = false(Android)webView.allowUniversalAccessFromFileURLs = false(Android)
Equivalent settings on iOS are the defaults for WKWebView.
Dev mode
When the CLI runs slm-eximia dev, the WebView is configured to load
from a development server (typically http://localhost:5173/index.html)
instead of slm-eximia://app/. The scheme handler is still installed but
inactive for the dev session. See
../cli/docs/commands/dev.md.
For dev mode to work, the app's AndroidManifest.xml must allow
android:usesCleartextTraffic="true" for localhost only — the CLI
emits a network_security_config.xml automatically when targeting dev.
Testing
The runtime test suite covers:
slm-eximia://app/index.htmlloads with status 200 and right MIME.slm-eximia://app/../etc/passwdreturns 404 (path traversal blocked).slm-eximia://other/xreturns 404 (unknown authority).- Range requests on a large file return 206 with correct partial body (iOS).
- Origin checks: a
fetch('/api/x')from the page makes a request withOrigin: slm-eximia://app.
See ../android/docs/SLMEximiaWebView.md
and ../ios/docs/SLMEximiaSchemeHandler.md
for the implementation detail.