The sandbox
Read this before you build. Every limit here is deliberate, and none of them are configurable.
No network. At all.
A Content Security Policy is injected before your code runs:
default-src 'self' 'unsafe-inline' data: blob:; connect-src 'none';
script-src 'self' 'unsafe-inline'
connect-src 'none' means no fetch, no XHR, no WebSocket, no beacon. No
CDN scripts, no Google Fonts, no analytics, no error reporting, no API you own.
Everything ships in your folder. Inline your CSS, embed images as data URIs, bundle your own fonts.
This is the single most important thing to design around, and the most common reason a plugin that worked in a browser does nothing on the device. The simulator does not enforce it — test on a device before you publish.
The only outside information available to you is data.weather().
You cannot leave your folder
File access is scoped to your own plugin directory. You cannot read another plugin's files, the app's files, or anything else on the device.
Navigation away from your own bundle is refused — links, redirects,
window.location. Your plugin is one page, for its whole life.
Nothing persists
Storage is non-persistent. localStorage, cookies and caches are cleared
between runs. Anything the user should still have next time has to be
re-derived or re-entered.
A storage API is the most-requested thing not yet built. If you need it, say so — that is how it gets prioritised.
Rate limits
voice.speak |
one per 0.4s, 300 characters |
motor.* |
one move per 0.35s |
| camera events | about 6 per second |
Exceeded calls resolve false and are dropped, not queued. A backlog of
stale commands is worse than a missed one, especially for movement.
Ambient plugins yield
An ambient plugin gives up the screen the moment the robot leaves idle, and
whenever the display deep-dims for battery. Do not depend on running
continuously.
What you can rely on
Your page is a full modern WebView. Canvas, WebGL, CSS animation, Web Audio, touch — all of it works. The limits above are about the outside world, not about what you can build on screen.
The simulator loads your plugin in a plain iframe
It does not inject anything. Two consequences, and both look like the simulator being broken if you don't know them:
Include the SDK yourself. Add <script src="../hairobo-sdk.js"></script>
before your own script, as every example does. Without it hairobo is
undefined and nothing runs. On a real robot the app injects the SDK before your
page loads, so that tag 404s there and does nothing — which is why it is not in
the manifest's files.
Serve your plugin from the same origin as the simulator. The SDK reaches
the fake robot through window.parent, and a browser blocks that across
origins. A plugin loaded from your own domain into a simulator on ours will
load and then refuse every call. Put the folder next to the simulator, or run
both from one local server.