Authoring guide
Choose the right tool
Build an installable extension when the same signed feature will be installed on more than one BRT site or needs an approved BRT capability such as catalog access or cart updates.
Use a builder Code component for one site's isolated custom interaction. Use Embed code for a supported third-party embed. Neither is a substitute for an installable, versioned extension.
Create a project
Run the scaffold command from the extracted BRT developer kit directory:
node tools/web-extensions/brt-extension.mjs init ./lane-status \
--namespace acme/lane-status \
--name "Lane status"
The publisher slug and package slug form the immutable namespace. Both use lowercase letters, numbers, and single hyphens. Choose them before the first release.
The scaffold includes AGENTS.md. Keep it in the project. Coding agents use it to avoid unsupported browser APIs and invalid package layouts.
Build the interface
Put runtime files under runtime/.
- Keep exactly one HTML file. Its path must match
manifest.jsonentrypoint. - Put executable JavaScript in local
.jsor.mjsmodule files. - Load JavaScript with
<script type="module" src="main.js"></script>. - Put CSS and assets in the bundle. Remote scripts, styles, fonts, and images will not load.
- Attach event listeners from JavaScript. Inline handlers such as
onclickare rejected. - Use
textContent, DOM construction, or carefully controlled templates for data returned by BRT. Treat every string as untrusted. - Design for a responsive iframe. Do not assume a fixed width or height.
The runtime permits HTML, CSS, JavaScript modules, AVIF, GIF, JPEG, PNG, WebP, WOFF, and WOFF2 files. It blocks network connections, forms, nested frames, workers, plug-ins, popups, top navigation, application cookies, and parent DOM access.
Read settings and call BRT
The platform injects a frozen window.BRT object before your entry module runs.
const {settings, capabilities} = await window.BRT.ready;
if (capabilities.includes('catalog.products.read')) {
const result = await window.BRT.catalog.products({limit: 12});
console.log(result.products);
}
document.querySelector('h1').textContent = settings.heading || 'Products';
Every API call is checked twice: once in the frame and again by the storefront host. Declaring a capability does not grant it. A store user must approve it.
Handle rejected promises and unavailable capabilities. A user can revoke a permission after installation.
try {
await window.BRT.cart.manage('add', {productId, quantity: 1});
} catch (error) {
status.textContent = error?.code === 'CAPABILITY_NOT_GRANTED'
? 'Cart access is not approved.'
: 'The item could not be added.';
}
Add visual controls
Manifest properties become controls in the BRT builder. Use them for public presentation settings, not credentials or private data.
{
"heading": {
"type": "text",
"label": "Heading",
"default": "Featured products"
},
"card_count": {
"type": "number",
"label": "Number of products",
"default": 6,
"min": 1,
"max": 12
}
}
Unknown settings are rejected. Existing installations keep their saved settings during upgrades, so preserve property names and compatible types whenever possible.
Preview locally
node tools/web-extensions/brt-extension.mjs preview ./lane-status --port 4179
The local preview injects a mock window.BRT with sample catalog, cart, theme, booking, form, SMS, waiver, and analytics behavior. It is for interface development. It does not replace the production validator or an installed-site test.
Exercise important fallbacks without changing source code:
# Permission was not granted.
node tools/web-extensions/brt-extension.mjs preview ./lane-status \
--deny cart.manage
# A configured platform adapter failed.
node tools/web-extensions/brt-extension.mjs preview ./lane-status \
--fail catalog.products.read
# The catalog returned no products.
node tools/web-extensions/brt-extension.mjs preview ./lane-status \
--empty-catalog
Pass more than one capability to --deny or --fail as a comma-separated list.
Check continuously
node tools/web-extensions/brt-extension.mjs check ./lane-status --json
Run the check in CI and before every package command. It validates the manifest, paths, sizes, entrypoint, local module references, disallowed HTML, capabilities, property defaults, and obvious resource references that the production CSP would block.
Test for failure
At minimum, test these states:
- A requested permission was not granted.
- An API call failed or timed out.
- A catalog or availability query returned no rows.
- A setting is empty or uses its default.
- The iframe is narrow.
- The extension initializes more than once after a preview refresh.
- Buttons are usable by keyboard and controls have accessible names.
Do not collect data just because a capability makes it technically possible. Request the fewest capabilities needed for the feature the user can see.