All but React Native are in beta.
All but React Native are in beta.

Download Buoy Desktop
Account required · All downloads
Buoy Desktop is a native dashboard for macOS, Windows, and Linux that mirrors your on-device Buoy tools to a full-size window in real time. It includes a live performance HUD, multi-device switching, and remote control over the running app.
React Native and Flutter devices speak the same protocol, so they show up side by side in one dashboard.
npx --package=@buoy-gg/core buoy login in its folder. A connected device does not sign Desktop in. Desktop takes a key too, under Already have a key?@buoy-gg/external-sync package installed in the app — it's the sync client, and it ships separately from the tools (see below).The download button above grabs the right build for your machine automatically; every build for macOS, Windows, and Linux is on the GitHub releases page. On Mac and Windows, Buoy tells you when a new build is out, and you pick when to install it. On Linux, it links you to the download page. A few updates are required, like a security fix. Buoy asks you to install those before you go on. Buoy Desktop is free to use — a Buoy Pro license unlocks full history and unlimited capture, See pricing for current plan allowances.
On a Mac, the download is a .dmg. Open it and drag Buoy into Applications. If you open Buoy from somewhere else, it offers to move itself there, because it updates itself from Applications.
Buoy tools sync to a local broker on port 42831. Launch Buoy Desktop first; it starts the broker and auto-detects connected devices. Use the device switcher in the title bar to choose which device every tool inspects — every install of your app is its own entry, named after the app and hardware (Acme App (iPhone 17 Pro · 2c1d)). If no device appears, the dashboard shows a troubleshooting panel with your machine's exact URLs and a test you can run from the phone's browser.
The broker listens on your local network, so phones on the same Wi-Fi reach it without setup. Only Buoy Desktop and the Buoy MCP server on your machine can read app data or send actions to an app. They prove it with a token the broker writes when it starts, to ~/.buoy/broker-token (%APPDATA%\Buoy\broker-token on Windows), a file only your user account can read. Anything else that connects is treated as an app: it can send its own data and receive actions addressed to it, and can't see or control anything else.
Each app has a device id. One machine can hold an id on many sockets. A second machine that shows up with an id already in use is refused, and the Desktop log says which address tried. If you pin externalSync.deviceId, give each device its own.
Web apps served from your machine or your local network connect as usual. When a page from any other site tries to connect, such as your production site with Buoy turned on for admins, Buoy Desktop asks whether to allow that site. It remembers an Allow; a Don't Allow lasts until Desktop restarts.
Click the Wi-Fi button at the bottom of Buoy Desktop. Then turn off Phones on Wi-Fi.
Now only this Mac or PC can connect. Sims and USB phones still work. A phone on Wi-Fi can't get in.
Your apps come back in a few seconds. Desktop keeps your choice the next time it starts.
You can also start Desktop with BUOY_BROKER_HOST=127.0.0.1. Then the switch is locked.
An admin can set rules for every Buoy on a Mac or PC. Put a file named policy.json in a folder that only an admin can change.
| System | File |
|---|---|
| macOS | /Library/Application Support/Buoy/policy.json |
| Windows | C:\ProgramData\Buoy\policy.json |
| Linux | /etc/buoy/policy.json |
{ "allowNetworkDevices": false, "allowShowSecrets": false }
{ "allowNetworkDevices": false, "allowShowSecrets": false }
allowNetworkDevices: false means Buoy only hears this Mac or PC. No one can turn Phones on Wi-Fi back on.allowShowSecrets: false means no one can turn on Show secrets.A rule you leave out stays allowed. Buoy Desktop and the MCP both read this file.
If the file has a typo, Buoy turns both rules off. They stay off until you fix it. Desktop shows a note.
A tool like Jamf can copy this file to each Mac or PC.
On Windows, make the Buoy folder as an admin first. Set it so only admins can write to it.
Buoy checks the file every 30 seconds. A new rule takes hold while Desktop is open.
Traffic between a phone and your machine isn't encrypted, so connect over a network you trust.
First, install the sync client in your app. It's a separate package on purpose — apps that never use the desktop dashboard don't carry the sync code at all:
npm install @buoy-gg/external-syncRestart Metro after installing it. In development, FloatingDevTools detects the package and derives the broker address from Metro. The device must be able to reach that address; use the options below when the derived host is unsuitable.
Need to point somewhere else? Pass socketURL in the externalSync prop:
adb reverse tcp:42831 tcp:42831 once per cable session; no socketURL needed (the derived localhost is kept as-is on physical devices — the 10.0.2.2 rewrite only applies to emulators).http://<ip>:42831.<FloatingDevTools externalSync={{ socketURL: "http://192.168.1.20:42831" }} />
<FloatingDevTools externalSync={{ socketURL: "http://192.168.1.20:42831" }} />
Just installed a @buoy-gg package? Restart Metro with
--clear— Metro caches the "optional package missing" resolution, and a plain reload never picks the new package up.
A shipped app must never dial a broker on a customer's phone, so sync is off whenever __DEV__ is false. To profile a release build you own — a local --configuration Release run, an internal TestFlight/EAS build, a field build that ships headless — opt in explicitly. It also requires a real Pro license.
<FloatingDevTools externalSync={{ enableInRelease: true }} />
<FloatingDevTools externalSync={{ enableInRelease: true }} />
There is no Metro server in a release bundle, so the broker host can't be derived and socketURL defaults to http://localhost:42831. That is already right for the iOS Simulator, the Android emulator, and Android over USB (adb reverse tcp:42831 tcp:42831).
Release sync carries real user data, such as session tokens in network captures. So a release build won't connect over plain http:// to another machine, where anyone on the same network could read the traffic; it logs a warning instead. It connects to localhost or over https://. For a physical iOS device on a network you trust, opt in to plain http as well:
<FloatingDevTools
externalSync={{
enableInRelease: true,
socketURL: "http://192.168.1.20:42831",
allowInsecureNetwork: true,
}}
/>
<FloatingDevTools
externalSync={{
enableInRelease: true,
socketURL: "http://192.168.1.20:42831",
allowInsecureNetwork: true,
}}
/>
Most tools work the same in a release build. That means network capture, storage, console, the state tools, routes, images, assets and the performance HUD. Two things stay off:
describe_screen, tap_element and measure_renders calls.expo-updates. Without that package, reload_app says it has no way to reload.A release build runs for real people on real accounts. So Buoy Desktop and the MCP can read each tool, but they can't change the app. A storage write, a response override, a tap or a restore is refused. The error says how to allow it. The tools on the device itself work as before.
To let Desktop and the MCP change one tool, list it:
<FloatingDevTools
externalSync={{ enableInRelease: true, releaseActions: ["storage", "network"] }}
/>
<FloatingDevTools
externalSync={{ enableInRelease: true, releaseActions: ["storage", "network"] }}
/>
To allow every tool, pass "all":
<FloatingDevTools
externalSync={{ enableInRelease: true, releaseActions: ["all"] }}
/>
<FloatingDevTools
externalSync={{ enableInRelease: true, releaseActions: ["all"] }}
/>
Impersonate and the OTA inspector need their own name, even with "all". Impersonate acts as another person, and the OTA inspector changes the code the app runs. Stopping an impersonation always works.
<FloatingDevTools
externalSync={{ enableInRelease: true, releaseActions: ["all", "impersonate"] }}
/>
<FloatingDevTools
externalSync={{ enableInRelease: true, releaseActions: ["all", "impersonate"] }}
/>
Your app covers login tokens, cookies and keys first. Then it sends its data. Buoy Desktop and the MCP see [hidden by Buoy] where a secret was. By default, a real token does not reach an AI model or the Wi-Fi.
This works the same in React Native, web, Swift, Flutter, Android and MAUI.
What gets hidden:
Bearer abc123 turns into Bearer [hidden by Buoy].password, accessToken or apiKey.Names, emails and other user data are not hidden.
No filter is perfect. A secret under a plain name like v can get through until Buoy has seen it in a header.
Need the real value? Click Secrets hidden at the bottom of Buoy Desktop. The app then sends real values, and the chip says Secrets shown. Click it again to hide them.
It also turns off on its own:
Some more things to know:
~/.buoy/broker-token can't.[hidden by Buoy].[hidden by Buoy] in it is refused. Type the real value to change it."secrets":<FloatingDevTools
externalSync={{ enableInRelease: true, releaseActions: ["secrets"] }}
/>
<FloatingDevTools
externalSync={{ enableInRelease: true, releaseActions: ["secrets"] }}
/>
In Swift, Flutter, Android and MAUI, a release build always hides secrets.
Run a debug build with BuoyDevTools mounted and your account configured. Flutter's widget does not enable this connection in profile or release mode.
localhost / 10.0.2.2).BuoyDevTools(
socketUrl: 'http://192.168.1.20:42831',
child: child ?? const SizedBox.shrink(),
)
BuoyDevTools(
socketUrl: 'http://192.168.1.20:42831',
child: child ?? const SizedBox.shrink(),
)
iOS will show the Local Network permission prompt on first connect — tap Allow. After adding a new buoy_* package, do a full restart (hot reload won't pick up new registrations).
The desktop app hosts the same local broker the MCP server uses. Your app connects as a device; the dashboard connects as a "Dashboard" client and receives live state and sends actions over the external-sync protocol. Tool data travels between the device and the configured broker, including across your LAN for physical devices. Account validation and telemetry use separate services; see Telemetry.
Download Buoy Desktop for macOS, Windows, or Linux and open your app with Buoy running. The app derives the broker address from the Metro dev server that served the bundle, so simulators, emulators, and physical devices on the same Wi-Fi connect with zero config.
Buoy tools sync to a local broker on port 42831. Launch Buoy Desktop first — it starts the broker and auto-detects devices. For Android over USB, run adb reverse tcp:42831 tcp:42831 once per cable session.
Sync is off whenever __DEV__ is false, so a shipped app never dials a broker on a customer's phone. To profile a release build you own — a local Release run, an internal TestFlight/EAS build — opt in explicitly; it also requires a real Pro license. Release builds connect to localhost or over https:// unless you set allowInsecureNetwork; see Release builds.