Chapter 21

The plugin system, API and WebSocket

Manage the plugin lifecycle safely on a standard bridge, label the built-in Camera and Security plugins as experimental, weigh the supply-chain risk of npm, tgz and symlink installs, and treat REST, WebSocket, logs, metrics and health as interfaces you can verify in v2.0.55 rather than as a permanent public contract.

A plugin is in-process code, not just a UI extension

A plugin can register Matter devices, update cluster state, receive controller attribute writes, call external services and use persistent storage. In v2.0.55 a plugin runs directly inside the HAMH backend process, with no OS-level sandbox; the error wrapper, the timeouts and the circuit breaker reduce how far a failure spreads, but they cannot make a malicious package safe. Installing a third-party plugin is the same as authorizing it to run code with the permissions of the HAMH process.

Plugins are supported on standard bridges only. A bridge with Server Mode enabled builds no plugin manager and carries nothing, not even the built-in plugins; if every bridge is in Server Mode, the Plugins page shows the matching empty state. This rule has nothing to do with the release channel: Server Mode itself is still experimental (experimental-in-Stable).

Back up before you install: create a full backup that includes identity, download it to a controlled offline location, and record the current bridge and plugin state. An uninstall, a package upgrade, a broken symlink or a bad plugin write can all make endpoints disappear; controller rooms, automations and endpoint identity can all be affected.
SourceUpdate / loadMain riskApplies to
Built-inWith the HAMH versionMay still be experimental; check controller supportSpecific Camera and Security capabilities
npm packageLoaded when you restart the bridge after installingName confusion, dependencies and the install-script supply chainReviewed, version-pinned production testing
uploaded tgzRestart after you upload and installOpaque origin, integrity and contentsBuild artifacts you can verify
local symlinkSource changes take effect after a bridge restartPath substitution, permissions, non-portability and persistenceIsolated development; not for production

Lifecycle, device registration and the circuit breaker

The one required plugin hook is onStart(context), which discovers and registers devices and opens connections when the bridge starts; the optional onConfigure() restores state after device registration, onShutdown(reason) cleans up timers and sockets, getConfigSchema() supplies the UI schema, and onConfigChanged(config) applies an update. The context gives you register and unregister device, update state, domain mapping, plugin-scoped storage, a logger, the bridge ID and an optional HA connection.

Enable and Disable are per-bridge, per-plugin state. Disable unmounts that plugin's mounted devices and persists the choice; Enable starts the plugin again. Settings can be saved while a plugin is disabled, and they take effect when you enable it later. Install and Uninstall work at the package level and usually need a bridge restart before they apply; do not confuse “the package is installed” with “the plugin is enabled on this bridge”.

SafePluginRunner applies a default timeout to ordinary lifecycle calls, and when consecutive failures reach the threshold it opens the circuit breaker and disables the plugin; a successful operation resets the failure count. The Reset button only clears the breaker so it can try again; it does not fix the root cause. Shutdown cleanup is still attempted even when the breaker is open, so that resources are not leaked.

Isolation boundary: a plugin is in-process with the backend, and the wrapper is only a defensive boundary. A fire-and-forget promise can escape the scope of a single runner call; the process-level unhandled rejection handler can only log and keep the process from crashing outright, which is not a security sandbox.

Experimental plugins inside Stable, and controller limits

CapabilityRelease channelProduct maturityController support
The Plugins page and the plugin managerStable 2.0.55The plugin system is usable; third-party quality is each publisher's own responsibilityDepends on the device type that gets registered
The built-in Camera PluginBuilt into StableExperimental (experimental-in-Stable); the media path has no completed end-to-end verification on real hardwareThe official docs at the same version say Apple Home does not render it; SmartThings was the main target at the time, which you cannot extrapolate from
The built-in Security PluginBuilt into StableExperimental (experimental-in-Stable), with its own state machineHow the mode switch and contact sensor appear depends on the controller
Plugins on a Server Mode bridgeServer Mode exists inside StableNot supported; Server Mode itself is experimentalNo plugin endpoints
REST / WebSocketVerifiable in the v2.0.55 codeFor the web UI and for integrationsNot directly related to Matter controller support

API contract statement: the v2.0.55 code and the pinned docs list the REST routes, the WebSocket messages and the response shapes, but in those sources the repo promises no public API versioning policy that guarantees permanent compatibility. Unless another source guarantees it, do not describe it as a guaranteed stable public contract. Automation should pin a version, check status and schema, set timeouts, and run a smoke test before an upgrade.

A plugin manifest can declare hamhPluginApiVersion; the manager constant is currently version one, and a mismatch is recorded as a warning. The warning is not a compatibility guarantee, and it does not upgrade a third-party package for you. A live matter.js EndpointType also has to come from the same matter.js instance, so an external package is usually better off with serializable deviceType and cluster data.

