# One core, many platforms



A peer-to-peer app has two very different halves.

1. One is the **core**: swarm identity, storage, replication, and the protocol logic—code that should be identical on every device.
2. The other is the **UI**: windows, views, and gestures—code that is necessarily different on a desktop, a phone, and a terminal.

Bare's answer is to keep the core in one place and let only the UI change. This page explains that pattern and the machinery that connects the two halves. For the runtime itself, see [Inside Bare](/bare/explanation/bare-runtime); for where this sits in the wider picture, see [How the stack fits together](/p2p/explanation/how-the-stack-fits-together).

## The shape: shell, seam, core [#the-shape-shell-seam-core]

A cross-platform Bare app has three parts:

<Mermaid
  chart="flowchart LR
  shell[&#x22;Native UI shell<br/>(SwiftUI / Kotlin / Electron)&#x22;]
  seam[&#x22;Typed RPC seam&#x22;]
  core[&#x22;JavaScript core<br/>(Bare worklet)&#x22;]
  shell <-->|&#x22;typed methods&#x22;| seam
  seam <-->|&#x22;IPC&#x22;| core
  core -.->|&#x22;runs&#x22;| net[&#x22;Hyperswarm · HyperDHT · Noise · storage&#x22;]"
/>

* The **native shell** is written in the platform's own language and owns nothing but presentation.
* The **core** runs as a Bare *worklet*—an isolated Bare thread—and owns everything peer-to-peer:
  * it holds the swarm identity
  * opens the [Corestore](/p2p/reference/helpers/corestore)
  * runs [Hyperswarm](/p2p/reference/building-blocks/hyperswarm) and [HyperDHT](/p2p/reference/building-blocks/hyperdht)
  * handles the Noise-encrypted connections to other peers.
* The shell never imports a networking library
* The core never imports a UI framework
* Between them is a [**typed RPC seam**](#the-typed-rpc-seam)

## Worklets carry the core [#worklets-carry-the-core]

[`bare-kit`](/bare/reference/bare/bare-kit) is the toolkit that runs a Bare core inside a native app. It exposes a web-worker-like API for starting and managing isolated Bare threads—called *worklets*—each with an IPC channel to the host.

* On iOS you drive a worklet from Objective-C (Swift interop is available through the standard language bridge);
* on Android, from Java (Kotlin interop is available through the standard language bridge);
* from React Native or Expo, [`react-native-bare-kit`](/bare/reference/bare/bare-kit) gives you the same `Worklet` and `IPC` objects in JavaScript;
* The worklet honours the Bare [lifecycle](/bare/explanation/bare-runtime#the-lifecycle), so the host can suspend and resume the core in step with the OS's app-lifecycle rules.

Because the worklet is just Bare, the core you ship to mobile is the *same JavaScript* you'd run in a desktop [worker](/pear/explanation/workers) or a standalone terminal binary. Write it once; embed it everywhere.

## The typed RPC seam [#the-typed-rpc-seam]

The shell and the core could exchange raw bytes over the IPC pipe, but that pushes framing and parsing into hand-written code on both sides. The Bare RPC ecosystem replaces those raw bytes with **typed methods generated from a shared schema**:

* [`hyperschema`](/p2p/reference/helpers/hyperschema) defines versioned, append-only data structures and generates [`compact-encoding`](/p2p/reference/helpers/compact-encoding) codecs for them.
* [`bare-rpc`](/bare/reference/bare/modules) frames requests and replies over a duplex stream, with a unique command number per method.
* Code generators turn the schema into typed bindings for each language. JavaScript bindings are generated today, and a Swift toolchain (`hyperschema-swift`, `bare-rpc-swift`, `compact-encoding-swift`, `hrpc-swift`) produces wire-compatible Swift structs and a typed RPC class, with C and Kotlin generators following.

The seam supports the RPC patterns a real app needs:

* **unary** request/response,
* **send-only** events,
* **response-stream** (the handler writes a sequence of chunks),
* **request-stream** (the caller streams input), and
* **duplex** (both at once).
  Update the schema, regenerate, and both sides get the new types—no hand-maintained parsing, and a compiler error if the shell and core drift apart.

## One core, many platforms [#one-core-many-platforms]

Put the pieces together and the payoff is structural.

* The JavaScript core is portable and unchanged across devices.
* Each platform adds two cheap, generated or layout-only pieces: a native shell and the bindings for the seam.
* Swapping from iOS to Android to desktop changes the shell and the generated bindings—never the protocol logic.

This is the architecture behind [Keet's](https://keet.io) identical behaviour on phones, laptops, and terminals: one core, several UIs. The desktop ([Electron](/pear/explanation/pear-desktop-architecture)) and terminal ([standalone Bare binary](/p2p/getting-started/from-a-template/start-from-hello-pear-bare)) hosts ship today. On mobile, [`pear-runtime`](/pear/reference/pear/runtime) itself targets desktop; its mobile counterpart is [`pear-mobile`](https://www.npmjs.com/package/pear-mobile), the embeddable Pear runtime for mobile applications—or wire the worklet and updates yourself with `bare-kit`.

## Common questions [#common-questions]

<Accordions type="single">
  <Accordion title="Does Bare run on iOS and Android?">
    Yes. `bare-kit` embeds a Bare worklet in native iOS and Android apps today, and `react-native-bare-kit` does the same for React Native and Expo. The oldest iOS and Android releases Bare supports are listed under [Supported platforms](/bare/reference/bare/runtime#supported-platforms). The [`pear-runtime`](/pear/reference/pear/runtime) equivalent for mobile is [`pear-mobile`](https://www.npmjs.com/package/pear-mobile) (noted earlier on this page).
  </Accordion>

  <Accordion title="How does native code talk to the JavaScript core?">
    Through an IPC channel between the host and the worklet. You can write bytes directly, or—recommended—put a [typed RPC seam](#the-typed-rpc-seam) on top so both sides call generated, type-checked methods instead.
  </Accordion>

  <Accordion title="How is this different from React Native's own JavaScript engine?">
    React Native runs your UI's JavaScript on the main JS thread. A Bare worklet is a *separate*, isolated runtime for your peer-to-peer core, running off the UI thread with its own module system and native addons. The two communicate over IPC; the worklet keeps networking and storage out of the UI's way.
  </Accordion>

  <Accordion title="Do I have to write the protocol twice—once in JS, once in Swift?">
    No. You define the schema once with `hyperschema` and generate bindings for each language. The Swift, JavaScript (and forthcoming C/Kotlin) sides are all generated from—and wire-compatible with—that single source.
  </Accordion>
</Accordions>

## See also [#see-also]

* [`bare-kit` reference](/bare/reference/bare/bare-kit)—the Worklet and IPC API for iOS, Android, and React Native.
* [Embed Bare in a React Native app](/bare/how-to/run-on-native/embed-bare-in-react-native)—start a worklet and exchange messages.
* [Type a native RPC bridge](/bare/how-to/run-on-native/type-a-native-rpc-bridge)—generate a typed seam with `hyperschema` and `bare-rpc`.
* [Bundle a Bare app](/bare/how-to/run-on-native/bundle-a-bare-app)—produce an embeddable bundle or a standalone binary.
* [Workers](/pear/explanation/workers)—the same host-and-core split on the desktop.
* [Inside Bare](/bare/explanation/bare-runtime)—the runtime the worklet is an instance of.
* [Bluetooth in Bare](/bare/explanation/bluetooth)—a core that talks to nearby devices over Bluetooth Low Energy, with no network.
