Date: 2026-05-11
Time: 15:20
This page documents how to build applications on top of FTL2's gate event protocol, using the ftl2-htop monitoring TUI as a reference implementation. It covers the full lifecycle: initializing the gate subsystem over SSH, sending commands, registering event handlers, and running concurrent application logic alongside FTL2's event dispatch loop. The key insight is that FTL2 can be used as a library for building real-time distributed applications, not just as a configuration management tool.
gate_subsystem=True, lasting for the lifetime of the async with automation() blockgetattr(ftl, group) that targets all hosts in a group; also works for individual hosts (e.g., ftl.webserver)proxy.on(event_type, handler), can be sync or async, multiple handlers per event type alloweduv run with no virtualenv setup.pyz bundle; compiled deps (like psutil with C extensions) must be installed on the host via dnf/apt["CommandName", {params}]Gate subsystem initialization:
async with automation(inventory=args.inventory, gate_subsystem=True) as ftl:
Host group iteration:
groups = list(ftl.hosts.groups)
proxy = getattr(ftl, group) # HostScopedProxy for all hosts in group
Sending a gate command (request/response):
await proxy.monitor(interval=2.0, include_processes=True)
Registering an event handler:
proxy.on("SystemMetrics", lambda m: metrics_store.update({m.get("hostname"): m}))
Blocking event dispatch:
await ftl.listen()
Concurrent listen + app logic:
await asyncio.gather(ftl.listen(), update_display())
Installing compiled host dependencies before gate use:
await ftl.webservers.dnf(name="python3-psutil", state="present")
Running the app:
uvx --from "git+https://github.com/benthomasson/ftl2-htop" ftl2-htop -i inventory.yml
uv run ftl2_htop.py -i inventory.yml
ftl2-htop -i inventory.yml --debug # raw events, no TUI
dnf, apt) are used to prepare hosts before gate features are invokedgate_subsystem=True extends it for persistent bidirectional communicationftl.listen() and application coroutines run via asyncio.gathergatesubsystem=True is the flag that enables persistent gate processes (not gate=True or enablegate)ftl.listen() blocks and dispatches events — it must run concurrently with application logic via asyncio.gatherproxy.on() registers handlers; handlers receive the event data as a dict; both sync and async handlers are supported["StartMonitor", {interval: 2}]), not dicts or protobuf.pyz — they must be pre-installed on the hostgetattr(ftl, group_name) returns a HostScopedProxy, not a list of hostsuv run — no virtualenv required