ZTUNNEL-CORE // EMBEDDABLE VPN ENGINE

// Rust OpenVPN + WireGuard · config parsing · connection manager · management protocol · logs · throughput — the core behind ztunnel, embeddable in any host

ztunnel-core is the embeddable engine behind ztunnel, a Tunnelblick-style VPN client. It owns the saved configuration model, OpenVPN (.ovpn / .tblk) and WireGuard (.conf) parsing, a connection-lifecycle manager, the OpenVPN management-interface protocol, the WireGuard config model, per-connection logs and throughput, and a credential boundary — and exposes all of them through one command surface (Engine::invoke), so every host drives identical behaviour. It carries no GUI and no platform UI deps, so the standalone app and every embed share the exact same code. The crate builds as rlib for native Rust/Tauri hosts and as staticlib/cdylib for non-Rust hosts over a C ABI. This project is in active development.

Two Backends

OpenVPN by driving the system openvpn binary + its management interface (the faithful Tunnelblick model), and WireGuard via a native pure-Rust userspace data path — a superset of Tunnelblick's OpenVPN-only scope.

Embeddable

One command surface — Engine::invoke(cmd, args_json) — drives every host identically: native Rust, a Tauri plugin, or a C/C++ host over the FFI (ztn_invoke).

Cross-Platform

macOS + Linux behind a platform abstraction (utun / /dev/net/tun, routing, DNS). Tunnelblick is macOS-only; ztunnel-core abstracts the OS layer from day one.

Honest Status

Tunnels need root; the engine, parsers, manager and GUI are complete around a privileged helper that is a marked phased deliverable. Until it lands, connect reports a needs_privilege error state — it never fakes a live tunnel.

Quick start

use ztunnel_core::Engine;
use serde_json::json;

let mut engine = Engine::new()?;

// One dotted command surface for every host.
let info = engine.invoke("version", &json!({}))?;

// Import a config (OpenVPN or WireGuard, auto-detected), then connect.
let added = engine.invoke("config.add", &json!({ "name": "office", "raw": ovpn_text }))?;
let conn = engine.invoke("vpn.connect", &json!({ "id": added["id"] }))?;
let log  = engine.invoke("vpn.logs", &json!({ "id": added["id"] }))?;

Surfaces

Depend on the rlib and call Engine directly, or go through Engine::invoke with JSON for parity across hosts. Enable the tauri feature to mount the engine as a Tauri v2 plugin. Link the staticlib/cdylib and call ztn_invoke from a C ABI host. Asynchronous facts (status changes, byte counts, log lines) are pushed to the host through an installed event sink.

Where it goes next

See the engineering report for the module map, dependency footprint, FFI surface and test status, the Tunnelblick port report for feature coverage, and the MenkeTechnologiesMeta umbrella for the wider stack this engine plugs into.