When to Use a Background Module
Use a background module when you need to:- Expose an HTTP API to your UI module (via Express routes)
- Handle IPC messages from the renderer
- Maintain a persistent connection (WebSocket, database, etc.)
- Run background polling or scheduled work
- Access Node.js APIs not available in the renderer
Declaring a Background Module
Add thebackground field to your plugin.json:
onStart(ctx) after auth is initialised.
The PluginBackgroundModule Contract
Your background entry module must export onStart and onStop. Use the createPlugin helper for type safety and validation:
PluginContext Fields
The ctx object passed to onStart() provides scoped access to shell infrastructure:
ctx.logger — PluginLogger
Standard log methods, automatically prefixed with your plugin ID in the log output:
ctx.config — PluginConfigStore
Persistent key-value store namespaced to your plugin. All methods are async. Values survive shell restarts:
config.get() is async in the background context. In the UI context (via usePluginContext()), it’s synchronous.
ctx.ipc — PluginIPCRegistrar
Register IPC handlers and send messages to the renderer. All channel names are automatically prefixed as plugin:{pluginId}:*:
plugin:my-plugin:get-status and plugin:my-plugin:new-flight-available — the prefix is applied transparently.
ctx.auth — PluginAuthAccessor
Read-only access to the current authentication state:
ctx.server — PluginServerRegistrar
Register Express routers on the shell’s internal HTTP server (port 2066):
http://127.0.0.1:2066/api/my-plugin/status. Use TanStack Query in your UI module to fetch from these endpoints.
Registering Express Routes
A common pattern is to fetch data from your VA’s external API in the background module and expose it locally:IPC Between Background and UI
Use IPC for real-time push from the background to the UI — data the UI didn’t ask for, like live updates or alerts:Lifecycle Summary
- Shell starts and initialises auth
- Shell calls
onStart(ctx)for each plugin with a background module - If
onStart()throws, the plugin is marked errored — other plugins still load - On shutdown, shell calls
onStop()for each running background module - After an OTA update, shell calls
onResume(ctx)if defined, otherwise callsonStop()thenonStart(ctx)fresh