The outerframe integrates into browser navigations, using a cross-platform protocol that hands off to platform-specific behavior. Here’s the current spec for browsers and web views supporting the outerframe.
On navigation, the browser sends the additional HTTP header Outerframe-Accept: application/vnd.outerframe. (Ideally it would instead do this in the Accept header, but Apple’s web view doesn’t expose a way to modify Accept on navigation requests, so I chose to add a new Outerframe-Accept header.) If a server wants to respond with outerframe content, its response HTTP headers should include Content-Type: application/vnd.outerframe. When the browser sees this header, it parses the response body as:
byte 0..3 The ASCII string "OUTR", as a "magic" sanity-check
byte 4..7 UInt32 little-endian format version, currently 1
bytes 8..15 UInt64 little-endian offset to beginning of path
bytes 16..23 UInt64 little-endian length of path
bytes 24..31 UInt64 little-endian offset to beginning of data
bytes 32..39 UInt64 little-endian length of data
remaining Variable length region, where path and data live
This is the outerframe’s analog of an “.html” file, and I call it the “.outer” file. Its contents are simply a format version, a path, and a data blob. Right up front, you see how this philosophy is different from the conventional web. This is not just plaintext, instead it’s an extremely-fast-to-parse binary format. You’ll generate this blob programmatically (e.g. using a simple Python script), or edit it with a hex editor. Making this format binary is planting a cultural flag: we put users, not developers, first. HTML already exists, so this platform gets to focus on the other extreme.
The browser parses the path and appends a platform string, for example this macOS implementation appends “/macos-arm” (or “/macos-x86” on Intel Macs). Other implementations might append strings like “/windows-x86” or “/linux-wayland-arm”. We then fetch that path to get the compiled code blob. The remaining data is an opaque data blob intended for the compiled code to interpret. (In the “Top” app, I don’t use this blob, but document-based outerframe apps use it heavily.)
This is where we move to the platform-specific part. The implementation gets to choose its own format for this compiled code blob. For this macOS implementation, I made it as Apple-native as possible; the “compiled code” is a “.bundle.aar” file, i.e. a compressed Apple Archive containing a loadable NSBundle. The outerframe code extracts this archive and a heavily sandboxed non-UI process “OuterframeContent” loads the bundle and asks it to start rendering.