How to use the goja-dbus module from JavaScript and bundled jsverbs.
This guide describes how JavaScript code should use the dbus module. The module follows one rule: JavaScript describes D-Bus intent, and Go owns execution, policy checks, type marshaling, cleanup, and Goja runtime scheduling.
The current implementation supports client-side method calls, explicit typed values, EventEmitter-based signal subscriptions, and runtime-owned cleanup. JavaScript-backed D-Bus service export remains deferred until the codec and callback-dispatch model are stronger.
Use CommonJS require inside the generated xgoja runtime:
const dbus = require("dbus");
The xgoja spec selects the dbus runtime module from the goja-dbus provider, so the generated binary can resolve this import in eval, run, and bundled jsverbs.
D-Bus has a precise type system, while JavaScript has broad Number, array, and object values. Use helper wrappers when the D-Bus signature matters:
const count = dbus.u32(7);
const path = dbus.path("/com/example/App1");
const hint = dbus.variant("s", "hello");
const names = dbus.array("as", ["one", "two"]);
const hints = dbus.dict("a{sv}", { urgency: dbus.variant("u", dbus.u32(1)) });
const pair = dbus.struct("(su)", ["count", dbus.u32(7)]);
Typed helper objects contain a signature and value field. They are primarily for the adapter and examples; application code should not mutate them.
Use a builder to choose the bus and set optional policy or timeout values:
const bus = await dbus.session().timeout(2000).connect();
The default policy allows the session bus and denies the system bus. This keeps safe examples runnable without accidentally exposing privileged host APIs.
Build calls by selecting a destination, object path, interface, and method. Inputs and outputs use D-Bus signatures:
const id = await bus
.destination("org.freedesktop.DBus")
.object("/org/freedesktop/DBus")
.interface("org.freedesktop.DBus")
.method("GetId")
.out("s")
.call();
Always close buses when you are done:
await bus.close();
The runtime also tracks buses and closes them when the runtime lifetime ends, but explicit close keeps scripts predictable.
Signal subscriptions use the Go-native EventEmitter from require("events"). This keeps listener registration in JavaScript while Go schedules delivery back onto the runtime owner:
const EventEmitter = require("events");
const emitter = new EventEmitter();
emitter.on("signal", (signal) => {
console.log(signal.sender, signal.path, signal.name, signal.body);
});
const sub = await bus
.signals()
.interface("org.freedesktop.DBus.Properties")
.member("PropertiesChanged")
.listen(emitter);
await sub.close();
The payload has sender, path, name, and body fields. Body values are normalized where the adapter has explicit support.
The generated binary includes jsverbs that demonstrate safe behavior:
./dist/goja-dbus verbs examples typed-values
./dist/goja-dbus verbs examples denied-system-bus
./dist/goja-dbus verbs examples get-id-script
Use these examples to verify that the binary contains the module and docs before trying host-specific D-Bus calls.
| Problem | Cause | Solution |
|---|---|---|
| A value marshals with the wrong type | JavaScript value did not carry an explicit D-Bus signature | Use dbus.u32, dbus.variant, dbus.array, dbus.dict, or dbus.struct |
A Promise rejects with ERR_DBUS | Policy, connection, marshaling, or D-Bus call failed | Inspect err.message; start with session-bus examples |
| Signal listener does not run | No matching D-Bus signal arrived or the emitter was not an events.EventEmitter | Verify the match filters and use const EventEmitter = require("events") |
| Runtime exits with open resources | Explicit close was skipped | Current runtime cleanup closes tracked buses on shutdown, but scripts should still call close() for deterministic behavior |
xgoja help getting-startedxgoja help api-referencexgoja help jsverbs-example-overview