Strux OS Documentation
Home
Guide
Concepts
BSP Development
Reference
GitHub
Home
Guide
Concepts
BSP Development
Reference
GitHub
  • Reference

    • CLI Reference
    • strux.yaml Reference
    • Go Runtime Reference
    • Frontend API Reference

Frontend API Reference

Everything your web frontend can call on the Go backend: your app's own fields and methods, the built-in window.strux.* system services, and the bidirectional event channel. There is nothing to import or connect — the bindings are injected into the page as global objects before your code runs, and a generated declaration file gives you full TypeScript types for all of them. For a walkthrough, see the Frontend guide; for the Go side of every service listed here, see the Go Runtime reference.

How the bindings reach the page

On a Strux device, your frontend runs inside WPE WebKit under the Cage compositor (see the display stack). A Strux-built WPE extension runs inside the browser process: at page load it connects to the Go runtime's IPC socket (/tmp/strux-ipc.sock), asks the runtime for its bindings, and injects matching JavaScript objects into window. Method calls and field accesses are forwarded over that socket; there is no HTTP API and no WebSocket client to set up.

Bindings exist only on the device

window.go, window.strux, and your app global are injected by the WPE extension. They exist when your page runs on a Strux device or in the QEMU VM that strux dev boots — not when you open the page in a regular desktop browser.

How the typed API is generated

strux types

strux types statically analyzes your Go code and writes frontend/src/strux.d.ts. It also runs automatically during strux init and at the start of every strux build frontend step, so the file tracks your backend. The file uses declare global, so the types apply project-wide with no imports.

What happens under the hood:

  1. The strux-introspect binary (a Go AST analyzer shipped with the CLI) parses your main.go and every file in its package.
  2. It finds your app struct by locating the value passed to runtime.Start(...) or runtime.Init(...); if that fails, it falls back to a struct named App.
  3. It extracts the struct's exported fields and methods, follows struct-typed fields into your own packages (resolving them with go list), and converts Go types to TypeScript.
  4. It merges in the built-in runtime service types (a snapshot of pkg/runtime/api baked into the CLI, generated by cmd/gen-runtime-types) and the types of any BSP runtime extensions declared by your active BSP.
  5. The result is written to frontend/src/strux.d.ts. Don't edit it — rerun strux types instead.

Type mapping

Go typeTypeScript type
stringstring
int, int8–int64, uint–uint64, float32, float64number
boolboolean
[]T, ...TT[]
map[K]VRecord<K, V>
*TT (pointers are transparent)
interface{}any
named type over a primitive (e.g. type AudioOutput string)the underlying type (string)
your structsa generated interface with the same name

The globals

GlobalWhat it is
window.go.<package>.<Struct>Your app struct's bindings, e.g. window.go.main.App.
window.<Struct>A shortcut to the same object, e.g. window.App — what you'll normally use.
window.strux.<namespace>The built-in runtime services (boot, capabilities, dev, display, network, project, update, wifi) plus any custom BSP extensions.
window.strux.ipcThe event API: on, off, send.

Because they are window properties, all of these are also reachable as bare globals (App, strux), and the generated strux.d.ts declares them that way.

Your app's bindings

Given the template backend from strux init:

type App struct {
	Title   string
	Counter int
}

func (a *App) Greet(name string) string { return "Hello, " + name + "!" }
func (a *App) Add(x, y float64) float64 { return x + y }

strux types generates:

interface App {
  Title: string;
  Counter: number;

  Greet(name: string): Promise<string>;
  Add(x: number, y: number): Promise<number>;
}

const App: App;

And the frontend uses it directly:

const msg = await App.Greet("World")   // "Hello, World!"
const sum = await App.Add(2, 3)        // 5

console.log(App.Title)                 // read a Go field
App.Counter = 42                       // write a Go field

Methods

  • Every bound Go method becomes an async function returning a Promise, regardless of how fast the Go side is.
  • Parameters are positional and JSON-encoded. Names in the .d.ts come from your Go parameter names.
  • If the Go method's last return value is an error and it is non-nil, the promise rejects with the error message as a string. The generated return type adds | null for these methods (e.g. Promise<string | null>).
  • A method with no non-error returns resolves to void; one value resolves to that value; multiple values resolve to an array, typed as a tuple.

