Status: draft, open for discussion
Discussion: this forum topic
Hi everyone,
I’m proposing a new feature that will change the way integrations are developed in Gladys.
This is a long-considered project, and I would be delighted to have your feedback, your ideas, and your comments to continue improving it ![]()
Why now
Since the beginning of the project, all Gladys integrations have lived in the core. Anyone can develop one or improve it via a pull request, but every line goes through my review before being merged. This choice has a huge advantage: when you install Gladys, everything is already there. No store, no dependencies to manage, no broken plugins after an update. This is one of the reasons why Gladys is easier to get started with than other solutions.
But this model has a ceiling, and I’m hitting it. There are thousands of brands, protocols, services, and even the slightest modification of an integration, even a simple translation change, must go through me: review, merge, release. It doesn’t scale, and it makes me the bottleneck of the project.
One might think that Matter will solve this problem. Matter is coming, and in my opinion, it will become the reference protocol that will control all connected devices in the future: one standard, all devices natively compatible, no need for an integration per brand. Except that we don’t know when this future will be a reality, and the project cannot afford to wait indefinitely for the installed base to switch.
And above all, Matter only covers the control of connected devices. There is still a whole range of integrations that have nothing to do with devices: communication (Telegram, etc.), weather (OpenWeather, Météo France), calendars (CalDAV, Google Calendar)… None of this will ever go through Matter. If we want Gladys to become a project with the same ambition as Home Assistant, we must be able to allow the installation of external integrations.
This RFC therefore proposes to open Gladys to external integrations: integrations developed and published by anyone, without prior validation from me, installable in one click from the interface.
The challenge is to do this without sacrificing what makes Gladys. Concretely, four non-negotiable requirements have guided this proposal:
- An integration that crashes must never crash Gladys.
- No incomprehensible states: if an integration no longer responds, the user must see it and be able to act.
- The interfaces must remain clean and consistent with each other.
- Zero technical manipulation for the user. No terminal, no YAML, no manual restart.
What this RFC does not propose
- We are not replacing native integrations. Integrations of universal protocols (Matter, Zigbee, Z-Wave…) will always remain in the core, pre-installed, maintained as today. The new system is added alongside, and targets all integrations of protocols or services that are not universal: from now on, anyone will be able to create a separate integration.
- We are not aiming for compatibility with Home Assistant / HACS integrations. They are deeply coupled to HA’s architecture; running them in Gladys is unrealistic. However, I am inspired by their distribution model (Git repository + manifest + store).
- We do not allow integrations to inject code into the interface. This is a deliberate choice, detailed below.
Proposed Architecture
A Docker container per integration
Gladys already runs exclusively in Docker, with the Docker socket mounted. We rely on this: each external integration runs in its own container, created and supervised by the Gladys core.
This container is locked to the maximum:
- no privileged mode, no access to the Docker socket
- no access to the database or Gladys files
- read-only file system, except for a small data directory that is its own
- strict limits on memory, CPU, and number of processes
- network restricted to the hosts that the integration has declared in its manifest.
An integration that crashes, leaks memory, or goes into an infinite loop remains confined in its box. And a malicious or poorly written integration cannot « go modify the database live »: the SQLite file simply does not exist in its file system.
This choice brings an important bonus: integrations are no longer limited to Node.js. A Docker image can contain Python, Go, Rust. Each author embeds their dependencies in their image, and all the build is done upstream, at the time of publication, never at runtime at the user’s end.
In the long term, this choice also brings something interesting: it will be possible to run remote integrations!
All communication goes through the host API
An integration never touches Gladys’ internals. Its only entry point is a host API: the integration communicates with the core via a simple HTTP JSON REST API, in the same spirit as what already exists in Gladys today.
The primitives envisioned for v1:
- declare devices and their features, in the existing Gladys device/feature model;
- publish states and receive commands
- store its configuration and secrets (stored in the DB on the core side)
- write structured logs
- request a mediated network discovery: it is the core, which has access to the host network, that performs the mDNS scans and the passthrough of USB devices, then transmits the results. The integration thus never needs the
hostmode or direct hardware access.
This protocol-level contract is the true foundation of the proposal. The container is « only » the mechanism that makes this contract impossible to bypass. It also allows the internal schema of Gladys to evolve without breaking the ecosystem: as long as the host API is stable, the integrations continue to work.
Supervision: no zombie states
The core includes a supervisor that manages the complete lifecycle of each integration, with a state machine always visible in the interface:
Installed → Starting → Running → Degraded → Faulted → Stopped
The supervisor sends a regular heartbeat to each integration. No response? The integration goes to « Degraded » and is automatically restarted, with an increasing delay between attempts. If it crashes in a loop, we stop insisting: it goes to « Faulted », and the user sees a clear message with the integration logs and possible actions (restart, disable, report to the developer).
All calls between the core and the integration have a timeout. An integration that hangs never blocks Gladys.
The goal: there should be no more unobservable or unrecoverable states. When something goes wrong, we see it, we understand it, we act, from the interface.
Interface: declarative, no arbitrary code
This is probably the most debatable choice of this RFC, so it’s best to clearly assume it.
Integrations do not provide interface components. They describe their needs, and it’s Gladys that renders the interface with its own design system:
- the configuration is described by a schema (type JSON Schema): Gladys generates the form, the validation, the error messages, in an identical way for all integrations;
- devices are declared in the existing device/feature model: they are automatically displayed with the same cards and the same controls as native integrations;
- for more specific needs, a vocabulary of declarative widgets will be defined and progressively enriched, according to the real needs reported by developers.
What it costs: a developer won’t be able to create a fully custom screen. What it guarantees: a consistent interface everywhere, no XSS vulnerabilities from a plugin, no front-end version conflicts, and an identical experience for the user regardless of the integration’s origin. Given the project’s priorities, I think this is the right compromise. But this is exactly the kind of point on which I look forward to your feedback.
Distribution: a store in Gladys
The user discovers and installs integrations from a store built into the interface. Installing = one click. The core downloads the image, verifies it, launches the container, and displays the configuration form if the integration requires credentials. No terminal, no files to edit.
Each integration is published with a manifest: name, version, compatible Gladys versions, requested permissions (network hosts, devices), configuration schema. Permissions are displayed to the user before installation, just like on a mobile store.
All external integrations are „community based‟: I don’t plan to review them one by one, that would fall back into the bottleneck that this RFC is trying to eliminate. A clear warning is displayed during installation to remind users that this is unaudited third-party code. It’s the role of the declared permissions and container isolation to make this openness acceptable.
Developing an integration in the era of AI
One thing that changes everything compared to a few years ago: developing an integration has become very easy thanks to AI. My intention is to provide an official, clean, and documented integration template. Starting from this template, with Claude Code/Cursor, creating an integration for a given service or protocol becomes a matter of a few hours rather than a few days.
This is what makes this proposal truly powerful: instead of waiting for me to develop or review each integration, the community will be able to produce many more, much faster.
The pace of new integrations will no longer be limited by my availability.
Proposed roadmap
- Define the host API and SDK, then propose to a few developers to create the first external integrations on it.
- Specify the manifest and the declarative interface schema.
- Implement the supervisor and the launch of locked containers, as a proof of concept on a single integration.
- Build the store and the installation process, with the official integration template.
- Open publication to everyone.
My goal is to release this new system as quickly as possible: with AI, it’s possible to move fast.
I plan to start the work this week with Fable 5, while it’s available.
Open questions to the community
- Does the „declarative UI only‟ compromise seem acceptable to you? What real use cases wouldn’t fit into this?
- What primitives are missing from the host API for your dream integrations?
- Should we limit v1 to Node.js integrations (with an official SDK) to simplify, or open all languages from the start?
This proposal is a starting point, not a decision set in stone: your criticisms, objections, and ideas are exactly what I’m looking for to make it better ![]()
















