Everything
on one hub
you own.
Aql (Arabic عقل — the mind) is the software brain for your physical space. Cameras, lighting, robot mowers, IoT sensors, energy meters, climate, security and cleaning bots — and access control. The reach of a smart-home hub, pushed wider: autonomous robots and business fleets, under one hub that owns everything.
You own the box. It runs on your own machine — no cloud broker, no account, no telemetry. Access control is the first module that runs end to end today; the status ledger names every other line, built and unbuilt, without rounding up.
- Hub
- 1 box
- Cloud
- 0 brokers
- Accounts
- 0 required
- License
- MIT or Apache-2.0
One hub owns
everything.
A house or a whole facility under one roof: seven device kinds, one internal model, one audit log, one place to look. Underneath sits a driver-adapter seam — pluggable protocol adapters over a single model — so a new class of hardware is a driver, not a rewrite. That engine is running: four drivers ship (MQTT, Modbus TCP, generic HTTP, ONVIF), and with them energy metering and the automations runtime. What varies by kind is how far the path goes — a meter is read end to end, a camera is discovered but not yet streamed, a robot has no driver at all. The ledger spells that out line by line.
Cameras driver ships · no video
The ONVIF driver finds cameras by WS-Discovery, authenticates, and asks one for the RTSP address of a chosen profile — then verifies the stream is really flowing and counts what it loses. Decoding a frame is the part that isn't written: live view and recording need a real camera in front of them, and a retention policy for footage that doesn't exist yet would be the wrong order.
Lighting via mqtt today
On, off and dim work now through the MQTT driver — including anything a
zigbee2mqtt bridge republishes — behind the same device model as
everything else, so an automation can't tell a fixture from a fleet. What's missing is
a dedicated driver: scenes and groups are still whatever your bridge exposes.
Robots planned
Mowers, security patrol and cleaning bots as first-class fleet members with status, tasking and run history — the part a consumer smart-home hub never reaches for, and the reason this project exists.
Climate via mqtt · modbus
Setpoints are a capability the engine already carries, reachable over MQTT or Modbus, and they answer to the same rules engine as the lights and the gate rather than to four vendor apps. Humidity and ventilation are read today; scheduling them is the automations runtime, which is running.
Energy runs end to end
Meter ingestion, hourly and daily rollups, and a source mix of solar, grid and battery — measured off real readings, never estimated. Every bucket carries how much of it was actually observed, so an hour nobody measured reports nothing rather than zero. Raw samples are pruned on a retention window; the counter endpoints a bill is argued from are kept.
Sensors read today
Motion, contact, temperature, humidity, water level, tamper — the inputs an automations runtime needs before it is worth having, and that runtime is running. Reads work now over MQTT and Modbus; the Modbus side is read-only by construction, refusing at config time any capability that could actuate.
Access runs end to end
Gates, doors and barriers — the seventh kind, and the first one that is real all the way down: a paired controller pulses the relay on an Ed25519-signed command, verified against a key it pinned itself. It is the proof the rest of the hub is built against, not the whole of what the hub is for.
Text a channel.
Reach the hub.
Chat isn't the product — it's the input surface across the whole hub with the lowest possible friction. Nobody installs anything: someone messages a channel they already have open, the hub resolves who they are, runs your rules, and acts. The hub behind it drives gates, meters, lights and climate; the console reaches all of them today.
Through chat specifically, the verbs are open and close. Ask it to turn on a light and it says so plainly and points at the console, rather than showing you a gate menu and letting you think it understood. Widening that vocabulary is the next piece of work, and it is deliberately gated on the safety ladder below — every verb carries a tier, and an unattended surface is not handed one it hasn't earned.
The channel rails themselves are moving out of Aql and into Ephor, a separate, swappable component you run or point at. That move is in progress — Aql's side of it is fixed either way: the hub authorises and signs, and it is the only thing that decides.
- The hub is the only authority — a channel is a transport, never a permission.
- A sender resolves to a membership before anything physical is considered.
- Rate limits and quotas are checked before anything physical moves.
- Every action and every denial lands in an append-only audit log, in the same second.
A scripted conversation, not a live hub — but the shape is the real one: resolve the sender, disambiguate the access point, sign, reply with the audit id.
Access control,
end to end.
One of the seven kinds is finished: gates, doors and barriers, from the intent all the way to the relay. It earns the space below by being real — a paired controller, signed commands, an audit chain and an offline path, all conformance-tested. Between the intent and the motor sit your rules, and an Ed25519 signature nothing on the network can forge: only the signature, the expiry and the nonce decide.
-
01
An intent arrives
Someone texts
open, or taps it in the console. However it arrives, the transport authenticates itself first and the hub trusts none of it on its own — an intent is a request, and it is about to be judged. -
02
Your rules decide
The hub resolves the sender to a membership, then checks the access point, the open cooldown, the hourly cap and any per-location quota. The verdict — either way — is appended to the audit log. Geofencing and recurring time windows are designed, not shipped.
-
03
A signed command goes out
An Ed25519-signed envelope — nonce, issued-at, expiry no more than 60 seconds wide — reaches the controller, which dialled out to the hub, so no inbound port is ever open at the gate. It verifies against the pinned key, fail-closed, and pulses the relay.
The gate still
opens.
A hub that answers only when the line is up isn't yours. Aql pre-issues a short-lived, signed grant of your rights; near the door, the app proves it to the controller directly, over LAN or Bluetooth. No connectivity, no hub, no third party in the loop.
-
1
Earlier, online The hub signs a grant: which access points, until when, bound to the app's own keypair. Default TTL is seven days, refreshed on every online launch — so a revocation converges within the TTL, and instantly on the normal path.
-
2
At the door, offline The app finds the controller itself — mDNS on the same LAN, or BLE GATT when there is no network at all — and asks to open.
-
3
Challenge The controller replies with a 128-bit random nonce, valid for 30 seconds and usable exactly once.
-
4
Proof The app signs the grant together with that nonce using its own key. A recorded exchange is worthless afterwards; possession of the grant alone is worthless too.
-
5
Verify, then open The controller checks the grant's signature against its pinned hub key, then expiry, rights, and the nonce signature — fail-closed, in that order — and pulses the relay. The audit event is queued locally and uploaded when the line comes back.
Status — honestly
- Real and conformance-tested. The grant wire format, the hub's issuance endpoint, and the controller's verification are all built and checked against the fixtures in
proto/vectors/. - LAN works today. The reference controller advertises itself over mDNS and serves the grant exchange over LAN.
- BLE is half-done. The framing codec and the open→challenge→proof→result session are implemented and unit-tested at ATT MTUs 23, 185 and 512; the radio itself still needs validation on real hardware.
- The app doesn't do it yet. Requesting, storing and presenting a grant is not built — so this path does not run end to end for a real person today. Both ends of it are ready and waiting for the app.
Aql sits in parallel with your existing gate hardware, never in series with it — the local button and any code-required fail-safe egress keep working on their own, and are never something this replaces. Full status →
No broker.
No account.
One Go binary and one SQLite file, on hardware you can point at: a VPS, a Pi behind the couch, a box in the plant room. There is no Aql service to sign up for, nothing phones home, and nothing in the binary charges anyone anything. The only private thing about your hub is its own config.
-
Bring your own channel Chat is a component you point the hub at, not a service we run — and the rails are moving to Ephor while this lands. Whichever side carries them, the accounts and credentials are yours: your workspace, your bot token, your business number. Nobody bills you through us, because there is no us in the path.
-
No public URL? Still a full install Controllers dial out to the hub unconditionally, so a Pi on the LAN with no inbound port is a complete access installation — CGNAT and a closed router are non-issues. What needs a URL is reaching the console from outside, and whichever chat rails you run that receive webhooks rather than dialling out.
-
TLS is yours, on purpose The binary ships zero TLS and zero ACME code, and refuses to bind a non-loopback address unless you tell it TLS is terminated upstream. Put a reverse proxy or any tunnel you already trust in front — Caddy, nginx, cloudflared, Tailscale Funnel. Less rope, fewer ways to serve a login page in cleartext.
-
Nothing to pay, nothing to report No billing code, no tiers, no licence server, no analytics beacon, no account with us — because there is no "us" in the runtime path. Your costs are your box and your own channel credentials.
# one binary, one SQLite file
git clone https://github.com/vul-os/aql
cd aql/hub && go build -o ../aql-hub ./cmd/hub
# loopback by default — this binary serves plain HTTP
../aql-hub -listen 127.0.0.1:8080 -data ./data
# TLS is yours: a reverse proxy, or any tunnel you trust
caddy reverse-proxy --to 127.0.0.1:8080
✓ hub up console + API on 127.0.0.1:8080
✓ signing key ./data (ed25519, 0600, first boot)
✓ audit log ./data (sqlite, one file to back up)
pre-1.0 — flags and paths can still move · see docs → run your hub
The whole truth,
on one page.
Aql is early, and early projects that oversell are how people end up with a gate that doesn't open. So here is the ledger: what is built and tested, and what is still only a design. Access control runs end to end, the device engine is running underneath four drivers, and what varies is how far each kind's path goes — a meter is read end to end, a camera is discovered but never decoded, a robot has no driver at all. That is the shape of the project right now. Nothing anywhere on this page is called real unless it appears in the left column.
Built & tested real
- The hub. One Go binary, one SQLite file: accounts, members, invites, locations, access points, device pairing and the controller registry — 111 HTTP routes over 19 migrations, and more than 870 tests green across 16 packages.
- Signed commands. Ed25519 envelopes carrying a nonce and an expiry capped at 60 seconds. The controller pins the hub's key at pairing and verifies fail-closed, in a fixed order.
- The reference controller. Its own Go module, over 120 tests green: dials out, pins the hub's key, verifies in a fixed order. The GPIO relay driver is written — the kernel uAPI layout is asserted at compile time on arm, arm64 and 386 — but the last inch is still unproven: no build in this repo has driven a real relay or a real gate.
- Offline-grant issuance. The hub issues a signed 7-day grant through the same authorization gates as a live open, and the controller's eleven-step verification is built and tested.
- Conformance vectors. 98 checks in
proto/across pairing, commands, grants, events, acks and webhooks, consumed by the implementations and by an independent verifier that trusts none of them. - Chat as an input surface. A message resolves to a member, passes the open path and becomes a signed command — and the per-channel rails are moving out to Ephor. That move is in progress, so read this as the behaviour being real, not as a claim about which binary carries an adapter this week.
- Rules and audit. Open cooldown, hourly caps, optional per-location quotas — and a SHA-256 hash-chained, append-only log of every open, denial, pairing and config change, verifiable from a cold backup.
- Temporary access grants. A dated window for a named person, with an optional use cap, revocable, refunded on a rate-limit denial.
- The device engine and four drivers. The registry, the internal model and the capability catalogue that assigns every verb a safety tier — with MQTT (including anything a
zigbee2mqttbridge republishes), Modbus TCP, generic HTTP and ONVIF discovery behind it. - The automations runtime. Rules, a scheduler and an execution engine that runs them against live device state, with a failure budget, a cooldown and a run history. It refuses to actuate anything above its tier ceiling, so “open the gate at 07:00” is deliberately not expressible.
- Energy metering. Meter ingestion, hourly and daily rollups and a solar/grid/battery source mix, measured rather than estimated — every bucket carries how much of it was actually observed, and raw samples are pruned on a retention window while the counter endpoints a bill is argued from are kept.
- Geofencing and recurring time windows. Both enforced inside the same choke point as every other limit. Geofencing is a convenience and not a security control — the position it tests is client-supplied and unverified, so it stops mistakes, not attackers.
- The app half of offline access. A routed screen requests, holds and presents a grant. Presenting works in the desktop app and in a browser over the LAN when the console is served over http; an https console cannot reach a plain-http controller, and BLE needs the app.
- A cross-module test harness. Boots the real hub and controller binaries and drives the open path over the wire, adversarial cases included.
Not built yet planned
- The camera pipeline. The ONVIF driver discovers cameras and resolves an RTSP address; nothing decodes a frame. No live view, no recording, no camera-driven alerting. Depacketization is the line — a fake written from the same RFC would agree with the code under test, so it waits for a real camera.
- Robot tasking. The one kind of the seven with no path at all: no driver, and nothing underneath the status in the design.
- Matter, and native Zigbee or Z-Wave radios. Matter needs a certified device and a stack. The radios are reached through a bridge instead, and the hub is not getting one — though discovery only reads
zigbee2mqtt’s announcement format, so a Z-Wave fleet is configured by topic rather than listed for you. - Anything past open and close, from chat. The hub drives meters, lights and climate; chat does not. Ask it to turn on a light and it says so and points at the console. Widening that vocabulary is gated on the tier ladder, not on parsing.
- Presenting a grant from an https console, or over BLE in a browser. The first is mixed content and no header fixes it; the second needs native code. Both are why the desktop app exists.
- One-time PIN and QR passes. Designed. Today's temporary access needs to know who you're granting it to.
- Mobile builds and first-class Zana support. On the roadmap, in no binary.
This ledger is written against ROADMAP.md and ARCHITECTURE.md in the repo, and the repo runs a docs-versus-code claim check in CI. If you find something here that the code doesn't do, that's a bug worth an issue.
Aql is the brain.
Zana is the body.
Zana is the open-hardware companion project: designs for the machines a hub like this is worth having — robot mowers, sensor nodes, security and cleaning bots. Open designs, so the thing in your garden isn't a black box either.
The two are deliberately not welded together. Aql is meant to control anyone's hardware; Zana devices are simply the ones designed against it first. First-class Zana adapters are a roadmap item — no Zana-specific code is in Aql today.
The things
people ask first.
Isn't this just Home Assistant?+
Same instinct — your devices, your box, nothing in the middle — and Home Assistant is a mature project with hundreds of integrations working today, which Aql very much is not. The difference in intent is the reach: the reach of a smart-home hub pushed wider, so autonomous robots and business fleets are first-class citizens rather than consumer gadgets, and every actuation is a signed envelope a device verifies against a key it pinned itself — which is what lets a hub command hardware across a network it doesn't trust. If you want a full smart home working this weekend, use Home Assistant. If that wider reach and the signed-command model are what you're after, this is that project, early.
What actually works today?+
One of the seven device kinds, end to end: access — gates, doors and barriers. A chat message or a click in the console resolves to a member, passes your rate limits and quotas, and becomes an Ed25519-signed command that a paired controller verifies against a pinned key and acts on, with every verdict audited. The engine behind the other six kinds — the drivers, the automations runtime, energy, cameras, robot tasking — is designed and unbuilt. The ledger above is the complete list, both columns.
So when do the other six kinds work?+
There are no dates, because none of it is scheduled — this is an open-source project being built in the open, not a product with a release plan. What the shape does tell you: access control was finished first because it is the case where getting it wrong is worst, and the pieces it forced into existence — one device model, a pairing ceremony, signed commands, an append-only audit chain, a controller that trusts nothing — are the same pieces every other kind needs. The device engine is the next phase, and the honest state of it is: not started. The roadmap is phase-by-phase and says so.
Do I need an account, or a cloud?+
Neither. There is no Aql service to sign up for and no broker between you and your hardware. You run the binary; it keeps its state in one SQLite file on your disk and generates its own signing key on first boot. Nothing phones home, and there is no billing code in the binary at all. The one outside dependency is optional and yours: if you want chat as an input surface, the workspace, the bot token or the business number behind it is your own account with that provider, billed by them, never by us.
What happens when the internet is down?+
The design answer is a pre-issued signed grant that the app proves to the controller over LAN or Bluetooth — no connectivity, no hub, no third party. The honest answer today: both ends of that exchange are built and conformance-tested, LAN/mDNS works, the BLE session is implemented and unit-tested but its radio still needs hardware validation — and the app does not yet request or present a grant, so the path doesn't run end to end for a real person. Separately, none of this ever sits in series with your gate: the local button and any code-required fail-safe egress keep working on their own. Full status.
Does everyone need to install an app?+
No — that's the whole point of chat as an input surface. People message the hub from an app they already have open. The only installs are the controller at the door, the hub on your box, and optionally the desktop app for administration.
What hardware does the access path work with?+
Any gate, door or barrier with a dry-contact relay input, which is most of them, driven by a Pi-class board running the reference controller agent on Wi-Fi or a 4G SIM. It only ever dials out, so CGNAT and closed ports are a non-issue. The two surfaces that still want real hardware in front of them are the GPIO relay driver and the BLE radio — everything above them is tested.
Is it secure enough for a real site?+
The core is built to be: Ed25519-signed commands with a nonce and an expiry no wider than 60 seconds, verified fail-closed in a fixed order against a key the controller pinned at pairing — so a hostile network, a hijacked DNS record or a malicious tunnel can't forge an open. Every inbound transport authenticates before it is read, tenancy is scoped in the database layer, the audit log is hash-chained and append-only, and the binary refuses to bind a public interface in cleartext. It is also young code that has not been externally audited; read the security chapter and decide for yourself.
What's Zana?+
The open-hardware half of the same idea — designs for robot mowers, sensor nodes and security or cleaning bots. Aql controls any hardware; Zana devices are the ones designed against Aql first. First-class support is a roadmap item, not code in the binary today. github.com/vul-os/zana.
Part of vulos