Fields

Exported primitive fields are injected as live properties: reading App.Counter performs a synchronous IPC read of the current Go value, and assigning to it writes the Go value. They look like plain values in TypeScript, but every access is a round-trip to the backend — for bulk reads, prefer a method that returns a snapshot struct.

Nested structs

Exported struct-typed fields on your app become nested objects with their own bound fields and methods. From the example project:

// Go: App.Settings.Audio (*settings.Audio) with method SetMasterVolume(volume float64)
await App.Settings.Audio.SetMasterVolume(0.8)
const vol = App.Settings.Audio.MasterVolume

Nil pointer fields in Go are skipped at bind time, so initialize nested structs before starting the runtime.

Naming and JSON encoding

Bindings for your structs keep their Go names exactly: App.SearchYouTube(...), result.Title. Struct values returned by your methods are serialized with Go's encoding/json, while the generated types always use the Go field names — so avoid json:"..." tags that rename fields on structs you return to the frontend, or the runtime values won't match the generated types. The built-in StruxRuntime.* types are the exception: they are declared with their camelCase JSON names (interfaceName, signalStrength, …), which is what the wire actually carries.

Runtime services: window.strux.*

The built-in services mirror the Go runtime services one-to-one — same methods, same semantics, with errors surfacing as promise rejections. This is the full generated interface:

interface Strux {
  boot: {
    HideSplash(): Promise<void>;
    Reboot(): Promise<void>;
    Shutdown(): Promise<void>;
  };
  capabilities: {
    List(): Promise<StruxRuntime.CapabilityInfo[]>;
    Supports(name: string): Promise<boolean>;
  };
  dev: {
    GetConfig(): Promise<StruxRuntime.DevState | null>;
    SetConfig(config: StruxRuntime.DevConfig): Promise<void>;
    SetEnabled(enabled: boolean): Promise<void>;
    Apply(config: StruxRuntime.DevConfig, enabled: boolean): Promise<void>;
    RestartService(): Promise<void>;
    ApplyAndRestart(config: StruxRuntime.DevConfig, enabled: boolean): Promise<void>;
  };
  display: {
    List(): Promise<StruxRuntime.DisplayOutput[] | null>;
    Get(name: string): Promise<StruxRuntime.DisplayOutput | null>;
    Apply(changes: StruxRuntime.DisplayOutputChange[], opts: StruxRuntime.DisplayApplyOptions): Promise<void>;
    SetListedMode(output: string, mode: StruxRuntime.ListedModeSelection, opts: StruxRuntime.DisplayApplyOptions): Promise<void>;
    SetCustomMode(output: string, mode: StruxRuntime.CustomModeSelection, opts: StruxRuntime.DisplayApplyOptions): Promise<void>;
    SetPreferredMode(output: string, opts: StruxRuntime.DisplayApplyOptions): Promise<void>;
    SetOutputEnabled(output: string, on: boolean, opts: StruxRuntime.DisplayApplyOptions): Promise<void>;
    SetLayout(output: string, x: number, y: number, opts: StruxRuntime.DisplayApplyOptions): Promise<void>;
    SetScale(output: string, scale: number, opts: StruxRuntime.DisplayApplyOptions): Promise<void>;
    SetTransform(output: string, transform: string, opts: StruxRuntime.DisplayApplyOptions): Promise<void>;
    GetBacklight(outputName: string): Promise<number | null>;
    SetBacklight(outputName: string, value: number): Promise<void>;
  };
  network: {
    ListInterfaces(): Promise<StruxRuntime.NetworkInterface[] | null>;
    GetDefaultInterface(kind: string): Promise<StruxRuntime.NetworkDefaultInterface | null>;
    GetStatus(interfaceName: string): Promise<StruxRuntime.NetworkStatus | null>;
    ConfigureIP(req: StruxRuntime.NetworkIPConfigRequest): Promise<void>;
    SetEnabled(interfaceName: string, enabled: boolean): Promise<void>;
    RenewDHCP(interfaceName: string): Promise<void>;
  };
  project: {
    Info(): Promise<StruxRuntime.ProjectInfo | null>;
  };
  update: {
    Progress(): Promise<StruxRuntime.UpdateProgress | null>;
    State(): Promise<StruxRuntime.UpdateState | null>;
  };
  wifi: {
    ListInterfaces(): Promise<StruxRuntime.WiFiInterface[] | null>;
    GetDefaultInterface(): Promise<StruxRuntime.WiFiDefaultInterface | null>;
    GetStatus(interfaceName: string): Promise<StruxRuntime.WiFiStatus | null>;
    Scan(interfaceName: string): Promise<StruxRuntime.WiFiNetwork[] | null>;
    Connect(req: StruxRuntime.WiFiConnectRequest): Promise<void>;
    ConnectKnown(req: StruxRuntime.WiFiKnownNetworkRequest): Promise<void>;
    Disconnect(interfaceName: string): Promise<void>;
    ListKnownNetworks(): Promise<StruxRuntime.WiFiKnownNetwork[] | null>;
    Forget(id: string): Promise<void>;
    SetKnownNetworkPriority(id: string, priority: number): Promise<void>;
    ConfigureIP(req: StruxRuntime.WiFiIPConfigRequest): Promise<void>;
  };
  ipc: {
    on(event: string, callback: (data: any) => void): () => void;
    off(event: string, callback: (data: any) => void): void;
    send(event: string, data?: any): void;
  };
}

