Yume Universal Multiprotocol Engine. An open-source post-quantum stealth transport.
YUME tunnels TCP and UDP through TLS 1.3 sessions that look like ordinary Chrome HTTPS to a DPI box, with hybrid ML-KEM-768 + AES-GCM inner crypto, an optional Argon2id heavy KDF, and 1–4 Hz live key hopping. Both the client (yume) and the daemon (yumed) are GPL-v3 and build from this tree. They run on x86, ARMv7/8, MIPS OpenWRT, BusyBox, macOS, and Windows; the minimal build runs on routers with as little as 128 MiB of RAM.
- Website: https://yume.fixcraft.jp
- Source: https://github.com/FixCraft-Inc/yume
- Issues: https://github.com/FixCraft-Inc/yume/issues
VPN protocols built for performance (WireGuard, OpenVPN) are also built to be recognisable. Their handshakes have static byte signatures that ISPs and national firewalls can match in milliseconds. Commercial VPN services then resell that same recognisable transport for $20/month, bandwidth that costs them pennies, and run it from cheap KVMs that any user could rent directly.
YUME tries to do the opposite: a transport that looks like ordinary Chrome HTTPS to a CDN, with crypto that survives the move to post-quantum, with both ends fully open-source so anyone can audit, build, and self-host. FixCraft will run a fleet of free public endpoints (no signup, no payment), but the endpoints run the same yumed you can build right here.
| YUME | WireGuard | OpenVPN | Tor (with bridges) | Shadowsocks | |
|---|---|---|---|---|---|
| Hybrid post-quantum (ML-KEM-768) | yes | no | no | no | no |
| Live key hopping | 1–4 Hz, OTA | no | no | no | no |
| Looks like real HTTPS to DPI | yes (Chrome JA3 + HTTP/2 carrier) | UDP signature | TLS-on-OpenVPN-port signature | obfs4 bridge | random-prefix |
| Anonym / no-log mode | built in | n/a | per-provider | yes | n/a |
| Free public endpoints | planned (FixCraft) | none | none | yes | none |
| Embedded / 128 MiB hardware | yes | yes | yes | partial | yes |
| Self-hostable, fully open | yes (GPL-v3) | yes | yes | bridge only | yes |
| Steady-state CPU/byte overhead | <1 % typical, <5 % always | ~0 % | a few % | high | low |
| License | GPL-v3 | GPL-v2 | GPL-v2 | BSD-3 | Apache-2 |
YUME's row comes from the live measurement reported in docs/PERFORMANCE.md. The other rows are conservative; anything contested is left blank or hedged.
cmake -B build
cmake --build build -j$(nproc)The build produces build/bin/yume and build/bin/yumed.
Server:
sudo ./build/bin/yumed \
--listen 443 \
--cert certs/server.crt --key certs/server.key \
--auth-keys /etc/yume/authorized_keysClient:
./build/bin/yume \
--server fixcraft.net \
--auth ~/.yume/id_ed25519 \
--socks 1080For a privileged port 443 on Linux, run yumed with sudo or grant cap_net_bind_service. Cloudflare HTTP-mode proxies will terminate TLS and break YUME. Use Spectrum or another TCP passthrough if you front the daemon with Cloudflare.
A Dear ImGui + GLFW desktop application is available in the same tree and is off by default. It uses the shared facade library for local server control and starts the real yume client runtime in the background, attaching to it through local IPC. The GUI is intended for desktop users; the CLI remains the supported automation surface.
cmake -B build-gui -DYUME_BUILD_GUI=ON
cmake --build build-gui -j$(nproc)
./build-gui/bin/yume-gui # main window
./build-gui/bin/yume-gui --help # CLI options
./build-gui/bin/yume-gui --headless # facade-only smoke testYUME_BUILD_GUI=ON pulls Dear ImGui, GLFW, and ImPlot via CMake FetchContent (pinned tags). On Linux the system tray (minimise-to-tray) is enabled automatically when libayatana-appindicator3-dev is present; without it the GUI builds normally but the tray icon is disabled.
System dev packages (Debian/Ubuntu):
sudo apt install libgl-dev libglfw3-dev libxkbcommon-dev \
libfreetype-dev libfontconfig-dev \
libayatana-appindicator3-dev- Client page for the main connect/disconnect workflow and saved server profile
- Security page for trusted anonym CAs and imported client auth keys, with a built-in default CA
- Overview with larger crisp desktop typography, connection status, local server status, byte counters, and a 60-second traffic graph (ImPlot)
- Server page that hosts a local server with the same controls as
yumed - Tools area for key generation, authorized-key management, logs, appearance, relay directory, and chat
GUI profile, trust material, generated keys, and runtime data live under ~/.yume/.
yume-gui ships as a separate binary package alongside yume. The build is gated by the nogui build profile — setting DEB_BUILD_PROFILES=nogui produces only the CLI .deb (matching the stock GitHub release flow). The default build produces both packages.
ServerSession::start()runs a real in-processyumedruntime through the shared server manager. Privileged ports still require root orcap_net_bind_service.ClientSession::start()currently launches theyumebinary as a managed background process and communicates with it through the existing local runtime socket. A pure in-process client controller still requires extracting the TLS/auth handshake fromclient/cli.cpp.- Chat / directory pages depend on a connected background client and use the live
RelayRuntimeIPC surface. - The tray code path is present but only assembles when
libayatana-appindicator3-devis installed; the rest of the GUI works without it.
Install from a build tree:
cmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build -j$(nproc)
sudo cmake --install build
sudo mandbBuild a Debian package:
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/usr
cmake --build build -j$(nproc)
(cd build && cpack -G DEB)Or use the helper:
./ezbuild.sh --debThe package installs yume(1), yumed(8), and the Markdown docs. See
docs/PACKAGING.md for cross-architecture package
notes, BaseFWX package dependency details, and manual man-page installation.
cmake -B build -DYUME_MINIMAL=ON -DYUME_USE_BASEFWX=ON
cmake --build build -j$(nproc)ezbuild.sh cross-compiles for Linux x86_64 / x86 / ARMv7 / ARMv8, MIPS OpenWRT, BusyBox flavours, macOS x86_64 / arm64, and Windows x86_64. Prebuilt vendor toolchains live in vendor/.
FixCraft will operate a small fleet of public yumed endpoints. They will:
- be free to use without an account, payment, or rate-limiting beyond fairness
- run the unmodified daemon you can build from this tree
- serve a real HTML page on
/so that a browser hitting the same hostname sees something normal - publish their public keys and authorised-keys fingerprints in advance so clients can pin them
Specific hostnames will land here once the fleet is up.
YUME stacks three independent layers of byte-shape camouflage:
- TLS 1.3 with browser fingerprint.
--profile chrome|firefox|safariconfigures cipher suites, supported groups, signature algorithms, and ALPN to match Chrome 135 / Firefox 126 / Safari 17. The handshake is a real TLS 1.3 handshake (OpenSSL emits the ClientHello against the configured profile), so JA3/JA4 fall in the browser cluster. Source: src/core/tls_stealth.cpp, src/core/tls_fingerprint.cpp. - HTTP/2 carrier handshake (
--obfs, default on). After the TLS handshake the client emits a real HTTP/2 connection preface (PRI * HTTP/2.0…), Chrome-shaped SETTINGS, a WINDOW_UPDATE, and a HEADERS frame forPOST /<token>/<nonce>with realistic Chrome request headers. The server validates the token (HMAC-SHA256 over(SNI || hour || "yume-obfs-v2")keyed by--obfs-secret), replies with canned SETTINGS / SETTINGS-ACK / HEADERS:status=200, and the YUME tunnel resumes underneath. To a stateless DPI box the first ~150 cleartext bytes of every connection look exactly like a Chrome → CDN gRPC-web request. The codec lives in src/core/obfs_h2.cpp; the token derivation in src/core/obfs_signal.cpp. Disable with--no-obfs. Per-frame DATA wrapping with PADDED frames and PING keepalive is implemented in the codec and is on the post-1.0 roadmap to enable by default. - Real HTML facade (
--real --real-index <html>). A browser that hits the same port withGET / HTTP/1.1is served the configured HTML page (or a Wikipedia redirect by default). YUME clients and browsers cohabit on port 443.
Limits: this defends against stateless DPI, classifier-based ISP filters, and active probes that complete TLS and inspect the first kilobyte. It does not defend against fully-stateful HTTP/2 middleboxes that track stream and HPACK state, or against ML traffic classifiers trained on joint inter-arrival × size distributions.
The CLI can hide the outbound connection behind a SOCKS5 proxy. Hostnames are sent as ATYP_DOMAIN, so .onion targets resolve on the proxy side and direct DNS never leaves the client.
+--------------------------------+
| HUMAN APP |
| browser / curl |
+--------------------------------+
|
| local SOCKS / --run
v
+--------------------------------+
| YUME CLIENT |
| --tor or --proxy |
+--------------------------------+
|
| SOCKS5 to <onion>:443
v
+--------------------------------+
| LOCAL TOR |
| 127.0.0.1:9050 |
+--------------------------------+
|
| encrypted Tor cells
v
+--------------------------------+
| TOR CIRCUIT |
| hidden-service rendezvous |
+--------------------------------+
|
| rendezvous on server
v
+--------------------------------+
| SERVER TOR |
| publishes .onion |
+--------------------------------+
|
| TLS 1.3 + YUME frames
v
+--------------------------------+
| YUMED SERVER |
| binds 127.0.0.1 only |
+--------------------------------+
|
| outbound socket
v
+--------------------------------+
| TARGET SITE |
| sees yumed egress IP |
+--------------------------------+
Diagram source: docs/diagrams/tor_pipeline.spec — regenerate with scripts/draw_pipeline.py docs/diagrams/tor_pipeline.spec. Widths are enforced by scripts/check_ascii_diagrams.py.
Client side. Add to config/yume.json:
{ "outbound_proxy": "socks5://127.0.0.1:9050",
"server": "abcdefghijklmnop.onion",
"port": 443 }Or on the command line:
yume --tor --server abcdefghijklmnop.onion --port 443 -i id_ed25519
yume --proxy socks5://user:pass@10.0.0.5:1080 --server gateway.example --port 443 -i id
yume --no-proxy ... # one-shot override that ignores config
Server side. No code change. Bind yumed to loopback and let Tor publish a hidden service. In /etc/tor/torrc:
HiddenServiceDir /var/lib/tor/yume/
HiddenServicePort 443 127.0.0.1:443
After Tor starts, cat /var/lib/tor/yume/hostname gives you the .onion to point clients at. yumed doesn't know it's reachable through Tor — it only sees 127.0.0.1 connections.
The proxy applies to the outer transport (TCP → SOCKS5 → TLS → Yume). Inner PQ crypto, anonym proof, HTTP/2 carrier obfuscation, and TLS stealth still apply on top — Tor adds an extra circuit hop, not a replacement for any of those layers.
--anonym strips identifying logs (no client hostname, no IP, no authentication line). --anonym-proof-mode {auto|local|fixcraft} selects how the server proves no-log compliance to the client:
auto: use every available proof source; only fail to start if none are usablelocal: CA / Sub-CA-signed proof only, no remote APIfixcraft: require a remote FixCraft Verity API call; local proofs may also be attached
Local proof setup is in scripts/gen_anonym_sub.sh. Clients trust a server's anonym claim by holding the matching CA cert (anonym_ca_cert) and setting require_anonym: true.
Optional inner encryption sits inside the TLS tunnel and runs end-to-end:
- ML-KEM-768 KEM (post-quantum) combined with the user's password / PSK via HKDF-SHA256
- AES-256-GCM AEAD (BaseFWX) on every YUME frame
- Argon2id heavy KDF (
--inner-heavy, default on for full builds) or HKDF-SHA256 light KDF - Live key hopping: a fresh 32-byte key derived from the base key every
--hop-intervalms (default 500, i.e. 2 Hz). Tested up to 4 Hz; configurable down to 0 (off).
// server config
{
"inner_crypto": true,
"inner_heavy": true,
"pq_private_key": "/etc/yume/master_pq.sk",
"allow_exec": false
}The embedded BaseFWX master PQ keypair is off by default (src/core/inner_crypto.hpp:17). It is only loaded when the operator explicitly passes --use-embedded-master, and both client and server log a warning at startup when that flag is in effect. Provide your own --pq-pub / --pq-key for production deployments.
Numbers below are from a live run reported in docs/PERFORMANCE.md. Client in the United States, relay in Japan, fixed endpoint for fair RTT.
| Metric | 0 Hz hopping | 2 Hz hopping |
|---|---|---|
| YUME-only ping overhead (filtered) | ≈ 0 ms | +1.4 ms |
| Throughput retained vs relay capacity | 78 % | 78 % |
| Client upload retained vs direct | 92 % | 92 % |
The throughput loss on long routes is dominated by the relay path's own capacity, not by YUME's CPU or framing. With hopping disabled, YUME's steady-state overhead is below the noise floor of the measurement.
SOCKS proxy (default):
yume --server fixcraft.net --auth id_ed25519 --socks 1080Port-forward, SSH-style:
yume --lport 2222 --rhost fw-main.fixcraft.jp --rport 22Reverse forward (server listens, tunnels back to the client's local port):
yume -R 7437:127.0.0.1:22Local run. Every TCP/UDP socket the command opens is routed through YUME:
yume --server fixcraft.net --auth id_ed25519 --run "curl https://1.1.1.1"Force IPv4 for --run (adds -4 --http1.1 to curl/wget):
yume --server fixcraft.net --auth id_ed25519 --run-ipv4 --run "curl https://ifconfig.me"SSH (auto-wrapped to route via local SOCKS when nc, ncat, or connect-proxy is available):
yume --server fixcraft.net --auth id_ed25519 --run "ssh user@host"Server-side command execution is disabled by default for safety. Use SOCKS or port forwarding.
Authentication and authorization live in two files, the way SSH splits authorized_keys from per-line options:
authorized_keyslists Ed25519 public keys that may connect.auth_keys.metais a JSON file mapping each key's fingerprint to what it may do (exec / LAN bridge / admin / chat / file / bytes). Default for every dangerous bit is deny; a key without a meta entry can connect but cannot exec, cannot reach LAN, cannot administer other clients.
Dangerous server features (server-side exec, LAN bridging, unrestricted address bridging) sit behind a three-layer gate that all must agree:
- Build switch:
cmake -DYUME_FEATURE_EXEC=ON(also_LAN_BRIDGE,_FULL_CONTROL). Stock builds ship with all three OFF; the runtime CLI flag still parses but logs a warning and stays disabled. - Runtime flag:
--allow-exec,--allow-local-ip,--control-fullonyumed. - Per-key meta:
"allow_exec": true(etc.) inauth_keys.metafor the specific key.
Removing any one layer is enough to keep the feature off. The bridge / admin matrix (server-controls-client × client-controls-server, four quadrants) and the full meta JSON schema are documented in docs/PERMISSIONS.md.
./build/bin/yumed --auth-keys /etc/yume/authorized_keys --keys-list
./build/bin/yumed --auth-keys /etc/yume/authorized_keys --keys-add /path/to/user.pub --keys-alias <fingerprint> alice
./build/bin/yumed --auth-keys /etc/yume/authorized_keys --keys-remove alice
./build/bin/yumed --auth-keys /etc/yume/authorized_keys --keys-gen ./keys/user1 --keys-gen-addAuthorized keys are loaded once at startup. Verification uses EVP_DigestVerify (constant-time at the OpenSSL level). The auth_keys.meta file is also read once at startup; restart yumed after editing.
Serve a real HTML page on / and 302 everything else to /:
sudo ./build/bin/yumed \
--listen 443 \
--cert certs/server.crt --key certs/server.key \
--auth-keys /etc/yume/authorized_keys \
--real --real-index certs/index.html \
--real-secret "change-me"Auto-generate and persist the HTML hidden-blob secret:
sudo ./build/bin/yumed \
--listen 443 \
--cert certs/server.crt --key certs/server.key \
--auth-keys /etc/yume/authorized_keys \
--real --real-index certs/index.html \
--real-secret-file ./.secrets/html_secret--real and --obfs may be set together; they share port 443 and are demuxed by the first cleartext bytes after TLS.
- GPL-v3, both client and daemon fully buildable from this tree
- BaseFWX is pinned by commit (see
.basefwx-ref); release CI fails if mandatory crypto support is missing - Authorized keys verified with constant-time
EVP_DigestVerify(src/server/auth.cpp:78–99) - Inner-frame AEAD verified before plaintext is delivered (OpenSSL
EVP_DecryptFinal_ex) - Master PQ keypair off by default; explicit
--use-embedded-masterrequired and warned about at startup on both ends - Server-side exec / LAN bridging / unrestricted bridging are off at compile time by default (CMakeLists.txt
YUME_FEATURE_EXEC/_LAN_BRIDGE/_FULL_CONTROL); enabling them requires opting in at build, runtime flag, AND per-key meta (see docs/PERMISSIONS.md) - Per-key admin permissions (
allow_inbound_admin,allow_outbound_admin) default to deny - Frame size capped at 16 MiB across all read paths
- New obfs path-token verifier uses
CRYPTO_memcmp(src/core/obfs_signal.cpp) - No security-by-obscurity: every claim above points at code. Features are either implemented or absent; no placeholders that pretend to protect anything.
- Server sessions are fully async on a shared
io_contextthread pool (no per-connection threads) - Authorized keys are loaded once at startup
- Frames are capped at 16 MiB per message to limit memory pressure
- Carrier-mode handshake adds one extra HEADERS frame per session, no per-frame cost in current default
- Release workflows run preflight validation against the pinned BaseFWX commit
- Release artifacts are inspected after build for linkage / runtime expectations
- Missing mandatory BaseFWX crypto support is a release failure, not a degraded release
- Full releases require Argon2, PQ/OQS, and LZMA support in the bundled BaseFWX dependency path
GNU GPL v3. See LICENSE.