Keep every Expo development build on your machine.
Find the build that matches your code, and install it in one click.
Read the docs · Quick start · Commands · LLM docs
The devhub ui page on main, with made-up projects. The next release ships this design.
Status: alpha. The CLI, the local page, and the Expo build cache provider are on npm. The desktop app and the launch of an app are not released yet. Each alpha can change the CLI and its JSON output. The CHANGELOG lists what each version has.
| Feature | What it does | State |
|---|---|---|
| Keep your EAS builds | devhub sync downloads each finished EAS dev client build before EAS deletes it. devhub sync --all syncs each project that devhub sync saw before. |
On npm |
| One library for all projects | One store folder, outside your projects, holds every build of every project. | On npm |
| Match by fingerprint | devhub match calculates the native fingerprint with the project's own @expo/fingerprint, and shows the stored builds with the same hash. |
On npm |
| Keep your local builds | As an Expo build cache provider, DevHub stores each build of npx expo run:*, and installs a stored build when the native code did not change. |
On npm |
| Install in one step | devhub install installs a stored build on a ready emulator, a USB Android device, or a booted iOS simulator. It checks the SHA-256 of the file first. |
On npm |
| The local page | devhub ui opens a page with the builds, the devices, an install button, and a sync button. |
On npm |
| Setup checks | devhub doctor checks Node, the store, eas-cli, adb, and Xcode, and gives the fix for each problem. |
On npm |
| JSON for scripts and agents | Each command takes --json. The llms.txt file in the package gives the help, the JSON types, and the API. |
On npm |
| No telemetry | DevHub uses the network only for EAS. It never reads, asks for, or stores your EAS token. | On npm |
| Desktop app | The local page in a desktop window, with a daily sync in the background. | In code, not released |
| Launch | Boot a device, install the build, and open the app with Metro or an EAS Update, in one action. | Planned |
| Profiles, prune, and import | Saved pairs of a device and a build, rules that delete old builds, and the import of an .apk, .aab, or .app file. |
Planned |
Android works on macOS, Windows, and Linux. iOS works on macOS.
In a narrow window, each build is a row.
Expo recommends a development build in place of Expo Go. When you work on many Expo projects, you get these problems:
- Each project needs its own dev client for each platform.
- EAS deletes old builds after a retention period.
- A native build takes minutes. But most of the time the native code did not change, and the old build still works.
- Nothing shows which builds you have, and which one matches your branch.
A dev client needs a rebuild only when the native fingerprint changes. DevHub stores each build with its fingerprint. When your code matches a stored build, DevHub uses that build, and you do not build again.
You need Node 20.19 or later, and an EAS login (npx eas-cli login).
npm install --global @rootnative/devhub
devhub doctor # check the tools, and get the fix for each problem
cd my-expo-app
devhub sync # download the EAS dev client builds of this project
devhub match # show the stored build that matches the current code
devhub install <id> # install it on a ready device
devhub ui # or do all of this on the local pageTo run one command with no install, use npx @rootnative/devhub <command>.
Note
A dev client is a debug build. Its JavaScript runs in development mode, so it does not show the real speed of your app in production.
Add DevHub to a project as an Expo build cache provider:
npm install --save-dev @rootnative/devhub{
"expo": {
"buildCacheProvider": { "plugin": "@rootnative/devhub/cache-provider" }
}
}The first npx expo run:android builds the app, and DevHub stores the APK.
The next run with the same native code installs the stored APK, and Gradle
does not run. iOS works the same for a simulator. Read
Keep your local builds
for each rule.
Tip
EAS deletes old builds. Run devhub sync --all every day, so DevHub gets
each build first.
Sync every day
shows the setup for cron, launchd, and Task Scheduler.
| Command | What it does |
|---|---|
devhub sync |
Downloads each finished EAS dev client build of the project in the current folder that the store does not have. --all syncs each project. |
devhub list |
Shows the stored builds: project, platform, profile, app version, git commit, date, and the EAS expiry date. |
devhub where <id> |
Prints the path of a stored build file. |
devhub devices |
Lists the Android emulators and devices that adb sees, and on macOS the booted iOS simulators. |
devhub install <id> |
Installs a stored build on a ready Android emulator or device, or on a booted iOS simulator. |
devhub match |
Calculates the native fingerprint of the current project, and shows the stored builds with the same fingerprint. |
devhub ui |
Starts a local server, and opens the DevHub page in the browser. |
devhub whoami |
Shows the EAS login state. |
devhub doctor |
Checks the tools and the store, and gives the fix for each problem. |
Each command takes --json. Results go to stdout, and progress and warnings
go to stderr. The exit code is 0 for success, 1 for a failure, and 2
for a usage error.
flowchart LR
run["npx expo run:*"] --> provider["Cache provider"]
term["Terminal"] --> cli["devhub CLI"]
browser["Browser"] --> server["devhub ui server"]
provider --> core["Core"]
cli --> core
server --> core
core --> store[("Store<br/>one folder, all projects")]
core --> tools["adb, simctl, eas-cli"]
tools --> targets["Simulators, emulators, EAS"]
- The project's own fingerprint. DevHub loads
@expo/fingerprintfrom the project folder, and reads thefingerprint.config.jsof the project. A bundled copy can calculate a different hash, and then you get the wrong build or no match. - One store for all projects. The store is plain files in the data
folder of your system, never inside a project.
DEVHUB_HOMEmoves it. - Commands as arrays. DevHub runs adb, simctl, and eas-cli with an array of arguments, never a shell string. A space or a quote in a path cannot change a command.
- Public eas-cli commands only. DevHub never calls a private EAS API.
- A local page. The
devhub uiserver listens on127.0.0.1only. Each run makes a new token, and each API call must send it.
DevHub grows in small milestones. Each one gives you one useful result.
| Milestone | Result | State |
|---|---|---|
| Keep | EAS dev client builds are never lost | On npm |
| Install | Install a stored build on a running emulator, simulator, or Android phone | On npm |
| Match | See the stored build that matches the current code | On npm. A manual check against a real EAS build is open. |
| Local builds | npx expo run:* stores each build, and uses a stored build again |
On npm. A CI check passed on Android with Expo SDK 57. The checks on SDK 58 and on iOS are open. |
| Local page | devhub ui opens a page with the list, the devices, an install button, and a sync button |
On npm. A new design is on main. |
| Doctor | devhub doctor checks the tools and the store, and gives the fix for each problem |
On npm |
| Desktop | The local page in a desktop app, with a daily sync in the background | In code, not released |
Each command takes --json, so an agent reads the same output as the local
page. The package ships llms.txt with the help of each command, the type of
each JSON output, and the core API of the installed version:
node_modules/@rootnative/devhub/llms.txt. The docs site serves the same
file at llms.txt. Read
For AI agents.
| Page | Read it to |
|---|---|
| Introduction | Learn what DevHub does, and why |
| Installation | Install DevHub and its requirements |
| Quick start | Keep, match, and install your first build |
| Keep your local builds | Set up the Expo build cache provider |
| Sync every day | Run the sync with cron, launchd, or Task Scheduler |
| The store | Learn where DevHub keeps the builds, and how to move the store |
| JSON output and exit codes | Read the output of each command in a script |
| Security and privacy | Learn what DevHub reads, writes, and sends |
| Package README | Read the full CLI reference |
| Layer | Technology |
|---|---|
| CLI and core | Node 20.19 or later, TypeScript 5.9 in strict mode, ESM |
| Runtime dependencies | cross-spawn and json5, nothing else |
| Build | tsup, Turborepo 2 |
| Tests | Vitest 3, on Linux, macOS, and Windows |
| Local page | An Expo SDK 58 app, exported for the web, and shipped inside the package |
| Desktop app | Electron 44 |
| Docs | Docusaurus 3 |
| Delivery | GitHub Actions, GitHub Pages, and npm with trusted publishing |
You need Node.js 24 (the version in .nvmrc) and pnpm 10.
git clone https://github.com/rootnative/devhub.git
cd devhub
pnpm install| Command | What it does |
|---|---|
pnpm run build |
Builds the page and the package with Turborepo |
pnpm run dev |
Builds the package in watch mode |
pnpm run typecheck |
Runs the type check |
pnpm run test |
Runs Vitest |
pnpm run lint |
Runs ESLint |
pnpm run build:llms |
Writes packages/devhub/llms.txt from the build |
pnpm run preflight |
Runs each check, in the order of a release |
pnpm run docs:dev |
Starts the docs site |
pnpm run desktop |
Builds and starts the desktop app |
Run the build from the root, so Turborepo exports the page first. Then run the CLI from the build:
pnpm run build
node packages/devhub/dist/bin.js --help
node packages/devhub/dist/bin.js uiTo run one test file:
pnpm --filter @rootnative/devhub test -- src/__tests__/cli.test.tsRepository layout
| Path | What it is |
|---|---|
| packages/devhub/ | @rootnative/devhub: the CLI, the core, the cache provider, and the devhub ui server |
| apps/web/ | The devhub ui page. Private. The package ships its build. |
| apps/desktop/ | The Electron app. Private. |
| docs/ | The Docusaurus docs site. Private. |
| example/ | The Expo app that the cache provider check builds. Run it through example/with-store.mjs, so a test build does not go into your own store. |
| scripts/ | The llms.txt generator, the package and release checks, and the brand generator |
To report a vulnerability, read SECURITY.md. To learn what DevHub reads, writes, and sends, read Security and privacy.
MIT © 2026 Root Native