- A broad internal audit focused on reliability — 37 system areas reviewed, from configuration and authentication to networking, clients and builds. The goal is consistent client behaviour and predictable recovery from failures without silently removing protection
- Over 1,200 commits since 0.8.1 and 323 audit reports — the 1,233 commits at the 17 October checkpoint include code, tests, documentation and build tooling, not 1,233 fixed bugs. This is an internal engineering audit, not independent security certification
- Shared configuration validation across clients — a common native parser for INI and
qeli://links checks values and duplicate parameters; validate existing profiles withcheck-configbefore upgrading - Improved server management panel — validation before saving, stale-revision protection, atomic writes, improved logs and backup/restore; notifications move to
notify.iniwithout automatic migration - More reliable network-state recovery — routes, DNS and firewall track resource ownership; failures trigger rollback, while incomplete cleanup retains protective blocking
- New manual IPv6 mode —
routing.ipv6.mode = manualleaves forwarding and firewall rules to the administrator; NDP proxy is supported and IPv4 is configured independently - Configurable Linux TUN queue — the client
tun.tx_queue_lensetting controls the queue of its own TUN/TAP; UDP processing and bounded buffer use are improved - Updated client applications — fixes cover reconnects, profile and secret storage, the Windows service and macOS helper; Android/iOS clear logs by default after a completed disconnect
- Important upgrade changes — on Windows/macOS, move
net_gatewayroutes fromroute_fileto localexclude; upgrade instructions →
Changelog
Version history
The current released beta is 0.8.2. Previous versions and their results are retained below. Qeli does not yet have a stable release.
- The UDP data plane processes datagrams in batches without an extra delay — Linux and Android use
recvmmsg/sendmmsgwith bounded batches of up to 32 while preserving PacketCodec order, QUIC packet numbers, fragmentation, roaming CIDs, pacing and PMTU fallback - The measured UDP bottleneck was removed on the two-core lab — in alternating same-window A/B runs, median upload rose from 320.8 to 694.7 Mbit/s and download from 359.8 to 697.5 Mbit/s; this verifies the mechanism rather than promising the same speed everywhere
- Mobile recovery no longer spends backoff without a usable network — Android/iOS resume immediately when Wi-Fi or cellular returns; Android preserves the fail-closed TUN when NetworkPlan is unchanged, while iOS avoids reapplying identical NetworkExtension settings
- TCP multipath gains stable logical slot IDs — losing or restoring one bonded carrier no longer remaps healthy flows; Android preserves up to 1,000 diagnostic events in private no-backup storage capped at 512 KiB
- Routed IPv6 gains a session-aware NDP proxy —
routing.ipv6.ndp_proxy=off|auto|requiredanswers only for addresses and non-default subnets owned by live sessions; disconnect, revoke and session replacement remove ownership immediately - The DHCP server now follows strict lease semantics — REQUEST/NAK, RELEASE, quarantined DECLINE, relay/broadcast, renewal and expired-lease cleanup are handled correctly; clients receive configured
dns.push_servers - Windows/macOS
route_fileis repeatable and fail-closed — it accepts CIDR, common OpenVPN IPv4 routes androute-ipv6, canonicalises and deduplicates them, with a 250,000-route total bound - Desktop, backup/restore and local storage are hardened — Windows per-app and transactional firewall updates are fixed, macOS cleanup is faster, backup validates the actual tar, and damaged secrets or settings fail closed
- Released as a beta — 17 payload files plus
SHA256SUMS; 1,041 Rust tests and 20 mandatory automated IPv6/roaming cases passed, while the first stable line remains planned as 1.0
- Session roaming preserves the logical session across network changes — TCP uses make-before-break with a two-phase commit, while UDP validates a new path with an authenticated challenge; when the full contract is unavailable,
autosafely falls back to a normal reconnect - Roaming works across ordinary TCP and UDP modes — Wi-Fi/cellular changes, NAT rebinding, outer IPv4/IPv6 path changes, stale-epoch rejection and bounded candidate queues are covered; explicit route exclusions keep priority
- Full IPv6 and dual-stack data plane — the outer carrier family and inner TUN families are independent, NetworkPlan v2 atomically delivers IPv4/IPv6 addresses, routes, DNS and MTU, and
ipv6=auto|required|offcontrols negotiation strictness - New installations gain dual-stack templates — separate IPv4/IPv6 pools, a deployment-unique ULA /64, dual-family DNS and
routing.ipv6.mode=nat66|route|off; existing configurations are not rewritten automatically - The PACKET_MUX_V1 recordizer changes encrypted-record boundaries — several inner packets can share one record, one packet can span records, and batching, target size and reassembly are protected by server-side limits
- The recordizer is negotiated after AUTH for any TCP/UDP carrier —
preferkeeps legacy clients compatible,requiredrejects an incompatible client before address allocation, andoffrestores the legacy data plane - Reality-TLS uses a genuine HTTP/2 carrier — after REALITY TLS 1.3 it opens one long-lived bidirectional H2 POST with standard SETTINGS, HEADERS, DATA and WINDOW_UPDATE; a 0.8.0 client does not silently downgrade
- Upgrade the server first — a 0.8.0 server accepts both new H2 and the legacy Reality carrier; preserve the identity key, short IDs, SNI and target, stage the recordizer with
prefer, and enable IPv6 explicitly in an existing config - Released as a beta — the set contains 17 payload files for Linux, Windows, macOS, Android, OpenWrt and Keenetic plus
SHA256SUMS; Qeli still has no stable release
- Trusted Wi-Fi and mobile profile checks — Android and iOS gain an exact-SSID policy where lockdown/kill switch always wins over a trusted network; Android also checks saved profiles only while the app is visible and the VPN is off
- Recovery after sleep and network changes — an intentional reconnect no longer increases exponential delay, Android does not cycle a healthy tunnel on screen-off, and a late platform ACK no longer kills the mobile dispatcher
- MTU-aware buffers and precise drop reasons — downlink pools are sized from MTU, while internal drops are split into pool exhaustion, queue full, oversize, unsupported packet and TUN write failure; a partial TUN write is no longer reported as success
- Desktop lifecycle and
persist_tunare strict — a new generation cannot start before the old one is fully torn down, and the TUN is reused only when the address, MTU, DNS, routes, exclusions and carrier-path fingerprint is unchanged - Fail-closed host management — Linux DNS, NAT, forwarding, kill switch, routes and TUN retain provable ownership, roll back partial setup, and keep failed cleanup retryable
- Server, panel and backup/restore hardening — child tasks finish before TUN/NAT removal, usage accounting fails closed, ACLs are validated as a whole, restore rejects unsafe hooks/symlinks, and the panel shows hostname plus precise UDP badges
- Strict framing and an honest QUIC boundary — TLS/AEAD records reject unauthenticated tails and truncation; new UDP packets use the exact qeli Initial
0xC3shape, documented as QUIC-shaped masking rather than full QUIC - Upgrading will require strict validation — fix allowed_networks, route metrics, unreachable REALITY targets, TUN/bind collisions and IPv6 DNS/PUBLIC_HOST; validate the backup and run
qeli check-configfor the server and every enabled profile - Released as beta — the GitHub Release contains 17 files for Linux, Windows, macOS, Android, OpenWrt and Keenetic, including
SHA256SUMS; Qeli has no stable release yet
- A shared Rust transport core for native clients — Linux, Windows, macOS, Android and iOS now use the same handshake, lifecycle, TCP/UDP pumps, reality-tls and NetworkPlan. This removes platform drift and delivers transport fixes to every app at once
- Configuration must be checked before upgrading — remove the obsolete
tun.netmasksetting (pool.cidris now the single source of truth for the subnet), back up/etc/qeliand/var/lib/qeli, then runqeli check-config - Safe configuration editing in the web panel — Form, JSON and Raw editors carry the exact file revision, detect concurrent edits, preview changes and create private snapshots. History keeps ten versions and can validate and restore them while preserving the current state
- A new Transport Health page — live state for every profile, sessions, streams, traffic, drops, TUN/MTU, DNS, routes, masking, multipath, buffers, limits and operational warnings without exposing keys or passwords
- More stable long-running connections and reconnects — a client no longer stays falsely Connected, multipath streams recover, and Windows, macOS, Android and iOS react correctly to sleep, Wi-Fi/Ethernet changes, DHCP, DDNS and temporary DNS failures
- DNS and routing are now fail-closed and consistent across platforms — tunnel DNS without an available resolver is rejected, split tunnel gets exact DNS routes, IPv6 resolvers are rejected until an IPv6 data plane exists, and
pool.cidris the single subnet source of truth - Android Disconnect no longer breaks device DNS or the next Connect — the app waits for the native transport and TUN to shut down completely; captured IPv6 probes are dropped before encryption and no longer flood the server log
- Per-user bandwidth limits now work equally for TCP and UDP — upload and download are limited independently, while multipath streams in one direction share the same budget instead of multiplying it
- Desktop and per-app security is stronger — macOS protects root state and the profile key more carefully, the Windows kill switch adds a kernel DROP gate over WFP, and per-app mode gains bounded flow tables plus correct DNS/NAT, MTU and IPv4/IPv6 fragment handling
- UDP profiles: packet loss fixed, throughput doubled — the udp-quic mode delivered half of what TCP did, due to fragmentation and small receive buffers. Fixed on the server and in every client (including OpenWrt and Keenetic): 22 → 40+ Mbps down and 55 up
- A «Protection» card on the Android home screen (and in the upcoming iOS client) — it shows what the profile actually protects: post-quantum encryption, REALITY, server-key pinning. With an honest warning when some traffic goes around the tunnel (LAN access, IPv6, exclusions, split tunnel) — the card is designed never to overstate
- A «Connection properties» card shows what the server sent the client: DNS, the applied MTU, the number of routes and bonded streams — instead of guessing from the profile
- Re-issue a config from the CLI —
qeli share-link <user>— send an existing user theirqeli://link again, without a password and without recreating the account. Previously only the web panel could do this - The client version is shown in
list-clientsand in the panel — no need to ask the user what they have installed - A run of external security audits (July 29 – August 1, several passes) — in particular a kill-switch DNS leak on Linux is closed: while the tunnel was down, every app's queries went around it; port 53 is now open only to the resolvers actually in use, and the «real resolver» criterion is unified across platforms
- The HTTPS web panel no longer hangs — with
tls = truethe port opened but the TLS handshake never completed, and the browser hung until timeout (the VPN kept working). Fixed - Server UDP buffer sizes — the new
perf.udp.*keys; on the client,recv_buffer_sizeandsend_buffer_sizenow work — they were in the config but did nothing before - ⚠️ Behavior change: the server refuses to start with an unparsed value in the config — an unrecognized value used to be silently replaced by a default, often toward weaker protection (a typo in
kill_switchwould turn it off). Start now rejects such a config and lists the offending keys. Runqeli check-configbefore upgrading
- Exit node — the client flag
exit_node— traffic can reach the internet through another node of yours (for example, behind your home IP): the chain «client → server → chosen exit client → internet». A mirror of gateway mode; configured on the Linux client, with nothing to change for the node using the exit - Android: a traffic leak on reconnect is closed — during a reconnect the VPN was torn down and every app's traffic went around the tunnel over the physical link for seconds. Now the tunnel is held until it's replaced in place (fail-closed), as on iOS
- A large security audit (July 27) — the core, the web panel, all clients and installers. The core itself (cryptography, protocol) is clean; the fixes are in the platform layer. In particular: a local privilege escalation via service-profile spoofing on Windows is closed, and
/api/config/rawin the panel no longer returned the admin password hash and user secrets - Traffic accounting survives a restart — previously every
systemctl restart(including the «Apply and restart» button) reset accumulated quotas, and users over the limit passed again - Management actions reach the UDP plane — a client disabled by quota or kicked used to keep sending packets for another 30–45 seconds from an already-released address; the revocation now takes effect at once
- The server refuses to start with a config that would lock you out of the machine — new pre-start checks, plus stricter validation: the
tun.mturange, brute-force policy bounds, and a DHCP pool that must sit inside the tunnel subnet - Reinstalling no longer wipes the config — a repeated installer run used to overwrite
server.conf, generate a new identifier and make every issuedqeli://link dead. Plus install from the binary without a.deband a choice of service user (qeliorroot) - A DNS setup failure no longer drops a working tunnel — on a read-only
/etc(containers, hardened systemd) resolver management became best-effort: the client keeps the tunnel up and logs a warning instead of a reconnect loop - iOS has reached UI parity with Android — a Russian language and switch, full localization, one-tap connect from the widget, a working UDP ping. The client is being prepared for release (a macOS build)
- Russian UI for the Android app and a language switch (English / Русский) in settings. English by default, regardless of the device locale
- Switching the profile on an active tunnel is now rejected (Android, Windows, macOS) — a stray tap on another profile used to silently drop the connection and bring it back up on the new one. Editing, duplicating, deleting and sharing any profile still work
- Validate a config without starting the server —
qeli check-config— exit code 0/1 (fit for CI and as a pre-flight before restarting the service), and above all it catches typos in key names that used to be silently ignored (the setting just stayed at its default) - Configurable log timestamp format — the
[logging] time_formatkey (datetime/rfc3339/time/epoch/none) on the server, the router client, the web panel and every app - Running on top of an existing network interface — the
dev_attachkey — the client attaches to a device managed elsewhere (systemd-networkd, NetworkManager, router firmware) and only carries packets, leaving addresses and routes to the owner. On top of it — integration with the Keenetic router web UI (OpkgTun). A community contribution - The largest external security audit yet (Rust server, web panel, Android, Windows/macOS clients, OpenWrt) — dozens of findings fixed. In particular, the per-user route restriction (
allowed_networks) is now actually enforced, and 26 config-template parameters that did nothing were removed - The web panel no longer starts without a password — set one with
qeli set-web-password(or explicitly allow open access): previously an empty password on loopback meant full access - Linux releases install on Ubuntu 22.04 and Debian 12 again — the 0.7.8–0.7.11 builds failed with
GLIBC_2.39 not found; the binary is now built with a pinned ABI (glibc 2.28) and a build-time check, and the.debdeclares its dependency honestly - Diagnostics: packet tracing (
QELI_TRACE) — a single timeline of packet shapes on the client and server; no payload and no addresses in the trace - More resilient connection — connect and handshake are bounded by a shared deadline (the client no longer hangs in «Connecting» on a silent server), and a lost datagram during the UDP handshake recovers in ~1 RTT instead of ~30 seconds
- Exclude subnets from the tunnel — the
excludeandincludekeys in every client (Linux, Windows, macOS, Android).excludesends the listed subnets around the tunnel,includepulls them into it. Previously these lists were reachable only from the JSON config - Excluding now works under a full tunnel too — it used to be silently ineffective there: the subnet went through the tunnel anyway. Now a more specific route via the physical gateway is installed for it, and that route wins
- Fixed reconnect when client and server share a LAN (Windows/macOS) — the tunnel used to stall right after the handshake and loop forever
- One profile on several ports — the repeatable
listenkey serves several addresses and ports without cloning the profile: shared address pool, key and users - Routing to networks behind a client — the server can now direct traffic into subnets that sit behind a connected client (the server half of site-to-site)
- Panel logins survive a full restart — no more signing in again; the "Apply and restart" button now performs a full server restart
- Version shown in
systemctl status— no need to ask the binary separately what is installed - Full security audit (server, web panel, Android, Windows/macOS clients) — everything found is fixed in this release
- Fast recovery after sleep and network change — clients notice wake-up and Wi-Fi↔LTE switches and reconnect in seconds instead of ~a minute (all platforms)
- Editing or deleting the active profile applies immediately (Windows/macOS) — a changed server address on a running tunnel takes effect at once; the editor no longer drops advanced fields
- Android — a major app update: per-app split tunnel (send only the chosen apps, or everything except them, through the VPN), a home-screen widget and a Quick Settings tile (one-tap connect), and a «Disconnect» action right in the notification
- Android — profile convenience: share a profile (QR code + a
qeli://link) and import by tapping a link, auto-connect on device boot, backup and restore of profiles, plus duplicate and reorder - Server: a fixed per-user IP is now actually pinned (
static_ip/pool.reservation) - Docker image with built-in diagnostics (traceroute, mtr, tcpdump, dig, curl…) — no need to install packages into the container
- Stability: the «Connected» status only after the tunnel is actually up, a fixed reconnect storm on network change, and a udp-quic fix for malformed datagrams
- Network- and config-compatible with 0.7.9
- udp-quic works out of the box — the server auto-detects the client's QUIC mode (even if not enabled in the profile), and the udp-quic tunnel no longer drops right after connecting
- More stable connection — fixed an idle-tunnel drop every ~5 minutes (clients always send keepalive) and a clean connection close (FIN)
- Windows: two accounts or servers on one host no longer fight over the adapter
- OpenVPN-compatible client options (Windows/macOS) —
persist_tun(the adapter and routes stay up between reconnects), binding to a local address/port, an interface metric, split-tunnel routes from a file - Minor: an amber connecting indicator, UTC log timestamps, a clean stop on Ctrl+C, and a fix for saving the config via the panel
- Network- and config-compatible with 0.7.8
- Fixed a «reconnect storm» — a local failure to bring up the tunnel no longer triggers a flood of reconnects (which could get the server banned by the host)
- Closed an IPv6 leak in full-tunnel mode — global IPv6 traffic no longer bypasses the VPN
- The client no longer touches other network adapters — Linux and Windows no longer delete or «adopt» another VPN's interface (e.g. OpenVPN)
- Mode aliases
udp-quic/udp-obfsfor simpler setup - Network- and config-compatible with 0.7.7
- A large security & reliability audit (~26 fixes) — including a fix for a critical tunnel drop under load at high speed
- Automatic MTU on UDP (
mtu = 0) — the client probes the right packet size for narrow links (LTE / CGNAT / PPPoE), avoiding fragmentation; in every client - Separate traffic accounting — the users page now shows download (↓) and upload (↑) separately; the quota limit counts download only
- Separate brute-force protection for the panel and VPN — two independent policies and logs, each can be turned off; blocks are visible and can be lifted on the «Blocked IPs» tab
- More flexible panel deployment — run behind a reverse proxy under a path prefix (
web.base_path), a CSRF toggle, and pushing a specific DNS resolver to clients (dns.push_servers) without the built-in proxy - Client stability — Windows: large split-tunnel lists apply instantly; bonding now speeds up downloads too; fixes for profile selection on macOS and link parsing on Android
- Network- and config-compatible with 0.7.6 — no settings change
- Update check (optional, off by default) — clients, the panel and the CLI can notify you of a new release with a link; qeli never downloads or installs anything itself. Releases now publish SHA256 checksums
- Web panel: changes apply reliably — user edits from the panel now always reach the server; a «Blocked IPs» tab (view and lift blocks), an IP-lockout notification, an account-expiry date picker, and a server name in notifications
- Windows / macOS: two accounts on one server no longer get mixed up — profiles now have a stable identifier, so the service and auto-connect pick exactly the right account
- Extra transport-privacy options (optional, off by default) — fine-tuning of the connection profile; enabled identically on the server and the client
- Performance and security — fewer allocations per packet (faster in every mode), fixes from a multi-agent security audit, and a fix for building the Docker image
- Network- and config-compatible with 0.7.5 — no settings change
- OpenWrt client — router-level VPN: a native package (procd service, UCI config, LuCI page), a firewall zone with NAT for the home network; builds for aarch64 / armv7 / mipsel / x86_64
- More reliable reconnect — fixed a TUN failure on Linux (
EBUSY) and a descriptor leak on Android during reconnect - Windows: fixed network-adapter creation (Wintun) when a stale registry entry blocked it
- A clear error when generating a link for a profile that isn't running yet
- Network-compatible with 0.7.4 — no settings change
- UDP now works on mobile networks (LTE/CGNAT) — the post-quantum handshake is now chunked at the application level, so networks that drop IP fragments no longer hang the UDP connection
- More stable UDP connection — removed a needless reconnect on an idle tunnel and a session drop on download-only traffic
- Correct UDP traffic accounting — incoming bytes (RECV) no longer show as zero in
list-clients - Handshake fragmentation implemented in every client (Linux / Windows / macOS / Android)
- Backward-compatible: the new server also works with older clients; TCP unaffected, no settings change (update the server before clients)
- Android: split-tunnel from config — the full/split-tunnel mode is set by the
gatewaykey and survives editing; correct parsing of DNS and reconnect settings - Android: more robust connection — if IPv6 is rejected, the tunnel comes up over IPv4 instead of failing
- Accurate UDP-profile availability indicator — no more false-red dots on working UDP modes (the probe now uses the post-quantum handshake)
- Linux kill-switch moved to iptables — one firewall backend; as a bonus it now works on Keenetic routers
- Security hardening from an external audit — safer native handlers (FFI), a separate signing key for web-panel sessions, AES key zeroization, log-injection protection
- Web panel polish (client manager, light theme for logs and editors) and Docker as an install option
- Network-compatible with 0.7.2 — no settings change
- Traffic-shape smoothing (optional) — when idle, the tunnel keeps an even background flow instead of going silent, for a more private connection profile; it doesn't affect the latency or speed of real data
- Web panel: stronger brute-force protection — the login pages can no longer bypass the attempt limit
- Crash-safe settings storage — the users, config and key files are written atomically (a power loss can't corrupt them or lock you out)
- Android: stability — fixed a hang (ANR) on reconnect and improved rendering on small / low-resolution screens
- Published a security policy (SECURITY.md) and a threat model — what is and isn't protected
- Added fuzz-testing of the network-data parsers
- Network-compatible with 0.7.1 — no settings change
- reality-tls is faster — download +30% (a new in-house TLS server instead of a third-party one)
- Two external security audits reviewed — fixes released, every finding checked
- Protection against locking someone else's account: username guessing is now slowed down (tarpit) rather than locking out
- Server-key pinning enabled by default — protection against a spoofed server
- Fixed device accounting (guard against an empty device-ID), refined kill-switch and reconnect
- Human-readable
qeli://links; the update is network-compatible with 0.7.0
- Post-quantum tunnel encryption — hybrid X25519 + ML-KEM-768 (FIPS 203) in every mode except plain
- Protection against “harvest-now, decrypt-later” attacks by a future quantum computer
- On by default and enforced — the server allows no silent downgrade to classical crypto
- Post-quantum on every client: Linux, Windows, macOS, Android (shared Rust ML-KEM core)
- Security & reliability audit — fixes from two external reviews
- Speed is unchanged — post-quantum affects only the handshake
- Multi-device — several devices on one account at once, each with its own IP address
- Seamless reconnect on network change — instant reconnect when switching between Wi-Fi and mobile data
- The “Disconnect” button responds instantly, even during a reconnect
- Per-user device limit — set in the web panel (the oldest device is dropped when exceeded)
- Shared Windows & macOS core — one .NET 10 library (more robust, less code duplication)
- Protocol, encryption and speed are unchanged — the update is fully compatible
- reality-tls — tunnel inside real TLS 1.3, byte-exact Chrome fingerprint (JA4 t13d1516h2_8daaf6152771); own Rust realtls core
- Certificate borrowing for REALITY — the server serves the target site's real certificate chain
- Post-quantum key exchange X25519MLKEM768 (ML-KEM-768)
- Stream bonding (multipath) — several TCP connections in one session
- Multi-core data plane: multi-queue TUN, SO_REUSEPORT for UDP
- Web panel — user management and QR codes
- reality-tls on every client — Linux, Windows, macOS, Android (shared realtls engine)
- Initial public release
- 6 transport modes: plain, fake-tls, obfs, reality, reality-tls, quic
- Clients: Linux, Windows, macOS, Android
- Web management panel (beta)
- Windows client — C# / .NET, service + tray
- macOS client — C# / Avalonia (.NET 8), port of qeli-win; utun tunnel
- UDP profiles and QUIC v1 short-header shaping
- Split-tunnel via routes
- Migration to flat-INI config (replacing JSON)
- Socket-based management (list-clients, kick, disable)
- CLI: add-client, show-identity, rotate-identity
- Android client — Kotlin, TCP/UDP
- fake-tls transport with JA3/JA4 randomization
- obfs transport — ChaCha20 stream over WebSocket Upgrade
- LAN access behind the server (routing.forward_private)
- Auto-reconnect on connection loss
- DNS resolver in the tunnel
- Linux client — CLI tunnel on TUN
- reality transport (proxy mode with a borrowed TLS certificate)
- Multiple profiles (tcp + udp in one process)
- Authentication: Argon2id, users.conf file
- Crypto stack: X25519 + HKDF-SHA256 + ChaCha20-Poly1305
- Qeli links (qeli://) and QR code import
- First prototype: L3 tunnel on TUN
- Base plain transport — encrypted tunnel with no extra wrapper
- Single-profile TCP server with NAT (MASQUERADE)
- JSON config: bind, tun, user onboarding
0.8.2 release notes → Qeli 0.8.2 on GitHub → ← Back to documentation