Every box includes a browser-accessible Linux desktop. Use it when you need to inspect a running app visually, use Chrome, debug Electron apps, or control GUI tools that are awkward over SSH.
Open the desktop
From your local machine, open an authenticated desktop URL. Integrations can request the same URL through the SDK or API:
curl -sS -X POST "$BOX_API_BASE/boxes/bx_f7k2q9hd/desktop?theme=light" \
-H "Authorization: Bearer $BOX_API_KEY"
const desktop = await box.desktop({ boxId: "bx_f7k2q9hd", theme: "light" });
console.log(desktop.desktopUrl);
desktop = box.desktop("bx_f7k2q9hd", theme="light")
print(desktop.desktop_url)
You can also open the desktop from the Box dashboard when the box is running.
Desktop streaming is available only after the box’s machine is up. If the box is provisioning, archived, or stopped, start or resume it first.
Streaming modes
The default desktop streams over Moonlight (60fps WebRTC) for the smoothest, lowest-latency picture. WebRTC relies on UDP and peer connectivity, so on restrictive, corporate, or low-bandwidth networks it can be choppy or fail to connect.
For those cases, use VNC, which tunnels over plain HTTPS and is far more tolerant of poor connections (at a lower frame rate):
box desktop bx_f7k2q9hd --vnc
box desktop bx_f7k2q9hd --vnc --public
curl -sS -X POST "$BOX_API_BASE/boxes/bx_f7k2q9hd/desktop?vnc=1" \
-H "Authorization: Bearer $BOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"publicAccess":true}'
const vnc = await box.desktop({
boxId: "bx_f7k2q9hd",
vnc: 1,
requestBody: { publicAccess: true },
});
console.log(vnc.desktopUrl);
vnc = box.desktop("bx_f7k2q9hd", vnc=1, request_body={"publicAccess": True})
print(vnc.desktop_url)
You don’t have to decide up front: the browser viewer shows a “Switch to VNC” option while the default stream is connecting or playing, and again if it errors. The dashboard also exposes both Desktop and Desktop via VNC directly.
The VNC viewer opens as its own top-level page (not embedded), which is required for its connection to authenticate. The first --vnc on a box takes a few seconds to prepare; later opens are instant.
Desktop Automation
Boxes also include Lux for desktop and browser automation. Lux is available inside the Box as both:
- a
lux CLI for driving the visible desktop from shell commands
- a Lux skill that helps agents use the CLI for GUI work
Use Lux when a workflow needs real browser or desktop interaction: logging in through Chrome, checking a visual UI state, testing an Electron app, or producing a demo video.
box ssh bx_f7k2q9hd
lux start "Open Chrome and test the login flow"
lux run
curl -sS -X POST "$BOX_API_BASE/boxes/bx_f7k2q9hd/commands" \
-H "Authorization: Bearer $BOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"command":"lux start \"Open Chrome and test the login flow\" && lux run"}'
await box.command({
boxId: "bx_f7k2q9hd",
commandRequest: { command: 'lux start "Open Chrome and test the login flow" && lux run' },
});
box.command("bx_f7k2q9hd", CommandRequest(
command='lux start "Open Chrome and test the login flow" && lux run',
))
lux run drives the active Lux session to completion and records a demo video under:
To get a recording out of the box, download it, or serve the folder and share the token-gated link:
box scp bx_f7k2q9hd:/home/user/lux-demos/<video>.mp4 ./demo.mp4
# or share a link straight from the box:
box ssh bx_f7k2q9hd "cd ~/lux-demos && (python3 -m http.server 8090 --bind 0.0.0.0 &) && host 8090 --private"
curl -L -o demo.mp4 "$BOX_API_BASE/boxes/bx_f7k2q9hd/artifacts?path=lux-demos/<video>.mp4" \
-H "Authorization: Bearer $BOX_API_KEY"
const video = await box.artifact({ boxId: "bx_f7k2q9hd", path: "lux-demos/<video>.mp4" });
video = box.artifact("bx_f7k2q9hd", "lux-demos/<video>.mp4")
The common commands are:
| Command | Use |
|---|
lux start "<task>" | Start a desktop automation session. |
lux run | Run the session until complete. Records a demo automatically. |
lux run --max-steps N | Raise the step budget for longer flows. |
lux status | Show Lux usage, limits, and active recorder state. |
Lux controls the Box’s single shared desktop, so run only one Lux session at a time. Wait for the active lux run to finish before starting another session.
Lux quotas
Lux has its own quota, separate from your plan’s machine time: 20 sessions per day per account, shared across every box you own, including no-env boxes you hand to your users. The quota is the same on every plan and resets daily.
Each session also has a step budget. Lux picks it from the task (20 steps for quick actions, 100 for thorough flows); raise it with lux run --max-steps N. lux status shows today’s usage.
You can combine Lux with normal desktop tools. For example, launch Chrome or focus a window with shell commands, then let Lux interact with the UI:
DISPLAY=:0 google-chrome-stable "https://example.com"
wmctrl -a "Chrome"
lux start "Complete the checkout flow in the open browser"
lux run
curl -sS -X POST "$BOX_API_BASE/boxes/bx_f7k2q9hd/commands" \
-H "Authorization: Bearer $BOX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"command":"DISPLAY=:0 google-chrome-stable \"https://example.com\" && wmctrl -a Chrome && lux start \"Complete the checkout flow in the open browser\" && lux run"}'
await box.command({
boxId: "bx_f7k2q9hd",
commandRequest: {
command: 'DISPLAY=:0 google-chrome-stable "https://example.com" && wmctrl -a Chrome && lux start "Complete the checkout flow in the open browser" && lux run',
},
});
box.command("bx_f7k2q9hd", CommandRequest(
command='DISPLAY=:0 google-chrome-stable "https://example.com" && wmctrl -a Chrome && lux start "Complete the checkout flow in the open browser" && lux run',
))
What happens when you open it
The CLI or dashboard asks Box for a fresh authenticated desktop URL for your box. If the desktop stream is not ready yet, Box prepares it and then opens the browser viewer.
The returned URL opens a browser page. For the default Moonlight stream it looks like this:
https://<box-desktop-host>/stream.html?hostId=<host>&appId=<app>&token=<token>&theme=light
For a VNC stream (--vnc) it points at a noVNC page instead:
https://<box-vnc-host>/vnc.html?autoconnect=true&password=<pw>&_token=<token>
If you request --public or send publicAccess: true to POST /boxes/{boxId}/desktop?vnc=1, the noVNC URL does not include _token. The exact host, IDs, password, and token fields are generated for the running box. Treat desktop URLs as sensitive because they can let the browser attach to that desktop session.
Security model
Desktop URLs are generated through the authenticated Box API. The URL contains a desktop access token, so do not paste the full URL into shared chats or logs. If you need to give someone else access to a box, use the intended Box account and access controls rather than sharing a desktop URL.
Stopping or archiving a box makes the desktop unavailable until the box is resumed.
Do not rely on desktop processes surviving resume or fork. After a resume or fork, reopen Chrome, restart your app or dev server, and start a new Lux session if you need automation.