Go Runtime Reference
Every exported function, method, and type in the Strux Go runtime library (github.com/strux-dev/strux/pkg/runtime). This is the package your main.go imports: it exposes your app to the frontend, serves your built frontend over HTTP, carries events in both directions, and provides the built-in system services. For a task-oriented introduction, see the Backend guide; for the JavaScript side of everything on this page, see the Frontend API reference.
Creating and starting the runtime
// Start creates the runtime, starts the IPC bridge, and blocks on the HTTP server.
func Start(app interface{}) error
// Init creates the runtime and starts the IPC bridge without blocking.
// Use this instead of Start when you need the Runtime for events or services.
func Init(app interface{}) (*Runtime, error)
// Serve starts the HTTP server and blocks until it exits.
func (rt *Runtime) Serve() error
// Stop shuts down the IPC server and removes the socket.
func (rt *Runtime) Stop()
The minimal pattern (no events) is runtime.Start(app). The template project generated by strux init uses Init so it can register event handlers before serving:
package main
import (
"log"
"github.com/strux-dev/strux/pkg/runtime"
)
// App is the main application struct.
// All public fields and methods are exposed to the frontend.
type App struct {
Title string
Counter int
}
// Greet returns a greeting message.
func (a *App) Greet(name string) string {
return "Hello, " + name + "!"
}
func main() {
app := &App{Title: "my-kiosk", Counter: 0}
rt, err := runtime.Init(app)
if err != nil {
log.Fatal(err)
}
defer rt.Stop()
rt.On("hello", func(data interface{}) {
rt.Emit("hello-reply", map[string]string{"message": "Hello from Go!"})
})
if err := rt.Serve(); err != nil {
log.Fatal(err)
}
}
Init returns an error if the IPC socket cannot be created. Start additionally returns the HTTP server's error when it exits.
What gets exposed
When the runtime is created, it walks your app struct with reflection and binds:
- Exported methods (pointer and value receivers) — callable from the frontend, with JSON-encoded parameters and return values.
- Exported primitive fields — readable and writable from the frontend.
- Exported struct fields — become nested namespaces; their exported methods and fields are bound recursively under a dotted path (e.g.
Settings.Audio.SetMasterVolume). Pointer fields are dereferenced; nil pointer fields are skipped, so initialize nested structs before callingInit/Start. - Unexported fields and methods are ignored entirely.
IPC bridge and HTTP server
- The IPC bridge listens on the Unix socket
/tmp/strux-ipc.sock. The WPE WebKit extension on the device connects to it and injects the JavaScript bindings — you never talk to this socket yourself. Servelistens on127.0.0.1:8080by default; set theSTRUX_HTTP_ADDRenvironment variable to override.- Static files are served from
/strux/frontendwhen that directory exists (the location in a built image), otherwise from./frontend. - Any path that doesn't match a real file falls back to
index.html, so client-side routers (Vue Router, React Router) work. GET /__strux/healthreturns204 No Content— a health check endpoint.
Method call semantics
Methods bound from your app struct (and from extensions) follow these rules when called from the frontend:
- Parameters are positional and decoded from JSON into the Go parameter types. A wrong parameter count or an undecodable value returns an error to the caller.
- If the method's last return value is an
errorand it is non-nil, the call fails and the frontend promise rejects with the error message. - Zero non-error return values resolve to nothing, one resolves to that value, and multiple resolve to an array.
Services
The Runtime exposes built-in system services through accessor methods. Each accessor returns a stateless service value, so call it wherever you need it:
rt.Boot().Reboot()
level, err := rt.Display().GetBacklight("HDMI-A-1")
Every service is also exposed to the frontend under window.strux.<namespace> — see the Frontend API reference.
Three services — Display backlight, Network, and Wi-Fi — are backed by BSP capability providers. If the active BSP has not registered a provider, those methods return api.UnsupportedError (message: capability <name> is not supported by the active BSP). Check support at runtime with the Capabilities service before relying on them.
Boot
rt.Boot() *api.BootService — namespace boot. Boot and power management.
func (b *BootService) HideSplash() error
func (b *BootService) Reboot() error
func (b *BootService) Shutdown() error
| Method | Description |
|---|---|
HideSplash | Tells the Cage compositor (via its control socket /tmp/strux-cage-control.sock) to hide the boot splash and reveal your app. Call this when your frontend is ready to be seen. If the socket doesn't exist or refuses the connection (e.g. in dev mode), it returns nil instead of an error. |
Reboot | Reboots the device (runs reboot). |
Shutdown | Powers the device off (runs poweroff). |
Capabilities
rt.Capabilities() *api.CapabilitiesService — namespace capabilities. Tells you which BSP-backed capabilities are available on the running device.
func (s *CapabilitiesService) List() []CapabilityInfo
func (s *CapabilitiesService) Supports(name string) bool
| Method | Description |
|---|---|
List | Returns every defined capability with its name, namespace, description, declared methods, whether a provider is registered, and the provider's Go type name. |
Supports | Returns true when the named capability has a registered provider. Unknown names return false. |
The capability names defined today are "display" (backlight control), "network", and "wifi".
type CapabilityInfo struct {
Name string `json:"name"`
Namespace string `json:"namespace"`
Description string `json:"description,omitempty"`
Supported bool `json:"supported"`
Provider string `json:"provider,omitempty"`
Methods []MethodSpec `json:"methods"`
}
Dev
rt.Dev() *api.DevService — namespace dev. Reads and writes the on-device dev-mode configuration, so a kiosk can expose a settings screen that enables dev mode without reflashing. The active config lives at /strux/.dev-env.json; a stored-but-disabled config lives at /strux/.dev-env.json.disabled.
func (d *DevService) GetConfig() (DevState, error)
func (d *DevService) SetConfig(config DevConfig) error
func (d *DevService) SetEnabled(enabled bool) error
func (d *DevService) Apply(config DevConfig, enabled bool) error
func (d *DevService) RestartService() error
func (d *DevService) ApplyAndRestart(config DevConfig, enabled bool) error
| Method | Description |
|---|---|
GetConfig | Returns the stored config plus whether dev mode is currently enabled. If no config file exists, returns defaults (mDNS on, inspector off on port 9223, USB networking on with subnet 192.168.7.0/24). |
SetConfig | Validates and writes the config without changing whether dev mode is enabled. |
SetEnabled | Enables or disables dev mode by renaming the config file between its active and disabled paths. Enabling fails if no config was stored (no stored dev config found; call SetConfig or Apply first) or if clientKey is empty. |
Apply | Validates, stores, and toggles in one call. |
RestartService | Restarts the strux systemd service ~500 ms after returning, so the caller's IPC response is delivered first. |
ApplyAndRestart | Apply followed by RestartService. |
type DevConfig struct {
ClientKey string `json:"clientKey"`
UseMDNS bool `json:"useMDNS"`
FallbackHosts []DevHost `json:"fallbackHosts"`
Inspector DevInspectorConfig `json:"inspector"`
USB DevUSBConfig `json:"usb"`
}
type DevHost struct {
Host string `json:"host"`
Port int `json:"port"`
}
type DevInspectorConfig struct {
Enabled bool `json:"enabled"`
Port int `json:"port"`
}
type DevUSBConfig struct {
Enabled bool `json:"enabled"`
Subnet string `json:"subnet"`
}
type DevState struct {
Enabled bool `json:"enabled"`
Config DevConfig `json:"config"`
}
Validation errors: inspector.port must be greater than 0; usb.subnet must be an IPv4 CIDR with at least two usable addresses (prefix length ≤ 30); every fallback host needs a non-empty host and a port greater than 0; clientKey is required only to enable dev mode.
Display
rt.Display() *api.DisplayService — namespace display. Lists and configures display outputs, and controls backlight.
Output listing and configuration are implemented by the runtime itself by running the wlr-randr command-line tool against the Cage compositor (with a 30-second timeout per invocation). Only GetBacklight/SetBacklight go through the BSP's display provider, because backlight hardware is board-specific.
What is wlr-randr?
wlr-randr is a small utility that queries and configures display outputs on wlroots-based Wayland compositors like Cage — resolution, refresh rate, rotation, position, and scale. The runtime shells out to it; if the binary is not on PATH, display methods return an error (wlr-randr not found in PATH).
func (DisplayService) List() ([]DisplayOutput, error)
func (DisplayService) Get(name string) (DisplayOutput, error)
func (DisplayService) Apply(changes []DisplayOutputChange, opts DisplayApplyOptions) error
func (DisplayService) SetListedMode(output string, mode ListedModeSelection, opts DisplayApplyOptions) error
func (DisplayService) SetCustomMode(output string, mode CustomModeSelection, opts DisplayApplyOptions) error
func (DisplayService) SetPreferredMode(output string, opts DisplayApplyOptions) error
func (DisplayService) SetOutputEnabled(output string, on bool, opts DisplayApplyOptions) error
func (DisplayService) SetLayout(output string, x int32, y int32, opts DisplayApplyOptions) error
func (DisplayService) SetScale(output string, scale float64, opts DisplayApplyOptions) error
func (DisplayService) SetTransform(output string, transform OutputTransform, opts DisplayApplyOptions) error
func (DisplayService) GetBacklight(outputName string) (int, error)
func (DisplayService) SetBacklight(outputName string, value int) error
| Method | Description |
|---|---|
List | Returns every connected logical display with its advertised timings, current mode, physical size, layout position, scale, and transform. |
Get | Returns the same snapshot for one output name. Returns ErrUnknownDisplayOutput if the name doesn't match a connected output. |
Apply | Applies several output changes in one batch, so multi-display kiosks stay consistent. Returns ErrNoDisplayOutputChanges for an empty batch. |
SetListedMode | Switches one output to an advertised width × height (and optional refresh rate in millihertz; 0 matches the first entry for that size). |
SetCustomMode | Drives an output with a timing that may not appear in its advertised list. |
SetPreferredMode | Selects the output's preferred timing when the driver exposes one. |
SetOutputEnabled | Turns the compositor's video output to that display on or off. |
SetLayout | Sets the output's position in the global compositor layout. |
SetScale | Sets fractional UI scaling for that output. |
SetTransform | Sets rotation or mirroring. Valid OutputTransform values: normal, 90, 180, 270, flipped, flipped-90, flipped-180, flipped-270. |
GetBacklight | Returns the backlight level (typically 0–100). Requires the display capability provider; otherwise returns UnsupportedError. |
SetBacklight | Sets the backlight level. The value must be between 0 and 100; requires the display capability provider. |
Pass DisplayApplyOptions{DryRun: true} to any setter to validate a change without committing it.
type DisplayOutput struct {
Name string `json:"name"`
Description string `json:"description"`
PhysicalWidthMM int32 `json:"physicalWidthMm"`
PhysicalHeightMM int32 `json:"physicalHeightMm"`
Enabled bool `json:"enabled"`
Modes []DisplayMode `json:"modes"`
Current *DisplayMode `json:"current,omitempty"`
PositionX int32 `json:"positionX"`
PositionY int32 `json:"positionY"`
Transform OutputTransform `json:"transform"`
Scale float64 `json:"scale"`
}
type DisplayMode struct {
WidthPX int `json:"widthPx"`
HeightPX int `json:"heightPx"`
RefreshHz float64 `json:"refreshHz,omitempty"`
Preferred bool `json:"preferred"`
IsCurrent bool `json:"current"`
}
type DisplayOutputChange struct {
Name string `json:"name"`
On *bool `json:"on,omitempty"`
ListedMode *ListedModeSelection `json:"listedMode,omitempty"`
CustomMode *CustomModeSelection `json:"customMode,omitempty"`
UsePreferred bool `json:"usePreferred,omitempty"`
PositionX *int32 `json:"positionX,omitempty"`
PositionY *int32 `json:"positionY,omitempty"`
Scale *float64 `json:"scale,omitempty"`
Transform *OutputTransform `json:"transform,omitempty"`
}
Network
rt.Network() *api.NetworkService — namespace network. Generic (wired-first) network interface management. Every method requires the network capability provider from the active BSP; otherwise it returns UnsupportedError. See Runtime Extensions for how a BSP supplies one.
func (NetworkService) ListInterfaces() ([]NetworkInterface, error)
func (NetworkService) GetDefaultInterface(kind string) (NetworkDefaultInterface, error)
func (NetworkService) GetStatus(interfaceName string) (NetworkStatus, error)
func (NetworkService) ConfigureIP(req NetworkIPConfigRequest) error
func (NetworkService) SetEnabled(interfaceName string, enabled bool) error
func (NetworkService) RenewDHCP(interfaceName string) error
| Method | Description |
|---|---|
ListInterfaces | Returns every managed network interface exposed by the BSP. |
GetDefaultInterface | Returns the preferred interface for a kind. Valid kinds: ethernet, wifi, cellular, usb, loopback, unknown, or "" for any. Other values return an error. |
GetStatus | Returns link and IPv4 state for one interface. |
ConfigureIP | Applies dhcp, static, or disabled IPv4 settings. For static, the address must be a valid IPv4 address; gateway, subnet, and DNS entries are validated as IPv4 when present. |
SetEnabled | Enables or disables one interface. |
RenewDHCP | Renews the DHCP lease on one interface. |
Interface names are validated against ^[A-Za-z0-9][A-Za-z0-9_.-]{0,14}$; invalid or empty names return an error before the provider is consulted.
type NetworkIPConfig struct {
Mode string `json:"mode"` // "dhcp", "static", or "disabled"
Address string `json:"address"`
Gateway string `json:"gateway"`
Subnet string `json:"subnet"`
DNS []string `json:"dns"`
}
type NetworkStatus struct {
InterfaceName string `json:"interfaceName"`
Kind string `json:"kind"`
Connected bool `json:"connected"`
LinkDetected bool `json:"linkDetected"`
HardwareAddress string `json:"hardwareAddress"`
SpeedMbps int `json:"speedMbps"`
Duplex string `json:"duplex"`
IP NetworkIPConfig `json:"ip"`
}
(NetworkInterface, NetworkDefaultInterface, and NetworkIPConfigRequest are exported with matching JSON field names — see pkg/runtime/api/network.go.)
Project
rt.Project() *api.ProjectService — namespace project. Metadata about the image the device is running.
func (p *ProjectService) Info() (ProjectInfo, error)
Info reads /etc/strux/project.json from the image and returns it. It returns an error if the file is missing or malformed.
type ProjectInfo struct {
Name string `json:"name"`
ProjectVersion string `json:"projectVersion"`
StruxVersion string `json:"struxVersion"`
BSP string `json:"bsp"`
Arch string `json:"arch"`
BuiltAt string `json:"builtAt"`
}
Update
rt.Update() *api.UpdateService — namespace update. Read-only view of system update progress and state, written by the on-device strux-client. See Updates and the update system concept page.
Experimental
A/B (dual-rootfs) updates are experimental. The UpdateState fields describing slots and boot tries may change. See Dual Rootfs.
func (u *UpdateService) Progress() (*UpdateProgress, error)
func (u *UpdateService) State() (*UpdateState, error)
| Method | Description |
|---|---|
Progress | Returns the latest progress from /run/strux/update-progress.json, or nil (no error) when no update has reported progress. |
State | Returns persisted A/B boot state from /strux-data/strux/update-state.json, or nil (no error) when the file doesn't exist. |
type UpdateProgress struct {
Status string `json:"status"`
Progress int `json:"progress"`
Message string `json:"message,omitempty"`
BytesWritten int64 `json:"bytesWritten,omitempty"`
TotalBytes int64 `json:"totalBytes,omitempty"`
Slot string `json:"slot,omitempty"`
Version string `json:"version,omitempty"`
UpdatedAt string `json:"updatedAt,omitempty"`
}
type UpdateState struct {
Version int `json:"version"`
ActiveSlot string `json:"activeSlot"`
PendingSlot string `json:"pendingSlot"`
TriesRemaining int `json:"triesRemaining"`
Generation int `json:"generation"`
LastGoodAt string `json:"lastGoodAt,omitempty"`
LastError string `json:"lastError"`
}
WiFi
rt.WiFi() *api.WiFiService — namespace wifi. Wi-Fi scanning, connection management, saved profiles, and IP configuration. Every method requires the wifi capability provider from the active BSP; otherwise it returns UnsupportedError.
func (WiFiService) ListInterfaces() ([]WiFiInterface, error)
func (WiFiService) GetDefaultInterface() (WiFiDefaultInterface, error)
func (WiFiService) GetStatus(interfaceName string) (WiFiStatus, error)
func (WiFiService) Scan(interfaceName string) ([]WiFiNetwork, error)
func (WiFiService) Connect(req WiFiConnectRequest) error
func (WiFiService) ConnectKnown(req WiFiKnownNetworkRequest) error
func (WiFiService) Disconnect(interfaceName string) error
func (WiFiService) ListKnownNetworks() ([]WiFiKnownNetwork, error)
func (WiFiService) Forget(id string) error
func (WiFiService) SetKnownNetworkPriority(id string, priority int) error
func (WiFiService) ConfigureIP(req WiFiIPConfigRequest) error
| Method | Description |
|---|---|
ListInterfaces | Returns every Wi-Fi-capable interface, with capability flags (supportsScan, supportsAPMode, supports5GHz, supports6GHz). |
GetDefaultInterface | Returns the preferred Wi-Fi adapter for apps that don't need explicit adapter selection. |
GetStatus | Returns connection and IPv4 state for one interface. |
Scan | Scans for nearby access points on one interface. |
Connect | Connects to an SSID, optionally with a password and BSSID. The SSID must be non-empty; SSID, password, and BSSID must not contain NUL bytes. |
ConnectKnown | Activates a saved profile by its stable ID on one interface. |
Disconnect | Disconnects one interface. |
ListKnownNetworks | Returns saved connection profiles. |
Forget | Deletes one saved profile by ID. |
SetKnownNetworkPriority | Sets a saved profile's autoconnect priority. |
ConfigureIP | Applies dhcp or static IPv4 settings to the active connection. static requires a non-empty address. |
Interface names follow the same validation as the Network service. Profile IDs must be non-empty and free of NUL bytes. All request and result structs (WiFiInterface, WiFiNetwork, WiFiStatus, WiFiKnownNetwork, WiFiConnectRequest, WiFiKnownNetworkRequest, WiFiIPConfig, WiFiIPConfigRequest, WiFiDefaultInterface) are exported from the runtime package — see pkg/runtime/api/wifi.go for their JSON field names.
Events
The runtime carries named events in both directions between Go and the frontend over a dedicated channel.
// Emit sends an event to all connected frontends.
func (rt *Runtime) Emit(event string, data interface{})
// On registers a handler for events sent from the frontend.
// It returns a handler ID for Off.
func (rt *Runtime) On(event string, handler func(data interface{})) uint64
// Off removes a previously registered handler by its ID.
func (rt *Runtime) Off(id uint64)
EmitJSON-encodes the payload and broadcasts it to every connected frontend. Broken connections are dropped silently. If encoding fails, the event is logged and dropped.Onhandlers run in their own goroutine per event, so a slow handler doesn't block the event loop — synchronize shared state yourself.- The frontend counterpart is
strux.ipc.on()/strux.ipc.off()/strux.ipc.send()— see Events in the Frontend API reference.
A common pattern from a real project (the same OS image rendering two browser views) is relaying events between frontends:
for _, evt := range []string{"video:play", "video:pause", "video:stop"} {
e := evt
rt.On(e, func(data any) {
rt.Emit(e, data) // rebroadcast to all connected frontends
})
}
The exported event types:
type EventMessage struct {
Type string `json:"type"`
Event string `json:"event"`
Data interface{} `json:"data,omitempty"`
}
type EventHandler struct {
ID uint64
Callback func(data interface{})
}
Provider registration (BSP extensions)
These functions let a BSP package supply the hardware-specific implementation behind the Display backlight, Network, and WiFi services. They are meant to be called from a BSP runtime extension's init() function — see Runtime Extensions for the full workflow and the extension system concept page for how extensions are wired into the build.
func RegisterDisplayProvider(provider DisplayProvider)
func RegisterNetworkProvider(provider NetworkProvider)
func RegisterWiFiProvider(provider WiFiProvider)
Each function panics if the provider is nil or if a provider for that capability is already registered — a misconfigured BSP should fail loudly at startup, not at first use. Once registered, the matching capability reports Supported: true and the service methods route to the provider.
The provider interfaces (re-exported from pkg/runtime/api):
type DisplayProvider interface {
GetBacklight(displayName string) (int, error)
SetBacklight(displayName string, value int) error
}
type NetworkProvider interface {
ListInterfaces() ([]NetworkInterface, error)
GetDefaultInterface(kind string) (NetworkDefaultInterface, error)
GetStatus(interfaceName string) (NetworkStatus, error)
ConfigureIP(req NetworkIPConfigRequest) error
SetEnabled(interfaceName string, enabled bool) error
RenewDHCP(interfaceName string) error
}
type WiFiProvider interface {
ListInterfaces() ([]WiFiInterface, error)
GetDefaultInterface() (WiFiDefaultInterface, error)
GetStatus(interfaceName string) (WiFiStatus, error)
Scan(interfaceName string) ([]WiFiNetwork, error)
Connect(req WiFiConnectRequest) error
ConnectKnown(req WiFiKnownNetworkRequest) error
Disconnect(interfaceName string) error
ListKnownNetworks() ([]WiFiKnownNetwork, error)
Forget(id string) error
SetKnownNetworkPriority(id string, priority int) error
ConfigureIP(req WiFiIPConfigRequest) error
}
The capability name constants are also re-exported: runtime.CapabilityDisplay, runtime.CapabilityNetwork, runtime.CapabilityWiFi.
Custom extensions
Beyond the standard capabilities, a BSP (or your own code) can expose an arbitrary Go object's exported methods to the frontend under a namespace:
// Process-wide: every Runtime created afterwards exposes the methods.
// Intended for BSP package init() functions. Panics on invalid or duplicate registration.
func RegisterCustomExtension(name string, instance interface{})
// Process-wide, with an explicit namespace. Returns an error instead of panicking.
func RegisterExtension(namespace, subNamespace string, instance interface{}) error
// Instance-scoped: registers on one Runtime only.
func (rt *Runtime) RegisterExtension(namespace, subNamespace string, instance interface{}) error
RegisterCustomExtension("gpio", &GPIO{}) makes the methods of GPIO callable as window.strux.gpio.<Method>() in the frontend. Registration fails (error or panic) when the namespace or sub-namespace is empty, the instance is nil, or the pair is already registered. Method call semantics are the same as for app methods. See Runtime Extensions for how strux types picks these up and generates frontend types for them.
Lower-level exports
You will rarely need these, but they are part of the public API:
| Export | Description |
|---|---|
New(app interface{}) *Runtime | Creates a Runtime (builds the binding tree, registers built-in and process-wide extensions) without starting the IPC listener. Init is New + (rt) Start. |
(rt) Start() error | Starts the IPC listener on /tmp/strux-ipc.sock. Called for you by Init/Start. |
(rt) GetMethodInfo() []MethodInfo | Metadata (name, parameter count, parameter kinds) for the app struct's top-level bound methods. |
(rt) GetFieldInfo() []FieldInfo | Metadata (name, kind) for the app struct's top-level bound primitive fields. |
(rt) GenerateTypeScript(outputPath string) error | Writes a TypeScript declaration file for the current bindings. The strux types command (which uses static analysis and produces richer types) is the recommended way to generate frontend types — see the Frontend API reference. |
Message, Response, MethodInfo, FieldInfo, ChannelHandshake | Wire-format types for the JSON-RPC style IPC protocol. |
Registry | The extension registry used internally by the Runtime. |
| Type aliases | All capability data types (NetworkInterface, WiFiStatus, CapabilityInfo, …) are aliased from pkg/runtime/api into the runtime package, so user code only needs the one import. |