What is finished, and what is not#
Every product has this list. Most keep it internal. This one is public because a reader who finds a gap here first is a reader who can still make a decision; a reader who finds it after paying is a reader who was misled.
It is the same list README.md keeps in the repository, and the two
are changed in the same edit.
Built and exercised#
- The agent. A Go binary fronting VyOS' HTTP API: staging, native commit-confirm, revision rollback, an attributed audit log, parsed operational state (counters, conntrack, routes, leases, certificates, logs) and a live telemetry WebSocket. It authenticates real accounts (Argon2id, TOTP, sessions with CSRF, three roles, scoped API tokens), speaks OIDC as a relying party, serves Prometheus metrics, terminates TLS and embeds the interface. 214 Go test functions, race-clean.
- The web UI. A React single-page application: 29 left-navigation entries across eight groups, 38 page components, every editor rendering its commands, one Commit Bar, live state beside configured intent with drift flagged, ⌘K over pages and configuration objects, dark and light.
- The app store. 38 catalogue entries — 36 containers declared in the configuration tree, plus 2 built-in feature modules that gate native pages. Installed apps get a navigation entry, integration offers on the pages they affect, an update check and a restart.
- Migration and unattended installs. An importer that turns an OPNsense
config.xmlinto commands plus a report, with--check-againstto compare a running router with the configuration it should have; and an installer that finds a seed and installs with nobody at the keyboard. - Multi-WAN. Uplinks through every mechanism the platform has, health-checked failover, load balancing, policy routing and per-uplink NAT — proven on a dual-WAN VM built from the ISO.
- Inline IPS. Suricata in the packet path via NFQUEUE, entirely configuration-tree, so it diffs, commits and rolls back. Fail-open twice over, each layer verified separately on the bench.
- The image pipeline. CI builds the
.deband the ISO on every tag, inspects nothing in between (see below), and publishes both with a checksum file, a corresponding-source record and an update-channel index.
Every release is driven in a browser against a live router replica before it is tagged.
Every count above is one command away from being re-checked, which is the point of printing them rather than rounding them:
grep -h '^func Test' agent/*_test.go | wc -l # test functions
grep -c "to: '/" ui/src/components/nav.ts # left-navigation entries
ls ui/src/pages/*.tsx | wc -l # page components
python3 -c "import json;print(len(json.load(open('agent/catalog.json'))['apps']))"Not shipped at all#
| Do not expect | What is actually true |
|---|---|
| Packet capture from the interface | POST /api/capture answers 501 and returns the monitor traffic command to run by hand. The Diagnostics page shows you that command |
| UPnP or NAT-PMP | There is no answer. The miniupnpd catalogue entry was removed because no credible published image exists, and the Companions page says the gap is open |
| A one-to-one NAT editor | The nat static table reads rules made elsewhere; the editor is switched off, because committing nat static through this build's HTTP API stops the API process. The CLI accepts the same lines |
| An OpenVPN instance editor | Instances are listed with status and can be disabled or deleted. Creating one waits on a generate pki flow and is not built |
| An nftables ruleset inspector | Firewall hit counters come from the router's own firewall statistics. A per-object nftables view is not built |
| Network namespace or VRF panels | Not built |
| Per-host traffic history, or long-term graphs | Live counters and a short server-sampled series only. Flow export exists so a collector can keep the history |
| Git-backed configuration export | Not built. Revisions, per-revision diffs and a configuration download exist |
| An OpenAPI document | There is none. The endpoint table in the operator reference is the contract |
| A support-bundle command | There is none. Four manual API calls are the procedure |
| An in-product update notification | Nothing on the router reads the release channel. The channel index exists for a person or a script |
| A rescue boot entry, or self-service upgrade recovery | Not built. Recovery is a documented order of steps at the console |
| Ethtool or offload controls | Not built |
| Per-app dashboard widgets | Not built, deliberately — the health probe is TCP plus an optional HTTP path, never an app-specific API |
| Starting or stopping a container out of band | There is no control socket. Stop stages disable, start deletes it, restart is a two-commit bounce |
| A signed catalogue fetched from an index | The catalogue is embedded in the binary; a corrected entry needs an agent release |
Shipped, but narrower than the name suggests#
- QoS. The editor stages a CAKE policy with bandwidth, flow-isolation and RTT,
attached as egress on one interface. There is no ingress or IFB pipeline, no
docsisoption, noack-filter, and no directtcprogramming. The page shows the attached policy beside the qdisc the kernel installed. - Companions. The page reports the configuration state of the platform's own UPnP, DNS and DHCP services. The agent supervises no external process. dnsmasq is available as a catalogue app, not as an agent-managed daemon.
- The desired-state file. The schema sketched in the project's plan document is not what shipped. The file mirrors the VyOS configuration tree.
- WireGuard. Tunnels, peers and configuration are shown. Handshake and transfer
counters need
wg showexposed by the agent, which it does not do. - Conntrack byte counters require
nf_conntrack_acctenabled on the router; without it they are absent. - Uplink health is the router's verdict — is the failover daemon's route in the forwarding table, and what does the load balancer say — not a Wheelhouse probe. There is no per-target RTT or loss history, and this build has no failover status command.
- Fleet is a JSON file of routers, concurrent health, and per-router configuration and version reads. Per-router staging and commit are not routed, and fleet writes are not audited.
- The audit log is attributed and persisted. It cannot leave the box, is not tamper-evident, and root can rewrite it invisibly.
- Roles are three, enforced per route. Per-object permissions do not exist, and neither does a hardware-key second factor.
- Two-factor is TOTP only. No recovery codes, and no admin reset of another account's second factor. Losing the device means console access and a hand-edited state file.
- App images float. All 36 container entries use floating tags. Digest pinning is tooled but not applied.
- Releases are not signed. The pipeline signs checksums the moment a key exists; no key does. A checksum proves transport, not provenance.
- IPv6 is configurable and not observable. No v6 route view, the uplink model has no
v6 concept, and the Firewall page is v4-first. The installer's firewall checkbox does
write an IPv6 ruleset, and says so at the end if the image has no
firewall ipv6node. - It runs on amd64 only. The arm64
.debruns the agent off-router against an arm64 router; there is no arm64 image, and no VM image at all.
Not yet proven, so not claimed#
- Bare metal. The live image has been booted on physical hardware. A complete install to disk followed by a reboot into the installed system is not recorded there. Every end-to-end verification names a VM.
- Tested hardware. There is no compatibility list. The image's driver packages can be listed; that is not a compatibility claim.
- In-place upgrade. An
add system imageupgrade of a Wheelhouse box is not recorded, on hardware or in a VM. - Any performance figure. None measured, on hardware or in CI. The resident-memory and idle-CPU budget is a target.
- Testing is not uniform. The agent has 214 test functions and CI runs them race-clean. The interface has no tests; CI only typechecks and builds it.
Security gaps worth knowing before you deploy#
- The agent binds every interface (
0.0.0.0:8443), so the firewall is the only thing keeping the management interface off the WAN. There is no listen-address setting short of editing the systemd unit, which an image upgrade replaces. GET /api/oidc/statusdiscloses the identity provider's issuer URL without authentication, undoing what the login screen's own probe deliberately withholds.- No factory reset and no rescue boot entry. Lock yourself out and the documented recovery is a console shell, stopping the agent, and editing the state file by hand.
- Redaction below the admin role matches secret leaf names, so a value that is sensitive because of where it sits — a container's environment variable, an SNMP community string — is not matched.
- The dependency scan does not gate a release, and nothing inspects an ISO between building it and publishing it.
- The licence fingerprint derives from
/etc/machine-id, and nothing in the build guarantees two machines installed from the same image get different ones. Check it if you run several.
The full treatment is in the project's own security documentation
(docs/security.md), and reporting a flaw is on
the security page.
Commercially not ready#
- No price. Both monthly figures are unset.
- No licence agreement and no terms of service. What ships is a stopgap that says in its own first paragraph that it has not been through legal review.
- No published support address, issue tracker, chat channel or status page. The forge is private, so "file an issue" is not something a customer can do.
- The beta is closed and invite-only.
- It is not open source. The agent and the interface are proprietary, driving GPL components as separate programs. The base is GPL and its corresponding source is published with every release.
How to check any of this#
Everything above is a file in a repository, and most of it is a grep. The wiki keeps
the same status page with the citations attached, and
the known-gaps page covers the security list in detail.