The "BrightSign OS Extensions" feature allows BrightSign partners to run their own code on BrightSign products. This repository provides a self-guided workshop for creating, debugging, and deploying extensions, with examples in multiple programming languages.
Want to get started quickly with a TypeScript/Node.js extension? Follow these steps:
Click the "Use this template" button at the top of this repository to create your own copy.
git clone https://github.com/YOUR_USERNAME/YOUR_REPO_NAME.git
cd YOUR_REPO_NAME
# Navigate to the TypeScript example
cd examples/hello_world-ts-extension
# Install dependencies
npm install
# Build the extension
npm run build- Rename your extension: Update
DAEMON_NAMEinbsext_initand the extension name inpackage.json - Modify the code: Edit
src/index.tswith your application logic - Update configuration: Adjust settings in
src/config.ts
# Package for deployment
npm run package-lvm
# Copy the resulting zip to your un-secured player and installDelete the examples you don't need:
# From repository root
rm -rf examples/time_publisher-cpp-extension # If not using C++
rm -rf examples/hello_world-go-extension # If not using Go
rm -rf examples/hello_world-ts-extension # If not using TypeScript
rm -rf Dockerfile # If not building SDK
rm -rf plans/ # Planning documentsIf you're using an AI assistant like Claude Code or GitHub Copilot, use this prompt to automate the setup:
I've created a new repository from the BrightSign extension template. Please help me set up a new TypeScript extension called "[YOUR_EXTENSION_NAME]" that [DESCRIBE WHAT YOUR EXTENSION DOES].
Please:
1. Rename the hello_world-ts-extension directory to [YOUR_EXTENSION_NAME]-extension
2. Update DAEMON_NAME in bsext_init to "[YOUR_EXTENSION_NAME]"
3. Update the package.json name and description
4. Modify src/index.ts to implement: [YOUR FUNCTIONALITY]
5. Update src/config.ts with appropriate configuration
6. Delete the examples I won't use: [time_publisher-cpp-extension / hello_world-go-extension]
7. Delete the Dockerfile if I don't need the SDK
8. Update this README.md to describe my specific extension
9. Delete the plans/ directory
My extension should: [DETAILED DESCRIPTION OF FUNCTIONALITY]
For more detailed guidance on working with this codebase using an LLM, see CLAUDE.md.
A BrightSign extension is a squashfs filesystem that is installed onto the player's internal NVRAM storage. Key characteristics:
- Persistent: Extensions survive reboots - they are stored in internal flash, not on the SD card
- Mountable: On boot, the extension filesystem is mounted at
/var/volatile/bsext/{extension_name}/ - Auto-started: Linux SysV init scripts (
bsext_init) automatically start your software on boot - Removable: Extensions can be uninstalled, freeing up the storage space
- Read-only: The squashfs filesystem is read-only; runtime data should go to writable locations
- Installation: Extension package is written to an LVM or UBI volume in NVRAM
- Boot: Player mounts the extension filesystem and runs
bsext_init start - Running: Your software runs as a daemon process
- Shutdown: Player runs
bsext_init stopfor graceful cleanup - Uninstallation: Volume is unmounted and removed from NVRAM
Extensions fall into two categories based on their compatibility with BrightSign OS (BOS) versions:
Standalone extensions work regardless of the BOS version. They have no dependencies on version-specific system libraries.
Examples:
- Go applications (statically compiled)
- JavaScript/Node.js applications (use the player's runtime)
- Any statically-linked binary
Versioned extensions only work on a specific version of BOS because they depend on version-specific libraries (like glibc or libstdc++). If the BOS is updated, the extension may need to be recompiled and re-signed.
Examples:
- C/C++ applications with dynamic linking
- Any application that links against system shared libraries
Extensions can contain different types of software. The type determines whether you need the BrightSign SDK:
| Type | Language Examples | SDK Required? | Extension Type |
|---|---|---|---|
| Compiled (Dynamic Linking) | C, C++, Rust (default) | YES | Versioned |
| Compiled (Static Linking) | Go, Rust (musl) | NO | Standalone |
| Interpreted | TypeScript/JavaScript, Python, Shell | NO | Standalone |
If your compiled code uses dynamic linking to shared libraries on the player (like glibc, libstdc++, etc.), you must use the BrightSign SDK. The SDK provides:
- Cross-compilation toolchain for ARM64
- Headers and libraries matching the player's runtime
- Ensures binary compatibility with the target OS version
Note: Extensions built with the SDK are versioned - they are tied to the specific BOS version the SDK was built from.
- Statically compiled binaries (like Go) include everything needed - no runtime dependencies
- Interpreted languages use runtimes already on the player (Node.js is available on BrightSign players)
These extensions are standalone and will work across BOS versions.
- Write and test your application on your development host
- For compiled languages: test natively first, then cross-compile
- For interpreted languages: test with the target runtime version
Every extension needs a bsext_init script at its root that handles:
start- Start your software as a background daemonstop- Stop your software gracefullyrun- (Optional) Run in foreground for debugging
Use the provided packaging scripts to:
- Create a squashfs archive of your extension
- Generate an installation script
- Un-secure a player for development
- Install and test the unsigned extension
- Iterate until working correctly
- Extension must have a globally unique name
- BrightSign may require name changes to ensure uniqueness across all partners
- Contact your Partner Engineer for submission process
- Receive signed
.bsfwfile for production deployment
- Linux development host with x86 architecture CPU
squashfs-toolspackage (apt install squashfs-toolson Debian/Ubuntu)- BrightSign Player running OS v9.x or later
- Serial cable for player connection
- Docker (recommended) for SDK build environment
- ~50GB disk space for SDK build
- Several hours for initial SDK build
- Go 1.21 or later
- Cross-compilation:
GOOS=linux GOARCH=arm64 CGO_ENABLED=0 go build
- Node.js 18+ and npm
- TypeScript and Webpack for compilation and bundling
- Verify Node.js version on target player matches development environment
Development extensions must be tested on an un-secured player.
- A player in production, factory reset condition with the Diagnostic Web Server (DWS) enabled
- Serial connection to the player - see Serial Connection Guide
- Un-secure the player
- Set up and verify SSH communications - see Telnet and SSH
Ensure:
- The player sends console output to your serial monitor
- You can connect to the player over SSH
- You can access the Linux shell (
Ctrl-C,exit,exitfrom SSH) - You can inspect processes with
ps
Do not proceed until all the above are functional!
This repository includes three example extensions demonstrating different approaches:
A C++ application that broadcasts the current time over UDP.
- SDK Required: Yes (dynamically linked)
- Demonstrates: Signal handling, UDP sockets, cross-compilation
- Documentation: examples/time_publisher-cpp-extension/README.md
A TypeScript/Node.js application that broadcasts device info and timestamps over UDP.
- SDK Required: No (uses player's Node.js runtime)
- Demonstrates: TypeScript setup, BrightSign JavaScript APIs, Webpack bundling
- Documentation: examples/hello_world-ts-extension/README.md
A Go application that broadcasts "Hello World" messages over UDP.
- SDK Required: No (statically compiled)
- Demonstrates: Simple cross-compilation, minimal dependencies
- Documentation: examples/hello_world-go-extension/README.md
- Choose an example based on your preferred language and SDK requirements
- Follow the README in that example's directory
- Use it as a foundation for your own extension
| If you want to... | Use this example |
|---|---|
| Use C/C++ with system libraries | time_publisher-cpp-extension |
| Use TypeScript/Node.js | hello_world-ts-extension |
| Use Go (simplest cross-compilation) | hello_world-go-extension |
The examples/common-scripts/ directory contains shared packaging scripts:
make-extension-lvm- Creates LVM volume package (most common)make-extension-ubi- Creates UBI volume packagepkg-dev.sh- Wrapper script for packaging
These scripts are used by all examples and can be used for your own extensions.
Contact your Partner Engineer for information about submitting your extension for signing.
Important: Your extension name must be globally unique across all BrightSign partners. When you submit for signing, BrightSign may require you to change the name to meet this requirement.
Once signed, the extension will be returned as a .bsfw file that can be applied to production (secure) players by placing it on the SD card. The extension will be installed on reboot.
- Open the DWS in a browser (typically
http://<player-ip>) - Navigate to Info > Extensions
- Installed extensions will be listed with their names and status
Connect via SSH and drop to the Linux shell (Ctrl-C, exit, exit), then:
# List installed extension volumes
ls /var/volatile/bsext/
# Check if a specific extension is mounted
mount | grep bsext
# View extension processes
ps | grep -E "(time_publisher|hello_world)"The recommended way to remove an extension is to perform a factory reset.
While it is technically possible to manually unmount and remove extension volumes (as shown in the individual example READMEs), a factory reset is the cleanest and most reliable method:
- Consult the Factory Reset Documentation
- A full hard factory reset (2-button approach) is recommended
This ensures all extension data is completely removed and the player is returned to a known good state.
To reset a player to factory state:
- Consult the Factory Reset Documentation
- A full hard factory reset (2-button approach) is recommended
This project is released under the terms of the Apache 2.0 License.