The lowest-risk way to put a plugin into production

  1. Confirm the standard bridge and the isolation scope

    In Bridges, pick a standard bridge that does not have Server Mode enabled, and confirm first that it is running and that its fabric and health are normal. Give the experimental Camera and Security plugins, or any third-party plugin, a dedicated bridge; that narrows both the controller-compatibility surface and the blast radius of a failure.

  2. Back up, then review the source

    Create a full identity backup and a record of the plugin state. For npm, pin an explicit version and cross-check the package owner, the source repository, the manifest, the dependencies and the release integrity; a tgz has to be produced by a CI you trust and then verified; a symlink belongs only in an isolated development environment.

  3. Install a package, or pick a built-in

    Do not type a built-in name into the npm field; enable and configure it straight from the Plugins page, because the code rejects the reserved built-in names. For a third-party package, use the matching npm, Upload or Local tab, restart the target bridge when the prompt tells you to, then Refresh the list.

  4. Configure the minimum the schema asks for

    Fill the required fields first, make sure every number parses, and leave the existing settings in place for schema keys you do not recognize. Fields marked secret are redacted by the backend and the stored value is never returned; leaving the redacted placeholder alone means keep the current value. Enter any token or password only through your secret-management process, never into a document or a log.

  5. Verify the endpoint and the controller

    Check the plugin metadata, the enabled state, the devices and the breaker state, then open the bridge endpoint tree and check the device types and clusters. Test state and a controller write with one low-risk device; Camera also needs you to assess the TCP firewall on the operational port, and with Security you must not use a safety-critical entity for the first test.

  6. Set up a fallback point for failure

    If errors are rising, Disable the plugin first so its mounted devices are removed, and keep the log and the settings. Only after you have fixed the external service or the schema should you Reset the breaker and Enable it again; if you are going to Uninstall, confirm first that no bridge is still using it and keep the backup, then restart and verify the other plugins.

Built-ins, schema, REST and the real-time interfaces

Camera Plugin

Camera exposes a chosen Home Assistant camera entity as a Matter camera device over a WebRTC transport flow. It registers no device until a camera entity is set; once one is set, it can be mounted dynamically. A custom HA URL and secret are optional, but the minimal configuration should reuse the bridge's existing HA connection. A dedicated bridge isolates the effect of the Matter over TCP capability on your other controllers.

In Stable 2.0.55 it is still experimental; you cannot claim Apple Home support, and you cannot claim that live view has been fully verified on any controller. A camera stream involves privacy, the network and the firewall, and the Plugins page working is not the same as meeting the surveillance regulations or the access policy you are subject to.

Security Plugin

Security creates a Home / Away / Night / Vacation mode switch and an Alarm contact sensor, and runs on exit and entry delays, a trigger list, an alert list, setters and a persisted armed state. It is not a synchronizing front end for an existing Alarmo or alarm integration; if both use the same set of sensors, you end up with two state machines that know nothing about each other.

This experimental plugin has no separate verification-code flow; a controller that can operate the mode switch can also disarm it. So expose it only to controllers you trust, and do not treat it as a certified intruder alarm. Trigger events during a Home Assistant outage can be lost, which is another reason it is not a safety system.

Schema secrets and the breaker

The v2.0.55 backend schema property has an explicit secret flag: a stored secret is replaced by a sentinel in the listing, and if the value is still the sentinel when you save, the original value is kept. The frontend also falls back to redacting by field name for older schemas, but the real protection has to come from the backend secret flag. If a third-party plugin does not mark its secrets correctly, the platform has no way of knowing on its own that a given string is sensitive.

REST surface

/api/plugins gives you the plugin metadata, the redacted config, the breaker and the devices for each standard bridge; alongside it are the installed list, npm install, binary tgz upload, local install, uninstall, enable / disable / reset, config schema and config update. The whole of /api also mounts matter, health, bridges export, images, mappings, settings, backup, HA, logs, system, diagnostic, metrics and network. This is the management plane, so it must have the access controls in Chapter 20 applied to it; but the WebSocket has an auth bypass boundary at the same version, so you must not extend REST protection to the upgrade.

WebSocket, logs, metrics, live and ready

The WebSocket path is mounted at /api/ws under the base path. It sends the initial bridge state on connect and can then broadcast bridge updates; a client can ping and pong, and subscribe to or unsubscribe from diagnostics, receiving a snapshot and then the diagnostic events that follow. A critical security boundary: in v2.0.55 the upgrade is attached directly to the raw HTTP server, which bypasses the HAMH Express Basic Auth and the application IP allowlist. A trusted proxy or network boundary has to authenticate, restrict the upgrade and block direct backend reachability by itself; the client also has to handle unknown message types and reconnect.