The supporting data shapes (StruxRuntime.DisplayOutput, StruxRuntime.WiFiStatus, …) are declared in the same generated file under the StruxRuntime namespace; their field-by-field documentation lives in the Go Runtime reference.

A few service-specific notes:

  • display (backlight only), network, and wifi are BSP-dependent. On a BSP without the matching provider, those calls reject with capability <name> is not supported by the active BSP. Probe first: await strux.capabilities.Supports("wifi").
  • boot.HideSplash() is the call your frontend makes when it has rendered and is ready to replace the boot splash.
  • update reads the device's update progress and A/B state.

Experimental

A/B (dual-rootfs) updates are experimental; the shapes returned by strux.update.State() may change. See Dual Rootfs.

Example: a Wi-Fi settings screen.

if (await strux.capabilities.Supports("wifi")) {
  const def = await strux.wifi.GetDefaultInterface()
  if (def?.found) {
    const networks = await strux.wifi.Scan(def.interfaceName)
    await strux.wifi.Connect({
      interfaceName: def.interfaceName,
      ssid: "MyNetwork",
      password: "hunter22",
      bssid: "",
    })
  }
}

Events: strux.ipc

Events are named messages that flow in both directions, separate from method calls — use them when Go needs to push to the page.

FunctionDescription
strux.ipc.on(event, callback)Listens for events emitted from Go with rt.Emit(event, data). Returns an unsubscribe function.
strux.ipc.off(event, callback)Removes a listener registered with on (same callback reference).
strux.ipc.send(event, data?)Sends an event to Go, where rt.On(event, handler) receives it.

The template project wires up a round trip — Go side:

rt.On("hello", func(data interface{}) {
	rt.Emit("hello-reply", map[string]string{"message": "Hello from Go!"})
})

Frontend side:

const unsubscribe = strux.ipc.on("hello-reply", (data) => {
  console.log(data.message) // "Hello from Go!"
})

strux.ipc.send("hello", { from: "frontend" })

// later, e.g. in a component unmount hook:
unsubscribe()

Event payloads are JSON-encoded; they are typed as any, so define your own types for them. rt.Emit broadcasts to all connected frontends, which makes events the natural way to sync multiple views of the same device.

BSP extensions

A BSP can expose extra hardware APIs under window.strux.<name> via runtime.RegisterCustomExtension (or runtime.RegisterExtension for other namespaces) — see Runtime Extensions. When your active BSP declares runtime extensions, strux types parses their Go source too and includes their namespaces and data types in frontend/src/strux.d.ts, so they are as fully typed as the built-in services. Call semantics are identical: exported methods, promises, error rejection.

Last Updated:: 6/13/26, 2:20 AM
Contributors: Miguel Medeiros
Prev
Go Runtime Reference