No description
  • Kotlin 39.8%
  • Objective-C++ 29.7%
  • JavaScript 17.4%
  • Ruby 4.6%
  • Objective-C 3.4%
  • Other 5.1%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Morten Olsen 3a35a41889
All checks were successful
ci/woodpecker/push/apps/1 Pipeline was successful
ci/woodpecker/push/apps/2 Pipeline was successful
ci/woodpecker/push/apps/3 Pipeline was successful
ci/woodpecker/push/android Pipeline was successful
ci/woodpecker/push/draft-release Pipeline was successful
ci/woodpecker/tag/release Pipeline was successful
fix(android): open links that arrive while the home screen is showing
MainActivity is singleTask, so a VIEW intent delivered while the address
bar is open came in through onNewIntent and was dropped.
2026-09-25 15:33:23 +02:00
.woodpecker ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00
apple/Unbundled ios support 2026-09-25 12:49:30 +02:00
examples ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00
host fix(android): open links that arrive while the home screen is showing 2026-09-25 15:33:23 +02:00
host-ios ios support 2026-09-25 12:49:30 +02:00
host-macos ios support 2026-09-25 12:49:30 +02:00
metro ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00
scripts ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00
tools ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00
.gitignore ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00
.taskrc.yml ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00
AGENTS.md ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00
CLAUDE.md Unbundled prototype: run React Native apps from URLs 2026-09-25 11:02:49 +02:00
DESIGN.md ios support 2026-09-25 12:49:30 +02:00
DEVELOPER.md ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00
mise.toml ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00
README.md ios support 2026-09-25 12:49:30 +02:00
Taskfile.yml ci: build and publish the Android runtime and example apps 2026-09-25 15:29:50 +02:00

Unbundled

What if a native app were just a URL?

The web has an incredible distribution model. You send someone a link, they tap it, and they are using your software. There is no store and no install step, and updates arrive on the next visit. It is the reason the web won, and it came bundled with HTML, CSS and the DOM.

Native apps got the better UI toolkit. They pay for it with packaging, installing and updating each app as its own binary, which makes store review, "please update" prompts and 200 MB downloads for a to-do list normal.

Unbundled pulls those two apart. It keeps the web's distribution model and puts ordinary native React Native apps underneath it.

https://notes.example/note/123

Tap that on a phone with the Unbundled runtime installed and you get a real native app, open on note 123. Nothing was installed first. Other apps you open live alongside it, each isolated by its origin just as websites are.

How it works

One native app per platform, the runtime, contains React Native, Hermes and a fixed set of native modules. Applications are only JavaScript and assets on an ordinary static web server:

npm run build          # → dist/
deploy dist/ to https://notes.example

When the runtime is given a URL, it:

  1. Finds the application from the URL's origin, via /.well-known/origin-runtime.json.
  2. Downloads only the entry bundle. Everything behind a React.lazy(() => import(...)) stays on the server until the app actually needs it.
  3. Starts the app in its own React Native instance, tied to that origin.
  4. Hands the URL to the app through normal React Native Linking, so its own router decides what /note/123 means.

The apps don't import any Unbundled SDK. They are plain React Native with React Navigation, AsyncStorage, PermissionsAndroid and Linking. The runtime works underneath those APIs:

  • Storage. Each app's AsyncStorage belongs to its origin, so two apps can both store a "theme" key and never see each other's.
  • Permissions. Android grants a permission to the runtime as a whole. An app also needs its own grant, so allowing location for Notes does not allow it for Calendar.
  • Links. Linking.openURL("https://calendar.example/today") from inside Notes starts Calendar in its own task. A link to an ordinary website goes to the browser.
  • Updates. There is no update step. Code is cached by content hash, so a new release downloads only what changed, and only when it's used.

Downloaded apps can't bring their own native code. They get the native modules the runtime was built with and nothing else, which is also what keeps the security boundary meaningful.

Status

Unbundled is an experimental prototype. Every milestone in the original plan works on an Android emulator, including the full end-to-end scenario. That scenario opens a shared link into a Notes app that has never been installed, lazy-loads its editor, keeps its storage and permissions separate, and starts a second app from a link.

Runtimes for iOS and macOS run the same deployed release natively: one origin, one build of the app per platform, no install anywhere. On iOS, Notes and Calendar run with per-origin storage too. Permission isolation exists only on Android so far.

It has not been hardened for untrusted code, and it has only run against local development servers so far.

What the platforms allow

This is a proof of concept, and it stays inside the walls each platform has built. Some features need the platform to play ball. The prototype shows they are technically feasible; whether they can ship depends on the platform owner.

  • Android is the most open. The runtime can own links through App Links, run as a service, and give each app its own task in Recents. Google Play's policy on downloaded code exempts code run by an interpreter with only indirect access to Android APIs, which is close to what this is, and sideloading is always possible.
  • macOS is open enough. The runtime can run as a background agent, open a window per app, and ship as a notarized download outside the App Store.
  • iOS and iPadOS are where the walls are. iPad gives each app its own window, but iPhone shows one window per app, so there the apps share it. There are no user-installable background services, so the runtime only lives while it's in the foreground. It can't claim arbitrary https links: universal links cover only domains it owns, and custom schemes trigger a confirmation prompt. The App Store's rules on downloaded code rule out publishing it. None of this is a technical limit of the model. Apple could make it possible, and the closest existing door is the default-browser entitlement, which already routes http(s) links to a chosen app.

Read more

  • DESIGN.md covers the architecture, how each piece works, and what the prototype found.
  • DEVELOPER.md covers building the runtime and apps and running them on an emulator.