Remote Access Services
August 17, 2026 · View on GitHub
The BrightScript Simulator desktop app, the same way all Roku devices, implements some remote access services in order to enable automation and monitoring of the apps being executed. It allows among other possibilities, to integrate the simulator to the VSCode BrightScript Extension (see how to integrate to VSCode). Below you will find a quick reference documentation about the services available.
Restricting Access to This Machine
By default all the services below accept connections from any device on the local network, the same way a Roku device does. If you prefer to keep the simulator reachable only from the computer it is running on, uncheck Allow connections from other devices on the network at the top of the Remote Access Services section of the Settings Screen.
When that option is disabled:
- The Application Installer, ECP, Remote Console, Debug Server and Remote Screen only accept connections coming from
localhost(127.0.0.1or::1). Requests from any other address are refused. - SSDP discovery advertisements are suppressed, so the simulator does not show up as a Roku device for the other machines scanning the network.
The change is applied immediately to the services that are already running, and any connection already open from another machine is dropped. Because SSDP is turned off, the VSCode BrightScript Extension will no longer discover the simulator automatically while this option is disabled — connecting to 127.0.0.1 still works.
Application Installer
This service allows you to remotely side load an app in the simulator, it has a web interface that can be accessed using a browser, or any HTTP client application. It also has a Utilities option where the user can request a screenshot of the currently running app.
The Development Application Installer by default listens to the TCP port 80 and requires authentication to be used. Because this port is the default HTTP port, it may cause conflict with existing services or be blocked by IT security policies. To overcome that, is possible to configure a different port, either using the Settings Screen or running the simulator with the command line --web=<newport>, this option is saved in the app local storage. An icon is shown in the status bar with the listening port number indicating the service is active, if the icon is clicked it will open the Installer page on the default browser (image above).
The Installer default user and password are both rokudev, besides the Settings Screen, the password can also be changed (and saved) by using the command line --pwd=<newpwd>.
Important
On Linux systems, due to OS restrictions, the Installer service can not be started on port 80, so the service is disabled by default. To enable it, you must specify a different port.
ECP (External Control Protocol)
Once it's enabled the ECP API allows the simulator to be controlled over the network by providing a number of external control commands. When the ECP is enabled it is discoverable using SSDP (Simple Service Discovery Protocol) just like a Roku device. ECP is a simple RESTful API that can be accessed by programs in virtually any programming environment. Please check the ECP official documentation for detailed documentation of the protocol.
The ECP listens to the TCP port 8060 and is disabled by default, it can be enabled either by using the options under the Device Menu or via the command line option --ecp. An icon on the status bar with the port number indicates that the service is active, if the icon is clicked it shows the XML result of the query/device-info command on the default browser.
Supported Commands
The BrightScript Simulator desktop app only implements a subset of ECP commands, here a list of supported commands:
| Command | Description |
|---|---|
| query/device-info | Retrieves device information similar to that returned by roDeviceInfo. (HTTP GET) |
| query/apps | Returns a map of all the recent opened apps paired with their application ID. (HTTP GET) |
| query/active-app | Returns a child element named 'app' with the active application, in the same format as 'query/apps'. (HTTP GET) |
query/icon/appID | Returns an icon corresponding to the application identified by appID. (HTTP GET) |
query/registry/appID | Lists the entries in the device registry for apps. (HTTP GET) |
| query/input | Sends custom events to the current application. It takes a user defined list of name-value pairs sent as query string URI parameters. (HTTP POST) |
launch/appID | Launches the app identified by appID. (HTTP POST) |
exit-app/appID | Terminates the app identified by appID if running. (HTTP POST) |
keypress/key | Equivalent to pressing down and releasing the remote control key identified after the slash. (HTTP POST) |
keydown/key | Equivalent to pressing the remote control key identified after the slash. (HTTP POST) |
keyup/key | Equivalent to releasing the remote control key identified after the slash. (HTTP POST) |
Note: The Application ID in the simulator is a simple hash of the full path of the app zip/bpk file.
BrightScript Remote Console
The Remote Console can be accessed using telnet through a shell application such as PuTTY for Windows or terminal on Mac and Linux:
telnet <simulator-ip-address> 8085
The simulator now supports the interactive debugging using the Remote Console, the list below has the Roku MicroDebugger commands currently implemented:
bt- Print backtrace of call function context framescont|c- Continue script executiondown|d- Move down the function context chain oneexit|q- Exit shellgc- Run garbage collector"last|l- Show last line that executednext|n- Show the next line to executelist- List current functionstep|s|t- Step one program statementthread|th- Show selected threadthreads|ths- List all threads of executionover|v- Step over one program statement (for now act as step)out|o- Step out from current function (for now act as step)var- Display local variables and their types/valuesprint|p|?- Print variable value or expressionexitorquit- Finishes current app executionclose- Disconnect from the remote consolehelp- Show a list of supported commands
When the debugger is activated (either with STOP statement or via Ctrl+Break) you can type any expression for a live compile and run, in the context of the current function.
If the Remote Console is enabled an icon is shown in the status bar together with the port number 8085.
Debug Server
The Debug Server can be accessed using telnet through a shell application such as PuTTY for Windows or terminal on Mac and Linux:
telnet <simulator-ip-address> 8080
This service emulates the device management and debug console available on port 8080 of a physical Roku device. It allows querying device state, managing installed channels, and simulating input. The list below shows the commands currently implemented:
genkey- Reminds to setup Developer Id in Settingsshowkey- Displays the current Developer Id (Dev ID)fps_display- Toggles the performance statistics overlay (e.g.,fps_display 1orfps_display 0)clear_launch_caches- Acknowledges cache clearancelogrendezvous- Toggles rendezvous logging (logrendezvous onorlogrendezvous off)plugins- Lists all currently installed/sideloaded applicationsremove_plugin- Removes a channel by its ID (remove_plugin <channel id>)press- Simulates pressing a sequence of remote buttons (e.g.press up down)type- Types a string of literal characters as if entered on a keyboardtarget- Lists targets or sets active targetexitorquit- Disconnect from the debug serverhelp- Show a list of supported commands
If the Debug Server is enabled an icon is shown in the status bar together with the port number 8080.
Remote Screen
The Remote Screen service streams the simulator display to a browser on your network over WebRTC, so you can watch and control a running app from a phone, a tablet or another computer. This has no Roku counterpart — a real device has no equivalent feature — so it is specific to the simulator.
It listens to the TCP port 8090 and is disabled by default. Enable it from the Device Menu or the Remote Access Services section of the Settings Screen, then open http://<simulator-ip-address>:8090/ in any modern browser. An icon with the port number appears in the status bar while the service is running; clicking it opens the viewer page locally. If the Application Installer is also enabled, its Utilities tab shows a Video Stream button that opens the viewer — useful when you already have the installer open on another device.
The viewer page provides:
- Live video of the simulator screen.
- An on-screen Roku remote, plus the equivalent physical keyboard keys (arrows,
Enter,Escape,Backspace,EndandHome). - A text field for typing into on-screen keyboards, which is often easier than pressing letters one at a time.
- A screenshot button that downloads the current frame as a PNG.
- The stream address, shown under the video with a button that copies it — see Embedding the stream below.
Warning
This service has no password. Unlike the Application Installer, anyone who can reach port 8090 can watch your simulator screen, and — if ECP is also enabled — control it. That is why it is the only service disabled by default. If you enable it, either keep Allow connections from other devices on the network unchecked, or only enable it on networks you trust.
A few things worth knowing:
- The remote buttons need ECP enabled, because that is what they are sent through. The viewer page detects this and shows a banner if ECP is off. Text entry and the screenshot button work either way.
- Video only, no audio. Audio is not part of the stream.
- Up to four viewers at a time. Each one is a separate video encode, so the cap protects the simulator's frame rate. A fifth viewer is told the simulator is busy.
- LAN only. No STUN or TURN server is used, so the browser and the simulator have to be able to reach each other directly. This does not work across the internet.
- Only the viewer page itself can use the service. The video channel and the text field refuse requests that come from a page on any other website, so browsing elsewhere while the service is running cannot expose your screen — but that protection stops at the browser, so the warning above still applies to anything else on the network.
- The stream is always at the display mode's full resolution (720x540 for 480p, 1280x720 for 720p, 1920x1080 for 1080p), regardless of the simulator window size, so shrinking the window or going fullscreen neither disturbs nor degrades it. Changing the display mode briefly interrupts the stream while it renegotiates.
- Updates are sent as the app draws them, so the stream stays in step with the simulator whether the app is animating constantly or sitting on a static menu.
- While at least one viewer is connected, the simulator window keeps rendering even if it is minimized. Without that, minimizing would freeze the stream on a stale frame.
Embedding the stream
The address under the video points at /embed, not at the viewer page: the same live video with no
header, no remote and no footer, sized to fill whatever frame you put it in. Drop it into a page of
your own with an iframe:
<iframe src="http://192.0.2.10:8090/embed"
width="1280" height="720" frameborder="0" allow="autoplay"></iframe>
The copy button gives you that URL with the simulator's own network address already filled in, which
is why the address is worth copying rather than typing: opened from the status bar the viewer is on
localhost, and http://localhost:8090/embed means "this machine" to whichever machine you paste it
into. While Allow connections from other devices on the network is off, the page shows its own
localhost address instead, because a network address would be a link to a connection the simulator
would refuse.
The scheme is http because what you are embedding is a page. There is no URL for the video
itself — WebRTC media is SRTP over UDP, set up by a WebSocket handshake, so there is nothing for a
<video src> or a media player to point at. The /embed page is what performs that handshake.
To drive the stream yourself instead — your own RTCPeerConnection, your own video element —
connect a WebSocket to ws://<simulator-ip>:8090/rtc-session and answer what it sends. The
simulator is always the offerer, because it owns the media track; a client only ever answers, and
never initiates negotiation. Messages are JSON:
| Direction | Message | Meaning |
|---|---|---|
| server → client | {"type":"hello","sessionId":"s1"} | Sent on connect. Informational; nothing to reply. |
| server → client | {"type":"offer","sdp":{…}} | Answer it with setRemoteDescription → createAnswer. |
| server → client | {"type":"candidate","candidate":{…}} | Add it, or buffer it until a remote description exists. |
| server → client | {"type":"busy","maxViewers":4} | The viewer cap is full; the socket then closes with code 4000. Do not reconnect. |
| client → server | {"type":"answer","sdp":{…}} | Your answer. |
| client → server | {"type":"candidate","candidate":{…}} | Your ICE candidates. |
Use { iceServers: [] } — the service is LAN-only, so host candidates are all that is needed and
STUN would only add delay. Candidates can arrive before the offer has been applied, so buffer any
that turn up early. An offer is only ever sent when a viewer joins, so if you lose the connection,
open a new WebSocket rather than waiting for a fresh offer on the old one.
The reference implementation is src/app/web/signaling.js, which is what both the viewer and the
embed page use; it is served at http://<simulator-ip>:8090/signaling.js and is about 200 lines.
