Remote content and hybrid apps
By default a koru app shows only the files in webDir. The optional server block in koru.json lets it load a page from your own website, open some sites inside the app and send other links to the system. This is phase 1 of remote content, and it has not been validated on an Android device yet; see Status.
On this page
Configure it
"server": {
"url": "https://example.com/app",
"allowNavigation": ["example.com", "*.example.com"],
"bridge": ["https://example.com"]
}
This is a field fragment, not a complete koru.json. All three fields are optional.
| Field | Meaning and validation |
|---|---|
url |
First page to open instead of webDir/index.html. Must be https. It cannot be app.koru.invalid. |
allowNavigation |
Hosts that open inside the app, at most 32. *.example.com matches subdomains such as app.example.com, not example.com itself. The host of url is included automatically. |
bridge |
Exact https origins, at most 32, with no wildcards and no path, whose pages receive the koru API. Each one must be loaded by the app: be in url or in allowNavigation. |
webDir stays required. It still serves your local pages at https://app.koru.invalid/. Using server adds android.permission.INTERNET automatically. Without server, the runtime bytecode is byte for byte the same as before; a test with pinned hashes checks it.
Three ways to use it
An app that is a website
Point url at your site. Add bridge only if its pages should call the native API.
"server": {
"url": "https://example.com/app",
"bridge": ["https://example.com"]
}
A local app with your own site or login inside
Keep your local pages as the first screen and let some hosts open in the app instead of leaving it.
"server": {
"allowNavigation": ["login.example.com", "example.com"]
}
External links that finally open the browser
Without server, a link to another site does nothing. With any server field, even an empty block, it opens in the system app. This is the whole configuration:
"server": {}
How navigation works
When server is present, a main-frame navigation is decided by its URL:
- An
httpsURL whose host is inurlorallowNavigation, or the local app origin, loads inside the app. Onlyhttpsnavigates inside the app. - Any other link opens outside the app, with the system app, for the schemes
https,http,mailto,tel,sms,geoandmarket. - Other schemes, such as
intent:orjavascript:, are ignored. - Iframes load where they are; this rule applies to main-frame navigation only.
Without server, behavior is unchanged: external links do nothing.
Give remote pages the koru API
bridge lists the origins whose pages get the same plugins as the local app, including shared storage. The remote server is yours, so those pages must load core.js themselves; copy it into your site with koru js.
When bridge is set:
- The app adds
KoruApp/1to the WebView user agent. - When each page finishes loading, the app origin and the
bridgeorigins receive the native channel. The MessagePort is delivered only to that exact origin. - Every other page receives
koru-none, andcore.jsuses its web implementations. - On a remote page with the marker,
Koru.platformis"android"from the start, and calls wait until the page has finished loading.
Security
bridge gives that site the same native access as your app. Any XSS or compromise of that site can use the API. List only origins you control, serve them over HTTPS and set a Content-Security-Policy. Remote pages do not get the CSP koru adds to local files; that header is yours to send. See Signing and security.
Status
Not validated on a device. Two checks exist without one: the runtime built without server is identical to before, and koru build now passes all bytecode (the runtime and your own plugins) through a register type verifier similar to ART's (internal/dex/verify). It refuses to build an APK that ART would reject when loading the class. That is not execution on Android.
The self-test app in examples/selftest uses allowNavigation: koru.js.org and bridge: https://koru.js.org, with links to try it: a remote page at https://koru.js.org/selftest/remote/ that checks the bridge and writes KORU_SELFTEST_REMOTE to logcat, an external link and a mailto: link. Its README explains them.