Strux OS

The open-source framework and device runtime.

You write a web frontend and a Go backend. Strux turns them into a complete, bootable image for your target hardware, kernel and bootloader included, and gives you one workflow to develop, build, flash, and update it.

curl -fsSL https://strux.sh/install.sh | sh

Install

One command downloads the CLI for your machine and puts it on your PATH. Builds run inside Docker, so the host stays clean.

curl -fsSL https://strux.sh/install.sh | sh

The script picks the right binary for Linux x64 and arm64 and for macOS on Intel and Apple silicon. Windows users download the executable from the releases page, where Debian and RPM packages and every plain binary are published too.

# check the install

$ strux --version

0.3.0

On your machineWhy
DockerrequiredEvery build step runs inside the strux-builder container.
Go 1.24+requiredCompiles your backend in dev mode and powers type generation.
Node.js and npmrequiredScaffolds and runs your frontend with Vite.
QEMUoptionalBoots your image on your laptop for strux run and strux dev.

A project is two files and a board folder.

The frontend is a normal Vite project. The backend is one Go file whose exported fields and methods become the page's typed API. Everything hardware-specific lives under the board folder, so your app code stays the same across boards.

my-kiosk/

├── strux.yaml project configuration

├── main.go Go backend entry point

├── go.mod / go.sum pinned to the runtime matching your CLI

├── frontend/ your web app (Vite project)

│ └── src/strux.d.ts generated TypeScript types for the backend

├── bsp/

│ └── qemu/ board profile for local testing

│ ├── bsp.yaml BSP configuration

│ ├── scripts/ lifecycle scripts (make-image.sh)

│ └── overlay/ board-specific filesystem overlay

├── assets/

│ └── logo.png splash screen logo

├── overlay/ global filesystem overlay

├── strux-update.key update signing key (private, gitignored)

├── strux-update.pub update verification key (built into images)

└── .gitignore

Dev mode is where you spend your time.

One session builds a development image, boots it, and streams your changes to it. Frontend edits hot-reload through Vite. Go edits recompile and push the new binary in seconds. No reboot, no reflash.

strux dev

remote board

strux dev --remote --bsp rt101-rk3568
struxDev server listening on port 8000
struxmDNS published: _strux-dev._tcp on port 8000
struxFile watcher started
struxStarting Vite dev server...
struxVite dev server started on http://localhost:5173
struxStrux Local MCP: http://localhost:8000/mcp
struxDevice connected: front-desk-01
  • QEMU on your machine

    A virtual machine with GPU acceleration where available, USB passthrough, and a serial console. No hardware needed.

  • Real boards on the bench

    Boards find the dev server over mDNS, or over a USB cable when there is no network. Each board gets its own dev server.

  • A terminal UI with the whole picture

    Logs from the app, the compositor, the system, Vite, and QEMU in one place, with filtering, an actions panel, and a build pane.

  • Headless for agents

    The same session with plain log output on stdout, and the MCP endpoint for coding agents.

The full dev mode tour

A build pipeline that knows what changed.

Every step runs inside the builder container and tracks its own inputs: source files, individual keys of your configuration, the builder image itself. A rebuild reruns only the steps whose inputs changed, and the first build is the slow one.

  1. 01FrontendGenerate strux.d.ts, then bundle the web app with Vite.
  2. 02ApplicationCross-compile the Go backend for the target architecture.
  3. 03Cage compositorCompile the Wayland compositor that runs the kiosk full-screen.
  4. 04WPE extensionCompile the bridge between WebKit and your Go process.
  5. 05Strux clientCompile the on-device lifecycle, update, and fleet agent.
  6. 06Base rootfsBootstrap a minimal Debian root filesystem.
  7. 07KernelCompile the board's kernel when the BSP configures one.
  8. 08BootloaderBuild U-Boot or GRUB when the BSP configures one.
  9. 09Post rootfsInstall packages, apply overlays, configure services.
  10. 10BSP scriptsRun the board's lifecycle hooks.
  11. 11BundleCollect every artifact for the image.
  12. 12Image assemblyWrite the bootable disk image, and the signed update bundle when updates are enabled.

The runtime: one API surface on every board.

Everything your app can call beyond its own code lives under window.strux in the page and on the Runtime object in Go. Built-in services work on every board. Board capabilities are stable Strux APIs whose implementation comes from the board support package.

main.go

You write

type App struct {
Title string
Counter int
}
 
func (a *App) Greet(name string) string {
return "Hello, " + name + "!"
}

frontend/src/strux.d.ts

Generated

interface App {
Title: string
Counter: number
Greet(name: string): Promise<string>
}

frontend/src/App.vue

You write

const greeting = await window.go.main.App.Greet("World")
 
if (await strux.capabilities.Supports("battery")) {
strux.battery.on("changed", (state) => render(state.percent))
}

Built in

Every board, including QEMU
  • strux.boot

    Hide the boot splash, reboot, power off.

  • strux.dev

    Read and change the on-device dev-mode configuration.

  • strux.display

    List and configure outputs: modes, layout, rotation, scale.

  • strux.profiles

    The application profile selected at build time.

  • strux.project

    Name, versions, BSP, architecture, and build time of the running image.

  • strux.system

    BSP name, CPU architecture, hardware model, hostname.

  • strux.update

    Update progress and A/B slot state.

  • strux.capabilities

    Which capabilities and optional features this device implements.

  • strux.ipc

    Your app's own event bus between Go and the page.