The logs API supports level, search, category, facility and limit / offset, plus a level count, clear and a Server-Sent Events stream; a low-memory system uses a smaller buffer. Metrics come in JSON and Prometheus form. Health live answers whether the process is alive, and ready looks only at whether HA is connected. These endpoints cover different scopes, so a single 200 response is not a claim that the whole Matter topology is healthy.

InterfaceWhat it is forSecurity note
Plugin RESTInstall, lifecycle, configurationIt can change things; restrict it to administrators
WebSocketBridge updates and diagnosticsFollows the base path, but does not inherit the HAMH Basic Auth or allowlist; the proxy has to authenticate and restrict the upgrade
Logs / streamQueries and live logsMay contain information about your environment; clear is a destructive diagnostic action
MetricsResource, bridge and HA trendsLabels and versions are operational information
live / readyProcess and HA readiness probesThe code skips Basic Auth; rely on network restrictions

Safe adoption and automation scenarios

Trying the built-in Camera plugin: build a dedicated standard bridge, put one non-sensitive test camera on it, restrict it to a test controller and a test VLAN, and confirm the TCP policy, the health and recovery through Disable. The result speaks only for that controller, that version and that network; do not extrapolate it to Apple, Google, Alexa or any other product.

Third-party cloud plugins: test in an isolated environment with a minimum-scope credential, with the schema secrets marked correctly and the log printing no request headers or tokens. Watch the failure, memory and network trends for a while before you consider production; review a package upgrade again as new code.

External monitoring: let the monitor read ready, metrics and the logs it needs over a controlled network, and do not give it an admin account that can install or delete plugins. Pin a v2.0.55 response fixture before an upgrade; after the upgrade, verify the content type, the required fields and the WebSocket messages before you adjust the parser.

Avoid automatic “fixes”: do not auto Reset or Enable the moment monitoring sees a single breaker error, and do not run a Factory Reset because ready failed. Automation may alert and collect, nothing more; any API that changes state should still need a person to approve it.

Common pitfalls and safe fixes

  1. The Plugins page is empty, or the built-ins are missing

    Check: whether there is no bridge at all, whether every bridge is in Server Mode, or whether the standard bridge is not running. Safe fix: create or start a dedicated standard bridge; do not go to npm and install a built-in of the same name.

  2. The install succeeded but the plugin did not load

    Check: the installed list, the manifest main and API version, whether the bridge has restarted, and import errors in the log. Safe fix: confirm the package integrity and compatibility, then restart that one bridge; remove a package you do not trust outright and rotate any secret it could have touched.

  3. Circuit breaker tripped

    Check: the last error, the consecutive failures, the external service and the schema. Safe fix: Disable first, fix the root cause, then Reset and Enable; a bare Reset will only fail again.

  4. The settings page does not redact a secret

    Check: whether the plugin schema marks the field as secret. Safe fix: stop using it and get the maintainer to fix the schema; rotate any exposed value immediately, because guessing from the field name in the frontend is not a sufficient guarantee.

  5. The web UI state does not update but REST is fine

    Check: the proxy WebSocket upgrade, the base path and the browser socket. Safe fix: fix the proxy and reconnect; do not reset the bridge fabric.

  6. A device is left on the controller after an uninstall

    Check: whether the bridge restarted, whether the plugin endpoint is still there, and the controller cache. Safe fix: first let the bridge rebuild normally and the endpoint disappear, then remove it safely on the controller side; do not run a disaster reset for one plugin.

FAQ

Can a plugin run on a Server Mode bridge?
No. The v2.0.55 code explicitly skips pluginInfo for a Server Mode bridge, and the built-ins are no exception. Use a standard bridge.
Is the built-in Camera in Stable a mature production feature?
No. It is an experimental feature inside the Stable channel. The media path and controller support have to be labeled separately, and you cannot promise it works on Apple Home or on every platform.
Is the circuit breaker a security sandbox?
No. It enforces a timeout and disables the plugin after consecutive failures; the plugin still runs in-process with the backend, which carries supply-chain and data-access risk.
Is the REST API a public contract guaranteed to stay compatible?
The sources for this version prove the v2.0.55 routes and shapes; no source is enough to guarantee permanent compatibility. An integration should pin a version, tolerate failure and test before an upgrade.
Is a local symlink suitable for production?
No. It depends on an absolute path on the host and on the symlink continuing to exist, and a change at the source runs directly after a restart; use it only for isolated development.
Does a successful readiness check mean every plugin is healthy?
No. ready looks only at whether HA is connected; plugin breakers, failed bridges, sessions and controller support all have to be monitored separately.

Pinned-version sources