Board capabilities

Provided by the BSP
  • strux.audio

    Master volume, mute, output routing. Optional auto-switching and capture.

  • strux.battery

    Battery packs, power sources, aggregate charge, critical shutdown.

  • strux.cellular

    Mobile data: SIM, registration, signal, data session, APN settings.

  • strux.deviceSigning

    A device-bound signing key and challenge signing in a trusted backend.

  • strux.network

    Wired-first interface management, DHCP and static IPv4.

  • strux.wifi

    Scanning, connecting, saved profiles, IP configuration.

Ask before you call

A board that lacks a capability rejects every call to it with a clear error. The frontend asks strux.capabilities first, which is exactly what QEMU makes you handle in dev mode.

Optional features

A capability can declare method groups a board may implement on top of the contract, such as a microphone on an audio board. Availability is reported per feature.

Typed events

Services that own hardware push updates on their own channel, separate from your app's event bus, with payloads the runtime type-checks on the Go side.

One project, several products, several screens.

A profile tells your frontend which product experience an image should show, without teaching it board names. The display section maps pages of the same app to physical outputs, so one device can drive a touch panel and a wall display at once.

strux.yaml

profiles

bsp: qemu

profiles:
  - name: kiosk
    label: Self-service kiosk
    bsp:
      - qemu
      - rt101-rk3568

  - name: wallboard
    label: Production wallboard
    bsp:
      - qemu
      - hd215-rk3576

strux.yaml

display

display:
  monitors:
    - path: /
      resolution: 1920x1080
      transform: 90
      names: [DSI-1, Virtual-1]
    - path: /tv
      resolution: 1280x720
      names: [HDMI-A-1, Virtual-2]

Updates replace the whole OS, atomically.

A bundle is a complete root filesystem with a signed manifest. The device streams it into its idle slot, verifies it twice, and asks the bootloader to try the new slot a bounded number of times. A broken update rolls back by itself.

  • Manifests are signed with RSA-PSS by a 4096-bit key the project owns. Keys under 4096 bits are refused, and there is no unsigned mode.
  • The manifest pins the payload by size and SHA-256, so verifying the small manifest transitively verifies the whole image.
  • The archive puts the manifest before the payload, which is what lets the device install straight from the stream with no temporary copy.
  • A bundle names the board it was built for. A device refuses a bundle built for another.
  • The boot environment is written twice so a power cut cannot lose the slot state.

strux.yaml

updates

update:
  enabled: true
  auto_bundle: true

# sign a bundle from the last build, then send it to a device

$ strux update bundle
$ strux update send dist/output/hd215-rk3576/rootfs.ext4.struxb

# or let a Fleet deliver it

$ strux registry publish --channel stable

Board support packages hold everything hardware-specific.

A BSP is a folder: the kernel and bootloader configuration, a filesystem overlay, lifecycle scripts that run at fixed points of the build, runtime extensions that provide capabilities, and the script that flashes the board. Your project ships with the QEMU one and grows a folder per board.

Kernel

Source, version, defconfig, configuration fragments, patches, device trees, and overlays. Open the kernel's own menuconfig inside the builder when you need it.

Bootloader

U-Boot, GRUB, systemd-boot, or a custom build, with the firmware blobs and device trees the board expects.

Lifecycle scripts

Hooks before and after every pipeline step, the image assembly step itself, and the flash script that runs on your machine.

Runtime extensions

Go providers for the capabilities the board can back: audio, battery, cellular, network, Wi-Fi, display backlight, and your own namespaces.

Overlay and packages

Files copied into the root filesystem for this board only, and the Debian packages it needs on top of the base.

Version compatibility

An extension can declare which Strux API versions it supports, so one BSP carries implementations for several releases from a single tree.

Every tool an agent needs, on one endpoint.

Strux dev embeds an MCP server with tools to observe the device, interact with it, and build and deploy, plus the documentation for the exact CLI version, embedded and searchable offline.

Strux Local MCP

strux dev

Claude Code, connected to strux dev over MCP
screenshot{ output: "DSI-1", maxWidth: 1280 }
1280 x 800 JPEG
click{ x: 812, y: 466 }
device_logs{ source: "app", grep: "checkout", limit: 20 }
3 lines
rebuild_app{}
job 12 finished: binary pushed, app restarted
screenshot{ output: "DSI-1" }
1280 x 800 JPEG
  • Observe

    device_status device_logs screenshot backend_bindings docs_search docs_read docs_list

  • Interact

    click drag scroll pointer_move key type_text frontend_eval backend_call exec push_file

  • Build and deploy

    watcher rebuild_app rebuild_components build_image create_update_bundle install_update flash_device reboot_device

Agents and Strux Local MCP

Questions people ask first.

Building single-purpose devices: kiosks, digital signage, point-of-sale terminals, control panels, and exhibits. You write a web frontend and a Go backend; Strux turns them into a bootable Linux image and gives you one workflow for developing, building, updating, and operating it.