Development changelog
These entries record development changes, including withdrawn version plans and test builds. Implementation status describes current behavior and qualification.
[Unreleased 0.3.0-dev1]
Native TCP and accepted TCP/UNIX byte channels report socket read errors as transport failures instead of directional EOF. Socket failures in either direction close the channel before delivery and settle queued operations once. Later reads retain the terminal failure. The direct TCP readiness probe also preserves errors it consumes. Orderly EOF and immediate cancellation keep their existing behavior.
The session engine retains a closing stream until its required peer settlement arrives when an abort crosses both directional FINs. A valid crossed settlement therefore preserves the authenticated session for later streams.
The experimental C ABI adds yume_config_validate_client_credentials to
validate actual client credential files and composite private/public key
pairs before application publication. It returns the local composite
fingerprint without starting an endpoint or authenticating a server.
Native session startup clears its stored one-shot callback before delivery. Repeated embedded client starts and stops release their run owners and polling descriptors on libc++ as well as libstdc++.
Stopping device-backed ABI startup now settles its session-keeper waiter
without waiting for the connection deadline. The public start returns
YUME_STATUS_CANCELLED after owned work drains and leaves the endpoint
STOPPED.
The product version moved to 0.3.0-dev1. yume and yumed compose native
YTP/1 with schema-1 configuration, and transport v2 has been removed. The
YTP/1, relay channel, schema, ABI and evidence-profile versions are
independent axes. docs/IMPLEMENTATION_STATUS.md is the current
boundary for what the 0.3 foundation implements, tests, and still gates.
Added
-
Community translations. The shared catalog accepts human-reviewed translations of languages the team does not maintain, with each language’s translators or studio credited. A language is listed from 80 % coverage, falls back to English per message, and never translates the route-consent, expert-warning and trust texts marked
maintainer_review. Every language, maintained or community, now credits who translated it; the maintained ones credit FixCraft Inc. The desktop follows the system language and lists the credits on its Credits page. Android’s picker shows Native and Community groups apart, each language with its translators’ names. Only maintained languages gate releases. -
Simplified Chinese. Simplified Chinese (
zh-Hans) is a maintained language in both apps, with every message translated, the safety texts included. A catalog language may name its script, and Chinese must, sozh-CN,zh-SGandzh-Hanslocales read it and Traditional locales stay English until Traditional Chinese has its own translation. Android no longer machine-translates Chinese, since its on-device translation produces Simplified Chinese only. -
One palette and mark across the apps.
config/palette.jsonis the single colour source for the desktop GUI, the Android app and the website, andscripts/yume_palette.pygenerates each one’s colours from it and checks the contrast every theme must keep. The colours are soft plum and sakura on the mark’s hue, with light and dark designed separately: in light, warm-white cards on a faintly pink page and blossom main buttons with plum text, and in dark, a plum night with a soft pink accent, plus a true-black variant on Android. Status colours are natural sage, apricot and rose. The desktop follows the system’s light or dark setting by default, as Android does. The mark keeps its original shape everywhere, filled with the solid colours it shows over white, and the Android launcher shows it on a warm-white tile. Notifications use a white outline of the mark in the status bar. Long Android explanations sit behind an info button, and each card keeps one short visible line. Desktop controls were revised to match: soft button states, a filled slider track, a chevron drop-down, and hairline cards. On the website the layered “what wraps your data” figure has light, evenly spaced rings whose labels clear their corners, the hero cloud is built the same way in both themes, and the header no longer draws a line across the section navigation. -
Interface design pass. The desktop moves explanation out of the page body: the import note and the tuning measurement’s conditions sit behind ? buttons, kit actions are named icon buttons, and the Credits page lists components and translators in compact groups. Tuning labels, notes and measurements are translated on the desktop as on Android. On Android, each tuning slider ends at its axis’s highest level instead of five, the language picker lists System default before the Native and Community groups and uses more of the screen, report buttons stack at large text sizes, and a phone on its side gets a navigation rail.
-
Desktop connection settings. Three coupled sliders use the shared tuning table, with measured conditions, plain values and the session’s negotiated server cap. A confirmed expert editor applies one validated field at a time through
yume-setup. Applying to a running client waits for process exit before restarting; stopped clients only save. -
Existing client tuning.
yume-setup tune --config FILEapplies a--presetor one expert--set KEY=JSONafter schema validation, with atomic replacement. Restart the client to apply it. The shared preset table addsstealth-16, measurement conditions, display fields and a deterministic slider coupling rule. Stealth remains the default. - yumed serves on several event loops.
endpoint.event_loopssets how many threads serve sessions, by default the CPUs the daemon may run on, at most 16. One loop accepts each connection and hands it, before TLS, to the loop serving the fewest sessions, where it stays for its whole life. The admission replay cache, the credentials a reload publishes, each identity’smax_sessions, the direct-route budgets, the egress rate and the resolver helper hold across loops, a module’s streams run on their session’s loop, and--statusshows the sessions on each loop. A cluster member or a server with a packet adapter keeps one loop. - Explanatory diagrams. Device bridge, circuits, cluster links, directional key rotation, GUI lifecycle, module streams, relay library, sealed kits and trust boundaries share one JSON source across web, Markdown and manuals.
- libc++ CI gate. The native application, module libraries and shared ABI are built and tested with Clang and libc++, which Android uses. Matching LLVM archive tools preserve LTO symbols despite older runner-image plugins. Retry-timer allocation fixtures intercept the POSIX path Asio can select with libc++. The TCP provider’s DNS round-trip fixture uses a named IPv4 loopback answer through the resolver helper, and timeout diagnostics name the test and wait.
- Device bridge in the C ABI.
yume_endpoint_set_deviceattaches a TUN descriptor to a client endpoint, which then carries what enters the device on the kit’s ordinary services: one byte-stream OPEN per TCP connection and one packet OPEN per UDP destination, as the SOCKS5 adapter does for a local application. The host kernel terminates each TCP connection, so no user-space TCP stack and no local proxy hop is involved, and the server sees the same services, limits and OPENs as from ayumeclient of the same kit. Lookups to one resolver share a stream. Such an endpoint keeps its session up with the client’s backoff, resets connections made while no session is active, andyume_endpoint_retry_nowstarts a waiting attempt for an application that learns the network is back. A path that stops answering ends the session at YTP/1’s 30 s rotation acknowledgement deadline.yume_abi_device_integrationruns it between two network namespaces: 300 parallel connections, UDP, shared lookups, refusals, session loss, a silent path, reconnect and restart. - Endpoint status, messages and sealed kits in the C ABI.
yume_endpoint_get_statusreturns one snapshot of the session, its traffic, the latest failure and the wait before the next attempt.yume_endpoint_read_messagereads the endpoint’s message feed, the linesyume --messagesshows.yume_kit_openopens a kityume --seal-kitsealed, so an application imports one file and a code instead of copying keys. A stop now cancels a client start in progress on another thread. - Identity route reservations. Direct TCP and UDP route capacity now reserves pending and active slots per authenticated identity across its sessions. Failed, canceled and closed opens release the reservation; a circuit exit accounts its authenticated previous hop, not the hidden client.
- Circuit streams share a slow path evenly. When a circuit’s link or the client’s session was the bottleneck, one stream could take every cell slot: an exit’s destination read that completes at once read again before its sibling streams, and the client gave freed slots to its lowest stream. Both now serve waiting streams in turn, so parallel downloads and uploads through a circuit share its path evenly again.
- Android build of the library.
tools/build_android_shared_abi.shbuildslibyume.sofor the Android ABIs with the pinned NDK, the patched OpenSSL built static for each ABI and the vcpkg manifest’s dependencies. Verified builds require both a signed HEAD and a clean native source tree. Unsigned or modified sources require the explicit test-only--unverified-sourceoption; Android records the native commit and dirty state in its build identity. The sources also build with Boost 1.90, whose timers lost the error-code form ofcancel. - Configuration reference. docs/CONFIGURATION.md
lists every schema-1 key with its role, bounds and meaning, and the tuning
presets, which moved there from the development guide.
tests/test_config_reference.pyrequires every key the parser accepts to be documented there and used by one of four complete documents undertests/fixtures/config, andyume_config_v1_closureputs an unknown key into every object of those documents and requires a failure at that object.tests/test_doc_paths.pyrequires every repository path the documents name to exist. - Contract pages first. CONTRIBUTING requires a recorded owner requirement
and the interface’s contract page before a feature’s code.
tests/test_contract_pages.pyrejects a control request, control error code or ABI export that its page does not state, and the ABI page now names every exported function. - Guardrails that fail the build or CI.
cmake/YumeLayering.cmakeis an allowlist: every directory undersrc/declares the layers its production sources may include, a new directory fails until it is declared, and every static or shared library must pin its direct link list. The CI jobreachabilitybuilds the full graph at-O0with one section per function and--gc-sections, andscripts/check_reachability.pythen requires every first-party function to survive in a shipped program or to be retained inconfig/reachability.jsonwith its reason.scripts/check_repository_hygiene.pychecks whitespace in every committed file and each pushed commit’s format and signature.tests/test_wire_labels.pyrequires every wire, KDF and AAD label in the source to appear in its contract page and the reverse. The documentation drift checks and the documentation tooling tests are CTests as well as CI steps. - Sequence diagrams. The documentation compiler draws messages between two to four parties from one JSON file, as an animated SVG in both themes and as ASCII for manuals and Markdown. YUME explained now shows where traffic goes, how a connection opens and what wraps the data on the wire, and the architecture shows the direct route.
- Relay channel library. The relay v2 end-to-end channel between two users
(handshake, sealed records and hybrid ratchet) builds apart from transport
v2 as
yume_module_relaywhenYUME_BUILD_BASEFWX_MODULESis on. Its labels and wire format are unchanged, which tests check against known answers from the transport-v2 code and against an independent Python reference that pins every handshake and ratchet label. It also holds the encrypted chat history, the relay secret, peer trust, and the invite and directory records with their policies. Peer fingerprints changed from the transport-v2 identity domain to the YTP/1 one, so a peer’s fingerprint is the oneyume-setupprints for its key.yume_module_filesadds the relay file transfer source and receiver. They are the base of the planned chat and file modules. - Application codec library. The application codec core (the HTTP
envelope, backend dialing rules and the Monero RPC descriptor) builds apart
from transport v2 as
yume_module_codecsfor a planned codec module. - SOCKS5 upstream provider.
providers/socks5_upstream.*reaches a server through a SOCKS5 proxy over any byte-channel provider, as transport v2’s client dialing did. It offers only the method its configuration implies, takes credentials that wipe themselves, and reads nothing past the proxy’s reply. A client configuration’sendpoint.socks5_proxynames the proxy’s numeric address and port and an optional protected credentials file with a username line and a password line, and a managed TUN excludes the proxy’s route from the tunnel. - Loopback cover fetch.
providers/loopback_http_fetch.*fetches GET and HEAD from a loopback web server within fixed bounds, the base of a reverse-proxy cover. Unlike transport v2’s client, it refuses a backend that closes before a complete response.cover.reverse_proxystill fails start until the FrontDoor uses it. - Egress lists. Server
direct_tcpanddirect_udpdestinations can addlistsof JSON or VPN provider database files and a MaxMindcountry_database. The most specific entry decides and a deny wins a tie. Lists refuse more but never permit more thanpublicandnetworks. This brings transport v2’s egress filter to the native daemon without archive unpacking or the compact country format, and it fixes v2’s reading of 28-bit MaxMind records. - Outer-carrier evidence.
yume --outer-carrier-evidence PATHwrites a payload-free report on its first connection’s HTTP/2 and WebSocket carrier when the run ends: frame types and sizes, pinned or redacted header values, timing, the quiet interval before the close and the close itself. It is the YUME arm’s input to the classifier comparison with the captured browser, ported from transport v2 without its benchmark mode. - Native YUME capture arm.
tools/cover-node/capture_yume151_runs.shcaptures nativeyumeandyumedfrom one release bundle, andscripts/yume_carrier_workload.pyruns the frozen workload through yume’s SOCKS5 port where the browser arm runs a page script. The classifier-input validator takes the YUME arm’s application volume from the driver’s measured result and no longer requires a GOAWAY that the browser capture never recorded. The TLS relay now also records each record’s time, type and length, andscripts/yume_classifier_features.pyturns that timeline into the classifier gate’s feature document. - Shell completion.
yume --completion bashandyumed --completion bashprint a bash completion script for the program’s options, their fixed values and file arguments.scripts/yume_cli.pygenerates it from the manual sources with the help text, so the two cannot drift apart. - Control socket and
yume --status. A client configuration may namecontrol.socket, a UNIX socket where the running client serves its state to processes of its own user: the connection state, the server and its verified identity, session counts, traffic, local listeners and the last failure.yume --config PATH --statusprints it. The socket follows the owner-only rules of local UNIX listeners, serves at most four connections, and answers one JSON request line with one JSON line (control protocol 1). That page is the GUI’s contract: fields and requests are only added, every status reply lists the requests its program takes, and an error names a stablecodebeside its text.yume-doctorchecks the key, and the embedding ABI refuses a configuration that names one. - Cluster links. Servers of one operator form a cluster from a list the
operator signs with a composite Ed25519 and ML-DSA-87 key
(cluster 1). A server’s
clustersection names the list, its signature, the operator key, a peer store with a PSK for each direction of each pair and a state file.yumedrefuses a list that fails its signature, has expired or does not name it, keeps an authenticated YTP/1 link to every peer and closes them when the list expires. SIGHUP reloads the list and peer store and keeps every link whose inputs did not change. The server saves the highest serial it has loaded in the state file and refuses a list with a lower one, after a restart too.yume-setup cluster-init,cluster-add,cluster-removeandcluster-signprovision it,yume-doctorchecks the section, and the embedding ABI refuses it. The links carry circuits, below. yumed --status.control.socketis now available to the daemon, which reports its listeners, client sessions, cluster links and circuits there.- Circuit 1 codec and crypto. Circuit 1
specifies the circuits that will carry a client’s traffic through two or
three cluster servers: fixed-size cells, a hybrid X25519 and ML-KEM-1024
handshake with each hop signed by its composite identity, one AES-256-GCM
layer per hop and relay messages. The codec (
circuit/) and the constructions (providers/circuit_crypto.*) are built, with vectors from an independent Python generator, real-key handshake tests and a fuzzer. - Circuits on cluster servers. Every cluster member serves
yume.circuit: it answers each hop’s handshake, extends circuits over its links, relays cells, and, as an exit named bycluster.exit, carries their streams through adirect_tcpservice’s destination policy. It serves the operator-signed routes view onyume.routes: server names, identities, exit marks and network tags, without addresses. The cluster section gainsroutes,routes_signatureandexit, names starting withyumeare reserved for these services, and a client needs theyume.circuitgrant. Circuits are bounded per client, per link and per node, their streams’ windows are capped at an eighth of the session budget, andyumed --statuscounts circuits per link and refusals by bound.yume-setup cluster-add --exit,add-client --circuitsand the routes view incluster-signprovision it. - Circuits for clients. A client configuration’s
circuitssection (hops,min_hops,plain_httpand the operator key, routes view and state files) sends every SOCKS5 CONNECT and forward to a destination through circuits of two or three servers thatyumechooses from the routes view: distinct nodes, an exit last and no shared network tag, with measured latency, a spare and rotation. UDP ASSOCIATE and packet adapters are refused with circuits, and plain HTTP is refused unless allowed. A stream opens as soon as its BEGIN is on its way, so its first data follows without a circuit round trip, and a destination the exit refuses or cannot reach then ends the SOCKS5 connection. The client builds a circuit when its session starts. The largest cell of each direction, 16311 bytes forward and 16315 backward, and its framing fill exactly one TLS record. A client’s stream windows grow from 256 KiB to 1 MiB as its reader drains them. The client never shortens a route on its own: it proposes one, whichyume --accept-route IDaccepts over the control socket, orcircuits.min_hopsapproves in advance.yume --statusshows routes and proposals, and a proposal names itscostas a code beside the English text,yume-setup add-client --circuitswrites the section and kit copies, and the C ABI refuses the section.scripts/yume_circuit_wan.pymeasures the direct session and circuits of two and three hops across emulated links, under any tuning preset, and serves them toscripts/yume_circuit_cable.pyon another host.scripts/yume_circuit_capture.pycaptures them and describes the client session against a browser capture. - Sealed kits.
yume --seal-kit DIR --output FILEseals a client kit into one file and prints a 25-character code, andyume --import-kit FILE --into DIRopens it on the client’s device with that code. The file is Argon2id and AES-256-GCM from the core’s OpenSSL 3.5, with no plaintext header and fixed parameters (sealed kit 1). Only a client configuration is sealed, and the import writes a new owner-only directory.yume-setupnames the command after it writes a client. The format replaces the BaseFWX.ysscontainer library that transport v2 kept client settings in, and that library is removed. - Control requests for the desktop GUI.
yume --control-socket PATHnames the control socket for one run, since an imported kit carries no socket path, and--statusand--accept-routetake it too. Both programs answermessages, the latest 256 lines they printed, numbered, paged within 63 KiB and tagged with an instance that changes on restart.yumeanswersstop, then stops as onSIGTERM. Its status gainsposture: the transport, suite, evidence profile, security provider and crypto library, the tuning limits in use and, while connected, the session’s negotiated key epoch. All are additive under control protocol 1’s rules. - Desktop GUI.
yume-gui(YUME_BUILD_GUI=ON, Qt 6.4 or later) is a new Qt Quick window, an unprivileged client of the control socket that links no YUME library. It imports sealed kits throughyume --import-kit, starts a kit’syumeas a detached process that outlives the window, stops it with the stop request and waits for the process to end, and shows the connection, rates, listeners, circuits, printed lines and the fixed posture with the tuning preset it matches. A tray icon shows the selected kit’s state and connects or disconnects it. A shorter route is accepted only from a review of that exact proposal, which states what the route gives up in the interface language. Headless actions, a page capture, keyboard and right-to-left tests and a live cycle against a real daemon cover it (desktop GUI). Stopped kits can be renamed without replacing an existing kit; the GUI checks both control sockets before moving the directory and retains the selection. A client still draining after its socket closes remains active even past the stop warning. It is not packaged. - Modules. A server
moduleadapter runs a program for one stream service.yumedgives it a listening UNIX socket as descriptor 3, sends each authorized stream as a connection that starts with the client’s identity, and restarts the program with backoff when it exits. The program runs without capabilities under no_new_privs, and the kernel kills it if the daemon dies. It still runs as the daemon’s user and can read the daemon’s files, so only trusted programs belong there.yume-doctorchecks the adapter, andyume-echo-moduleis an example. See modules. - Local forwards. A client
forwardadapter listens on a loopback TCP port or a UNIX socket and turns each connection into a stream on its service, optionally to a fixed destination that the server’sdirect_tcppolicy checks. This replaces transport v2’s-Lforwards. Their TCP listeners were not limited to loopback, while native forwards are. A UNIX socket only accepts the client’s own user. UDP forwards are not ported yet. - Per-client speed limits. A server’s
limits.max_egress_mbpscaps the stream payload the daemon relays, and clients share it by theweightin their authorized-keys entries. Every served stream is paced in both directions, whatever its service. A reload changes a weight for open streams at their next transfer.yume-setupsets both andyume-doctorchecks them. This replaces transport v2’segress_mbpslimiter, which gave two busy clients half the configured rate between them and ignored their weights. - Bounded name-lookup shutdown. Client hosts and destination names now
resolve in a separate helper process instead of Boost.Asio’s single
in-process resolver thread. Cancellation settles immediately, one slow name no
longer delays other lookups, and closing a runtime kills and reaps the helper,
so SIGTERM or SDK stop no longer waits for a stalled
getaddrinfo. The helper holds no inherited connections and drops every capability.yumeandyumedserve as their own helper. The SDK installsyume-resolver, which embedding hosts pass asyume_runtime_options.resolver_program. Without it, a client that dials a name fails closed at start. - Per-identity session limits. An authorized-keys entry may set
max_sessions. A new session beyond it replaces that identity’s oldest session, so a reconnecting client displaces its own half-open session instead of being refused. Without the field the daemon’s capacity is the only bound, as before. Doctor validates the field against the daemon’s bound. - Credential reload.
yumedreloads its key stores and server keys on SIGHUP, and the Debian unit wiressystemctl reload, validating first. Removed identities lose their sessions at once, established sessions apply changed grants to their next stream, and an invalid store keeps the previous credentials. TLS and admission key changes still need a restart. - More client bundles.
yume-setup add-clientissues another client for an existing server tree and extends its authorized-keys store in one all-or-nothing step that keeps the store’s file owner.remove-clientrevokes one, and a reload ends its sessions. - Client status for embedding.
NativeClientRuntime::status()reports the connection state, last failure, verified server fingerprint, attempt counts and cumulative traffic from any thread, with an optional state-change callback.SessionEngine::traffic()supplies per-session byte counts. - Native operations guide.
docs/OPERATIONS.mdnow covers the native programs. The transport-v2 operator material moved todocs/TRANSPORT_V2_OPERATIONS.mdunder its reference program names. Release verification, which describes the native release, stays on the native page. The duplicate website build notes were dropped in favor of the documentation source guide. - Native acceptance tooling. Native-only builds register setup, doctor and packaging regressions. Bounded libFuzzer targets cover YTP framing, AUTH and schema-1 configuration; numeric JSON overflow now reports a typed validation error. Optional isolated network tests exercise the actual systemd-resolved service and competing streams under kernel-induced latency and backpressure.
- Cryptographic construction vectors. A public synthetic corpus and an independent generator cover transcripts, key schedules, record protection and directional rekey calculations. The internal crypto module exposes those deterministic constructions to tests without exposing them through the SDK.
- Native packaging migration. Release archives and Debian definitions use schema-1 tools and configuration. GUI and helper build lanes explicitly select the retained reference graph. The Debian service waits for operator-provided credentials and validates configuration before starting; package installation does not enable or start it. Release qualification remains open.
- Native terminal failure cleanup. The CLI reserves its runner-exception stop task before execution. Client reconnect and SOCKS accept retry failures that cannot recover close the runtime and report a typed terminal status, allowing the process to drain and exit. Ordinary failures keep backoff.
- TCP allocation-failure completion. Pending read/write construction keeps callback ownership with the caller until storage exists. Allocation refusal settles each callback once and returns queue capacity for subsequent traffic.
-
Shared allocation test hooks. The remaining ten private new/delete replacements use the shared out-of-line helper, including optional aligned provider allocation interception and secret-buffer quarantine. Fault policies and wipe-before-release observations remain owned by their tests.
- Development standalone runtimes. With every native provider enabled the
default build installs
yumedandyume. The daemon serves configured direct TCP/UDP adapters within their destinations. The client keeps one authenticated session with bounded reconnect and runs loopback SOCKS5 listeners. CONNECT streams join the existing direct-route bridge. Payload sent before the CONNECT reply stays in the socket until the remote endpoint accepts the route; the bridge preserves it and the client’s half-close. An OPEN deadline returns SOCKS5 0x06 (TTL expired); other cancellation keeps its existing status, and late acceptance cannot revive an expired request.yume_native_runtime_testcarries a payload through SOCKS5, checks refusals and requires clean SIGTERM exits. Managed Linux packet/TUN adapters and experimental SDK streams, packets and routes are also implemented; arbitrary standalone named-service handlers remain unfinished. - SOCKS5 UDP ASSOCIATE. A client
socks5adapter that names a packetudp_servicerelays UDP through a loopback socket tied to the client’s TCP connection. Each destination opens its own authenticated packet stream, so identity grants anddirect_udpdestinations apply as they do to CONNECT. Backlogs use the transport-v2 UDP budget of 64 datagrams and 1 MiB, now owned bycommon/udp_queue_budget.hpp, and destinations are bounded, expire when idle and wait before a refused OPEN is repeated. The schema-1 parser, doctor and setup kit carry the new key. Endpoint and process tests cover traffic, refusals, source locking, the budget and relay closure. - Runnable development kit. Installed
yume-setupandyume-doctoruse schema 1. The generated kit starts nativeyumedandyumewith SOCKS TCP/UDP. Managed TUN addresses, routes and DNS are explicit operator choices. - Client dial address. Schema-1 clients may set
endpoint.connect_addressto dial a numeric address while TLS and admission still authenticatehost. - Evaluation tooling.
scripts/ensure-ndpi.shbuilds pinned nDPI 6.0, or newer sources such as--ref devrecorded by commit.scripts/yume_ndpi_smoke.pyrecords nDPI output for one rootless loopback session with Ethernet-sized frames.scripts/yume_ethernet_smoke.pycompares repeated untunnelled and tunnelled transfers over a direct link after a preflight that fails when the link is unavailable. It can capture the tunnel on the remote interface, with offloads off for wire-sized frames, and run nDPI on that capture. It names the remote host with a required--remote-host.scripts/yume_wan_emulation.pymeasures sessions across emulated wide-area links without root: netem delay, loss and rate limits in a router namespace between client and server, with session setup time, direct and tunnelled download rates and small request round trips per condition. Their results are observations, not classifier verdicts or benchmarks. - Configured egress destinations. Server
direct_tcpanddirect_udpadapters requiredestinations:publicfor globally reachable unicast addresses andnetworksfor canonical CIDR prefixes.NativeEgressPolicyenforces them before DNS or socket creation, and for every resolved address when the route provider uses it. Unspecified, multicast and reserved addresses are never permitted, IPv4-mapped IPv6 is evaluated as IPv4, and public space excludes private, shared, loopback, link-local, documentation, benchmarking, 6to4, Teredo and NAT64 prefixes. The native endpoint no longer requires an application route callback, which can now only refuse more. Setup kits declare public destinations explicitly, and the native parser andyume-doctor-ytp1share network test vectors. Direct adapters withoutdestinationsno longer parse. - Endpoint-owned native accepts.
NativeEndpointcan keep a sized number of server starts pending on every listener, re-arming after each settlement and retrying refused or immediately failed starts after a delay. The schema-1 ABI backend uses it instead of its own loop. A listener that stops accepting, or a retry that cannot be scheduled while its listener has nothing pending, now closes the endpoint and is reported once. Previously the backend could leave such a listener idle without a report, or keep retrying a closed listener every 100 ms. Later ABI accepts reportYUME_STATUS_INVALID_STATEuntil restart. Deterministic pacing tests and socket tests cover refusal, capacity recovery, re-entrant completion, two listeners and an injected OS accept failure. - Native YTP endpoint composition accepts schema-1 direct TCP/UDP declarations with explicit destination policies. Route dispatch supplies the engine’s selected provider, removing independent selection by the built-in handler. Refusal, failed-listener rollback, packet boundaries and retained receive credit have focused native regressions. Standalone CLI/daemon and public-ABI destination adapters remain unfinished.
- Shared carrier build ownership. Native YTP and the remaining transport-v2 callers link one H2/WebSocket/wire-profile/observer library and one bounded-file library. Existing codec and file implementations are retained. The H2 library propagates its diagnostics declarations to callers; Release and MinSizeRel still compile diagnostics out. Existing WebSocket and bounded-file regressions now also run in the native-only build, with assertions enabled in Release.
- Schema-1 embedding backend. With the shared ABI and every native YTP/1
provider enabled,
yume_embed_ytp1runsNativeEndpointbehind the blocking C ABI on its own execution thread. Schema-1 clients open and servers accept authenticated named byte streams with the composite peer identity. A server holds each authorized OPEN until the application accepts it. Undeclared, unregistered and unauthorized services are refused, clients cannot register services, and declared adapters or reverse-proxy cover fail start. The backend links no transport-v2 target or BaseFWX. Such a library’s compatibility manifest and build info nameopenssl35.ytp1-securityand the loaded OpenSSL release, such asopenssl-3.5.7, instead ofunwired.yume_abi_ytp1_stream_integrationprovisions a kit and drives both roles. Packets, routed OPEN and standalone runtimes remain unfinished, and the backend has no production qualification. - Typed end of stream. Engine stream reads report the peer’s authenticated
FIN as
EndOfStream, separate from session end, abort and local close. The direct-route adapter half-closes a destination only on that status, and the ABI never reports a lost session as EOF, including after an earlier FIN. A thread-safe responder query keeps adapter-retained records and EOF subject to later termination. - YTP embedding deadlines and drain. Accept transfers local ownership without waiting for runner dispatch. Write shutdown bounds that dispatch and can be retried while its single FIN remains pending. Escaped runner exceptions stop the endpoint and drain accepted work; drain completion is published only after execution returns. Paused-executor regressions cover those cases, all-or-none writes and partial-read credit ownership.
- Restartable native listener. The FrontDoor enables address reuse on POSIX, so a stopped server can rebind while earlier connections sit in TIME_WAIT.
- Typed native listener failures. Socket setup distinguishes denied permission, occupied or unavailable addresses, resource exhaustion and unclassified failures through the engine and C ABI. Failed startup releases its socket; the ABI requires stop to settle FAILED before retrying, without disturbing another live listener. Unexpected exceptions no longer masquerade as memory exhaustion.
- Server authentication deadline. Native server startup begins its bounded session-creation/AUTH phase at validated carrier promotion, so an idle accept cannot consume a late-arriving client’s budget. FrontDoor ingress bounds and client end-to-end startup deadlines remain enforced. Readiness refusal or allocation failure closes the promoted carrier before session security starts.
- Resolved destination authorization. The native route provider requires policy for every selected numeric address before socket creation, including DNS answers and IPv4-mapped IPv6. Refusal, policy exceptions and reentrant cancellation fail the OPEN without connecting. Applications retain ownership of destination rules; standalone adapter composition remains unfinished.
- Allocation-failure test pairing. Nothrow scalar/array allocations use the same test allocator as throwing forms and matching deallocation. TLS tests exercise observers, countdown and sustained failure across those forms; production TLS allocation and sanitizer diagnostics are unchanged.
- Release fixture validation. Native TLS test credentials are generated
and checked even when release builds define
NDEBUG. - Native YTP session composition. Protected schema-1 credential loading and per-identity service policy now feed a shared native endpoint, with bounded admission/AUTH starts, owned session lifetimes and explicit executor drain. It directly composes the existing TCP/TLS/H2, FrontDoor and OpenSSL providers without BaseFWX. Standalone CLI/daemon adapters and the YTP public ABI remain unfinished. Explicit destination handlers can share the native endpoint and its route provider; TCP/UDP egress uses the same execution owner and reserved cancellation as ingress.
- Stream acceptance and settlement. OPEN success waits for peer service acceptance; routed handlers wait for destination establishment. Ordered close acknowledgement bounds retained stream state across crossed data, credit and cancellation. This intentionally corrects unfinished YTP/1 wire semantics; transport-v2, crypto domains and independent version axes are unchanged.
-
Native startup and teardown. Setup supplies complete cover pages and profile assets; static loading snapshots bounded routes and startup rejects missing priming files. IPv6 listeners use IPv6-only sockets. Bootstrap and engine teardown retain completions under selected allocation failures, and repeated terminal provider/endpoint cleanup is inert after final drain.
- TLS/H2 failure settlement. H2 close, cancellation and credit return use reserved control tasks shared with the native TCP executor. TLS/H2 retain completion ownership through construction, registration and diagnostic allocation failures. The native front door supplies both dispatch paths from its existing context; provider tests exercise one-shot and sustained allocation denial. This changes internal C++ provider seams, not the wire or C ABI.
- Native YTP/1 FrontDoor candidate. An opt-in listener adopts TCP through the shared channel owner and serves an operator-supplied immutable static site. Ordinary TLS 1.2/1.3 and HTTP/1.1/H2 cover remain separate from strict TLS 1.3/H2 promotion. Actual SNI/exporter binding, shared replay reservations and a connection-lifetime guard gate admission; rejected and saturated admission uses the configured cover. Promotion transfers the existing TLS and H2 state after output/timer drain and preserves cover handling and owner lifetime. Standalone YTP session and public-library backend wiring remain unfinished; this adds no production or stealth-advantage claim.
- TLS duplex and callback ownership. Independent underlying read/write state prevents duplicate reads during concurrent TLS output. The TLS dispatcher retains its state when an immediate callback releases the final channel handle.
-
H2 callback allocation failures. nghttp2 callbacks contain allocation failures and retain a nonallocating failure marker, including when diagnostic storage is unavailable. Header, DATA, close and client-priming regressions exercise one-shot and sustained allocation failure in the shared codec.
-
Explicit native TCP execution ownership. The experimental provider and accepted-channel owner share one caller-owned execution context. Reserved control dispatch replaces allocating strand cleanup, initiation stays on the declared context, and shutdown drains callbacks before the runner ends. Partial writes are initiated explicitly so a later allocation failure is handled by the provider. This changes the source-level provider factories; it does not introduce a native YTP endpoint or alter the public C ABI.
- YTP ingress components. The TLS client shares browser-profile configuration with transport v2 and still requires negotiated TLS 1.3/H2. Client H2 admission binds a fresh proof to the live TLS connection with its own domain. Accepted TCP sockets reuse the bounded client-channel implementation. The separate native FrontDoor composes ingress and cover; the YTP session runtime and ABI endpoint remain unfinished. These components establish no stealth advantage.
- YTP setup TLS certificates. Development kits generate P-256 certificates with SHA-256 signatures to match the browser TLS profile. Doctor separates TLS leaf and trust checks from the unchanged composite YTP identity rules.
- YTP TLS failure settlement. Closing the underlying channel during cancellation preserves the original failure. Terminal state transitions deliver pending completions without requiring another I/O event; late transport callbacks cannot replace the terminal status or feed more TLS data.
- Shared documentation sources. Topic
.docfiles underdocs/src/en_US/own text, web titles and catalog metadata. One sync renders Markdown, manuals, web pages, diagram assets, CLI help and completion. Locale paths prepare for translations; onlyen_USis active. See the authoring guide. - YTP/1 replacement foundation. Dependency-pure engine and protocol kernel, numeric config schema 1, the role-neutral C ABI v1 candidate with a transport-v2 backend that carries authenticated named byte streams, and the opt-in TLS 1.3, HTTP/2 duplex carrier, hybrid session-security, and Asio byte-channel provider candidates. None of it is a live YTP/1 endpoint.
- Own liboqs dependency edge. YUME’s ML-KEM key generation in
core/security/inner_crypto.cpplinks liboqs through YUME’s own CMake discovery (yume_liboqs,YUME_HAS_OQS) instead of inheriting it from the BaseFWX build.YUME_REQUIRE_OQSfails configuration when liboqs is missing. - libFuzzer harnesses for the untrusted parsers.
YUME_BUILD_FUZZERS(Clang-only, never installed) buildsyume_fuzz_h2_probe_decoder,yume_fuzz_client_config, andyume_fuzz_server_config. Each asserts that its parser rejects malformed input rather than throwing, because the H2 probe decoder runs on an Asio worker where an escaping exception does not reach the session.tests/fuzz/run_fuzzers.shseeds the corpora, enforces a per-target budget, and fails on any artifact, so CI, a build host and a local run are the same invocation.docs/DIAGNOSTICS.mdrecords it. - Fuzz instrumentation reaches parser libraries. The fuzz profile selects ASan+UBSan and coverage instrumentation for source-built dependencies as well as the harnesses. Instrumenting only the entry point left out-of-line parser bodies without coverage feedback or sanitizer checks. Other sanitizer selections require a separate build directory. Optimized sanitizer builds retain frame pointers. The ratchet no longer stores an unused copy of its constructor role; directional setup still uses the original parameter.
-
ThreadSanitizer and fuzzing are CI gates. A
thread-sanitizejob builds withYUME_SANITIZE=threadand runs the full suite, and afuzzjob builds the Clang harnesses and runs each briefly against the seeded corpus. Neither had ever run automatically, so a data race or a parser crash could reachmainwith nothing objecting.thread-sanitizeis the one lane not onubuntu-22.04: it ships nothing, and that image’s libtsan 11 reports a false double lock against mutexes in recycled heap storage. - Fuzzing reaches what a client sends before it authenticates.
yume_fuzz_h2_admissioncovers the admission path and the:authoritycheck,yume_fuzz_cover_sitethe cover site’s answer to any method and target, andyume_fuzz_websocketthe WebSocket framing of an admitted stream in either role, fed in pieces. Each checks a property beside crashes: an accepted path builds back to the same text, only GET and HEAD of the site’s own files succeed, and the codec never hands back more bytes than it was fed. CI’s fuzz job runs all seven harnesses.
Changed
-
Debian service lifecycle. Package configuration creates the locked daemon account before its state directories. Upgrades restart a running daemon and leave a stopped daemon stopped. Removal stops the daemon and loaded module instances. Each module instance has a separate dynamic user. Both packages require the exact patched OpenSSL runtime version used for their build, so stock cannot replace it while satisfying their dependencies.
-
Cover method refusals match the reference. The static cover answers methods other than GET and HEAD with Node’s fixed 405 headers and
Method Not Allowed\nbody. Authenticated H2 admission still precedes static-site handling, and GET/HEAD route and not-found behavior is unchanged. -
Modules run under systemd as users of their own. A
moduleadapter now names the module’s listeningsocketinstead of aprogramand itsarguments.yumedchecks at startup and with--validatethat only its own user and root can reach or replace the socket, then connects once per stream with the same identity header. The Debian package shipsyume-module@.socketandyume-module@.service, which run each module as a dynamic user without network, capabilities or access to the daemon’s files. The in-daemon launcher, its restart backoff and theyume-moduleentry are removed, so modules no longer run as the daemon’s user. -
H2-only cover. The public front door now requires ALPN
h2over TLS 1.2 or 1.3. HTTP/1.1-only offers fail negotiation. A connection without ALPN gets the Node reference’s fixed 239-byte 403 refusal and is closed without serving the site. The HTTP/1.1 site handler is retired. YTP promotion still requires TLS 1.3 and authenticated admission. - One switch for developer diagnostics.
-DYUME_DEV_DIAGNOSTICS=ONcompiles the timing helpers and the new key-rotation benchmark probes for the whole build, and nothing else does: the timing helpers no longer follow theDebugandRelWithDebInfobuild types. With the probes, a build records why each rotation started, the provider’s rekey steps and the records that waited, whenYUME_BENCH_PROBE_FILEnames a file../ezbuild.sh --devand CI’s sanitizer jobs turn the option on. - Native setup and doctor.
yume-setupandyume-doctorare C++ programs built besideyumeandyumed, with the same actions, options, outputs and file formats as the Python tools they replace. Keys, certificates and signatures come from the pinned OpenSSL through the readersyumeandyumeduse, and every key is read back by them before it is written, so a kit is made and checked with no Python oropensslcommand: stock Ubuntu 24.04’s OpenSSL 3.0 cannot make ML-DSA keys.yume-doctorparses with the configuration parser itself, so it reports the parser’s own wording, and the tuning presets come fromconfig/tuning_presets.jsonas the GUI’s do. Both have manuals and help generated from them, take--option=value, and print the help with a usage error, which exits with 2. The release archive carries the programs, and its quick start drops Python and the OpenSSL command. - The cover tools’ tests run on the pinned Node. They model the Node 24.18
cover server of the evidence profile, and ran on whatever Node was installed.
scripts/ensure-node.shdownloads that release and checks its SHA-256, test builds and CI provision it, and on any other Node the tests fail with the reason. The resolver test takes its missing name from its test helper, since a host without DNS answers a missing name with a temporary failure. - BaseFWX pin
471f5fd. The module libraries build on BaseFWX with the documentation tooling’s sequence type, restyled figures and lighter layered figures, and a B512 stream encoder that no longer trips a false GCC 13-Wstringop-overflowwarning in CI’s Release build. The pin also takes the six BaseFWX commits after8a8ab66. - Reload reaches armed accepts.
yumedkeeps up to four accepts armed on each listener, and each held the credentials loaded before a SIGHUP. A client added byadd-client, or a changed PSK, failed its first attempts until those accepts were used up, which cost the client its backoff. Every session now takes its credentials from the endpoint’s current factory when AUTH starts, so the reload reaches it on the first attempt. - Receive-window autotuning. A session returns consumed credit in one
update per half window instead of two records per received record. A stream
window grows from 256 KiB while the application drains it within two round
trips, up to two thirds of
limits.max_queued_bytes, and the connection window up to the whole budget. A peer’s grants are checked against the protocol’s 2^30 bound rather than the local window, so a client whose budget is smaller than its server’s no longer fails the session on the server’s first grant. - Fewer round trips on far servers.
yumednow sets TCP_NODELAY on the connections it accepts, asyumealready did on its own. Nagle had held small records such as AUTH, credit and REKEY_INIT behind unacknowledged data for a round trip. The endpoint’s rekey timer also rotates a used epoch without waiting for a send, so a request after an idle moment no longer waits a round trip on each side for REKEY_ACK. - Tuning presets.
yume-setup init --presetselectsstealth, the default,balanced,fastormax, which set the session budget, the epoch size and the credit returns on both sides of a kit.add-clientcopies the server’s settings andyume-doctornames the preset a configuration matches.config/tuning_presets.jsonlists the presets with a speed, security and stealth level each, for a later interface. - Client run settings.
yumeaccepts--connect,--socks-addressand--socks-port. Each replaces one configuration key for one run before the configuration is validated, so the value meets the file’s checks and an error names its key. None can change the wire image, authentication, encryption, admission, destination policy or leak protection, andyumedaccepts none.--validateprints every key a run setting can replace, with its value and where it came from. Theyumed(8)manual also printed the client’s SIGNALS section beside its own and now has only its own. - Packet channels return credit by whole packets. A packet cannot be split to spend the last of a stream’s credit, so a sender whose packets do not divide the receive window is left short of one packet while the receiver waits for half the window. It then got one return, about half a window, per round trip. On a packet channel the engine now returns at half of the whole packets that fit, by the largest packet the peer has sent. With circuit cells of 16311 and 16315 bytes, four streams through one circuit moved 52 Mbit/s at 40 ms per hop without this and 98 with it.
- Key rotation without a pause. A sender sent nothing protected between REKEY_INIT and REKEY_ACK, so every rotation cost a round trip, about 29 % of the time at 200 ms with 500 ms epochs, and a request that met a rotation took an extra round trip. A rotation now starts once an epoch has used half its payload, half its records or half its 500 ms send lifetime, and the sender keeps using the old epoch, within its limits, until the ACK arrives. The receiver keeps the old inbound root after accepting INIT and wipes it at the peer’s first record of the new epoch. Epoch limits are unchanged, a direction holds at most one old root, and an INIT in the epoch it replaces now ends the session.
- The carrier closes like the captured browser. A stopping
yumeused to drop its TLS connection. Its carrier now sends one HTTP/2 PING when it has read nothing for more than ten seconds, then a masked WebSocket CLOSE of the captured 18 bytes. The server echoes it and ends the stream, as the captured Node server did, and the client ends the connection when the echo arrives or after three seconds. The same PING rule, Chromium’s, applies to any write after such an idle. Neither side sends GOAWAY, which the capture does not show and Chrome sends only on errors. The server used to close the connection before its echo was written. The endpoint no longer cancels the client’s TCP I/O on close, so the CLOSE is not cut off. - Idle keys rotate only on request. The endpoint’s timer rotated every
key epoch that had carried records once it was 250 ms old, so each burst
of traffic ended with a rekey exchange of about 1.7 KB each way. The
matched stealth capture shows the browser session sends nothing there. The
timer now runs only with the new
limits.idle_epoch_rotation, which thefastandmaxpresets set. Without it the next send rotates the epoch and a request after more than half a second of quiet waits a round trip for the new key. The measured preset rates were taken with the timer on in every preset. The side that answers such a request now starts its own rotation beside its REKEY_ACK, so the answer does not wait another round trip. - More of Chrome’s page load. The client now sends its preface PING in a
TLS record of its own, as the captured Chrome session does, and fetches
/favicon.icoonce the WebSocket is open, with the headers and priority of the captured stream-9 request.scripts/generate_transport_profiles.pytakes that request from the fixture’s observations. - The server picks the cover server’s cipher. OpenSSL’s default made
yumedfollow the client’s order and answer Chrome with AES-128-GCM, where the captured Node server answers with AES-256-GCM. The ServerHello is not encrypted, and the first native stealth capture showed the difference.yumednow applies Node 24’s own order over the client’s: AES-256-GCM, ChaCha20-Poly1305 and AES-128-GCM under TLS 1.3, and Node’s cipher string under TLS 1.2. The registry’s newcover_server_tlsholds both, and the generator refuses a TLS 1.3 order that would not pick the captured ServerHello’s suite from the captured ClientHello. - Connection credit of dropped data. A DATA or PACKET record that started a key rotation waited behind it with its connection credit already taken. If its stream closed before REKEY_ACK arrived, the record was dropped without returning that credit, while the peer, which never saw the record, still counted it as granted. Each drop shrank the session’s usable window by a record, until the sender held less credit than its next record while the receiver waited for half its window, and every stream stalled. A soak of repeated parallel downloads slowed from about 190 to 126 Mbit/s and stalled after 16 to 18 minutes. The dropped record’s credit now comes back.
- H2 carrier window follows the session budget. The admitted carrier’s
receive window was a fixed 8 MiB. The carrier keeps each record’s H2 credit
until its sink completes and nghttp2 returns credit at half its window, so a
session with a budget above 4 MiB moved at most 4 MiB per round trip whatever
its budget. The window is now twice
limits.max_queued_bytes, at least 8 MiB and at most 128 MiB, and the carrier’s retained-byte bound grows with it. The default 4 MiB budget keeps 8 MiB. - More frequent credit returns, on request. A receive window returns
credit once half of it is consumed, so a window that can no longer grow
lets a far sender move about half of it per round trip.
limits.credit_returns_per_window, 2 by default, 4 or 8, returns credit every quarter or eighth of such a window instead, for about three quarters or seven eighths of it per round trip and that many more upstream credit records. - Cancelled writes on an aborted stream. When the peer aborted a stream with queued writes, the engine released the stream’s queued bytes at once and then failed the writes one at a time. A write cancelled in between, for example by a handler reacting to the first failure, was released a second time. The session’s queued-byte count wrapped below zero, and the next record found the queue full and ended the session or refused its writes. Fast far-path downloads and a soak of repeated parallel downloads hit it.
- Negotiated epoch size.
limits.max_epoch_bytes, a power of two from 1 MiB, the default, through 64 MiB, sets how much protected payload one directional key epoch may carry. The capability manifest, now schema 2, signs it in the AUTH flight, and both directions use the smaller of the two sides’ values, so neither side can make the other rotate less often than it allows. Rotation waits one round trip for REKEY_ACK, so on a far path a session carries about one epoch per round trip. A larger epoch trades key granularity for speed there. The record limit stays at one record per 2 KiB of the epoch and the 500 ms age limit is unchanged. Schema-1 manifests are rejected and the YTP/1 vectors changed with it. - Session queue budget.
limits.max_queued_bytesaccepts 64 KiB to 64 MiB. The parser andyume-doctorhad accepted up to 64 MiB while the session engine refused more than 16 MiB, its largest single buffer, so a larger budget passed--validateand doctor and then stoppedyumedandyumeat start. The engine now has its own 64 MiB bound for queued and held bytes, the parser owns the configured bound, and the runtime checks at compile time that the engine accepts it. A larger budget buys far-path speed with memory, since a session’s receive windows grow up to it. - Native failure handling and memory bounds. Expired ABI opens no longer enter the session while waiting for dispatch. Startup and stream operations settle their callbacks and state when allocation fails. Buffer growth stays within its bound, and write queues charge retained capacity. The public listener retries temporary descriptor or memory exhaustion. Share-import errors omit decrypted JSON, file-name checks need no allocation, and relay rekey and history cleanup keep secret storage under a wipe guard. History reads reject duplicate or nested fields without building a JSON tree.
- Unqualified cover profiles are refused. A schema-1 document that names a
cover profile other than the one the build qualifies now fails to load, in
yume,yumedand the C ABI alike. Before, the programs accepted such a document and ran with the built-in profile anyway, whileyume-doctoralready refused it. - Session closure and reconnect. The native client reconnects from an endpoint notification instead of checking session state every second. Teardown settles pending engine callbacks before the endpoint frees the session slot and reports closure on its context. Failed starts, and sessions that end sooner than the longest backoff after authenticating, keep exponential backoff. A path that drops each connection right after AUTH can no longer drive a tight reconnect loop that uses up the server’s shared admission replay entries.
- Empty UDP datagrams. The direct-route bridge drops an empty datagram from a destination instead of closing the flow, since a YTP packet cannot be empty.
- GCC 11 allocation hooks. The AUTH publication and admission replay regressions now use the shared out-of-line test hooks as well. GCC 11 CI had reported the AUTH publication test’s inlined malloc/free pair as a mismatched new/delete call. Both keep their injection points and cases.
- DNS route refusal. Asynchronous OPEN permission failures now send the existing unauthorized CLOSE code, so SOCKS5 returns 0x02 instead of host unreachable. A Linux process test runs the production daemon with controlled resolver answers, checks mixed allowed and denied answers in both orders, verifies that refused routes open no sockets, and carries traffic afterward.
- Build and tooling portability. The direct-route archive applies the shared ABI’s non-LTO policy only when the native backend is composed, preserving Release test links against LTO-built engine libraries in other configurations. Documentation pipeline fixtures use cleanup compatible with Python 3.10. Test-only allocation-failure hooks stay out of line. Optimized GCC 11 builds had inlined them and reported their malloc/free pair as a mismatched new/delete call. The server teardown regression now uses those shared hooks too, retaining its caller-thread failure scope and all teardown checks.
- Replacement work and embedding boundaries. Contributor and automation guidance require source-backed decisions, review of the proposed solution, capability-based retirement, and consumer-independent interfaces. Build help no longer makes historical transport-v2 parity a replacement prerequisite. The source map identifies admission’s v2 token coupling and separate replay check so reuse does not silently import another protocol’s contract.
- Neutral example configuration. The client example uses the reserved
server.example.invalidplaceholder. Server certificate/key examples use/etc/yume/server.crtand/etc/yume/server.key. - Removed the inactive ABI log callback. The experimental runtime options no longer accept a logging callback that was stored but never called. Its record type and level constants are removed with it. Rebuild candidate consumers against the current header; event callbacks remain supported. Exported functions, protocol domains and independent versions are unchanged.
-
Codec permission documentation. Per-key authorization uses
allow_codecs; the oldallow_monero_rpcfield is rejected by the closed metadata parser. Server configuration and CLI codec controls remain separate. -
Transport-v2 configuration parity. Server saves preserve shaping inputs, metadata/capture paths resolve beside the configuration, and nonempty cover roots/backends activate HTTP cover validation in both readers. Client saves replace cleared/default values and stale split codec endpoints. Overridden server security booleans, client codec endpoints and the Android pin alias remain type-checked. Nonzero client shaping fails facade validation, and serialization failures preserve the destination. Parsing remains separate from runtime acceptance; schema 1 and the Android alias retirement stay separate.
- TransportCore shutdown transfers stream callbacks and drains queued writes without allocating. Session close releases the socket if its close deadline cannot be armed, cancels TLS and HTTP idle deadlines, and releases codec sockets, receive credit and response reservations. Closed sessions refuse late idle rescheduling. TCP and UDP streams own their pacing timers so close releases delayed writes without waiting for the pacing deadline. Regressions cover live reads/waits, codec deadline setup, terminal close and subsequent executor destruction under allocation failure. Broader async ownership remains open.
- AUTH frame declarations are checked at the header against the 64 KiB record budget, allowing at most 256 extra padding bytes. The server rejects other frame types before unauthenticated payload reads. Frame encoding and decoding also check lengths before narrowing or adding a peer-controlled length.
- Private PEM loading and partial share JSON construction register cleanup before later allocations. Composite PEM export allocates the returned vector once, signing contexts use RAII, and mutable-buffer erasure covers retained capacity. Failure regressions observe storage before it is freed, including OpenSSL memory-BIO growth failure. Library scratch erasure remains unproven.
- Transport-v2 admission refuses saturation without evicting live replay
entries and rolls back partial insertion on allocation failure. AUTH
rejects empty admin fields, requires exact ML-KEM-1024 widths and enforces
the server-info cap on both encoding and parsing. Identity admission no
longer leaks a slot on allocation failure, and optional
last_seenwrites cannot turn a published AUTH decision into a rejection. Valid wire bytes and cryptographic domains are unchanged. - Control registration, lifecycle IP checks, and operator-proof URL parsing
avoid Boost’s allocating
noexceptstring-view overload, which could abort the process on allocation failure. Control and directory parsers share field predicates while retaining their limits, accepted values, and diagnostic order. - Share-bundle construction wipes partial secret copies. Copy and move constructors delegate to a completed empty object before assignment, so allocation failure runs secret cleanup before member destruction. The old constructor failure handlers accessed already-destroyed strings. A focused regression checks secret bytes at deallocation on success and at each allocation failure, including a later public-field copy.
- The control reference describes the current C++/IPC interface. Removed claims about deleted generic C request calls, cached sizing retries, and queued-handler cancellation. Local socket timeouts do not provide an overall operation deadline or rollback.
- Filter archives and reloads preserve publication boundaries. Native liblzma/libarchive processing replaces shell tar execution, with compressed, decoded, payload, member, filesystem-node, decoder-memory and elapsed-work limits. Failed loads retain previous rules and files; successful reloads replace an immutable snapshot instead of appending into active rule vectors.
- File and cover boundaries fail closed. POSIX bounded/private readers reject embedded NUL paths and open nonblocking before rejecting FIFOs. Static-root reads pin each directory descriptor, refuse symlink components, and take file bytes and modification seconds from the same handle. HTTP byte-range integers reject overflow. Unsupported platforms refuse confined static reads and archive extraction; Linux filter archives use bounded native decoding and confined publication.
- Captured HTTP responses preserve their bodies. Header-only LF normalization replaces whole-response rewriting. A bounded HTTP parser refuses invalid or incomplete framing, and H2 conversion decodes chunked bodies before DATA emission.
- CLI and embedded validation agree on explicit null. Known transport-v2 fields reject null through the shared closed-key validator. Daemon identity loading uses the canonical key-management parser and bounded reader while retaining the five-resource startup/reload transaction.
- Embedded operator proof is refused explicitly. The server runtime
reports an invalid argument for
anonym=true; generation, refresh, and logging policy remain owned byyumed. GUI controls reflect that boundary, and Bash completion treats the operator token argument as a file path. - Standalone security regressions now execute their assertions and link the
production helpers they exercise. The early-declared operator-token and
curl-transport tests explicitly undefine
NDEBUGin optimized builds, and the static-site regression linksyume_corefor the shared bounded reader. The warnings-as-errors qualification build caught both build-graph gaps. - Operator proof tokens are file-only. The inline
anonym_tokenconfig key and--operator-proof-tokenargv surface are refused. External proof tokens now come fromanonym_token_file/--operator-proof-token-file, loaded through an owner-only, no-follow, regular-file check with a 4096-byte bound. - BaseFWX dependency evidence is current and CI-enforced. The YUME pin now
names remotely reachable BaseFWX
8a8ab66, whose current workflows include the retired-media repair missing from the previous pin. The deterministic source-dependency SBOM is regenerated from that manifest, and CI now runs its drift checker before building. - Authorization metadata uses a closed schema. Permissions belong under
permissions, codec access usesallow_codecs, and scheduling usesweight. Unknown fields and alternate spellings are rejected. - Configuration and key stores accept their current writer formats. Client
configuration uses
threads,udp, andapp_codec; Android’stls_pin_sha256remains accepted until its config writer changes. Relay keys require the generated JSON record, and GUI material metadata requires schema 1 with random IDs. The unusedinner_dual,inner_required, and--inner-requiredserver inputs are removed. Inner encryption remains mandatory. The carrier diagnostic uses one--client-profileoption. - Client configuration keys are a closed set. The
yumeCLI parser and the facade parser used by the GUI and the C ABI share one key table insrc/config/client_document_keys.hpp. An unknown key is an error, a retired key names its replacement, and integral fields reject values they cannot represent instead of wrapping. Both parsers previously ignored unknown keys, so a misspelledtls_pinsilently disabled pinning. - Obfuscation jitter and worker threads are bounded.
obfs_jitter_msandthreadswere only checked for representability, so a config could ask for roughly 49 days of delay on every outbound frame or a thread count that exhausts the process limit at start. Both parsers now enforcepolicy::kMaxObfsJitterMsandpolicy::kMaxIoThreads. - Inline
obfs_secretis refused at parse time. The client and server configuration fields that carried an inline admission secret were rejected at start since transport v2; the field and its plumbing are gone and every parser now rejects the key with the file-based replacement named. - Server teardown contains cancellation failures before joining.
RuntimeController::stop()records the first failed stage without allocating. Successful cancellation drains shutdown handlers normally; a cancellation failure stops the I/O context before joining so a pending accept cannot hang the caller. Later status reads format the diagnostic. Local IPC socket-path cleanup also avoids allocation during destruction. IPC shutdown retains the listening descriptor until its serving thread joins, removing an accept/stop data race and premature descriptor reuse. This closes the teardown abort and join-hang paths without claiming graceful session cleanup after failed cancellation. - A cancelled endpoint start reaches
STOPPED. The C ABI no longer strands a handle inSTARTINGwhen a runtime stop races a start; the endpoint moves toSTOPPEDand emits aCANCELLEDevent. - Registration shape and H2 regression test renamed. The only control
registration format the client sends was called “legacy”; it is now
ControlRegistration, and the retained transport-v2 H2 regression test isyume_transport_v2_h2_regression_test. - Share-file KDF label is pinned. New
.yssshare files always request Argon2id explicitly, so theBASEFWX_USER_KDFenvironment variable can no longer downgrade them to PBKDF2 or fail encoding with an unsupported label. Existing files are read from their serialized label as before. - Manual pages and front-door documents name the product version. The
.THheaders and the README, documentation map, and status page state0.3.0-dev1and describe0.2.0-dev6only as the transport-v2 wire.tests/test_project_metadata.pynow fails when they drift. -
Third-party attribution corrected. The GUI credits page names OpenSSL as the ML-DSA-87 provider and the downstream OpenSSL patch overlay, and no longer calls BaseFWX the core crypto engine or claims ML-DSA comes from liboqs.
- No built-in cover page, and a cover source is required.
yumedcontained a compiled redirect stub that probes reaching the decoy path could identify. Startup now refuses unless one ofupstream_response_dir,upstream_response,real_root, orreal_index_pathis set alongsidereal_backendforreal_httpoperation. Ordinary HTTP/1.1 and recognized HTTP/2 GET/HEAD requests already reach the same backend. Separate partial/malformed and admission-disabled probe paths can use the decoy. The cover reference records their remaining differences. - Cover material has explicit file and cache bounds. A configured index, static asset, or captured response is capped at 8 MiB and read through the regular-file, no-follow bounded reader. Rotated captures are limited to 256 matching files, 4,096 directory entries, and 64 MiB retained in aggregate, so a bad operator path or periodic reload cannot grow the daemon without a ceiling.
- Packet-mode DNS has no default. The resolver was silently
1.1.1.1when none was configured, making a third party the observer of every hostname a tunnelled client resolved.--packet-egress tunnow refuses to start until an IPv4 resolver is named. - Server configuration keys are a closed set. The
yumedCLI loader and the facade parser used by the GUI share one key table insrc/config/server_document_keys.hpp, over the same predicate as the client set. An unknown key is an error instead of “leave the default”, and several of those defaults are security decisions. - Server
obfs_jitter_msandthreadsare bounded. The server loader accepted any representable value, so a config could delay every outbound batch by up to roughly 49 days or request an unbounded worker count. Both now usepolicy::kMaxObfsJitterMsandpolicy::kMaxIoThreads, matching the client. - Configuration and start failures are typed. A failure attributable to
one member now carries an RFC 6901 JSON pointer in both dialects, malformed
input is
YUME_STATUS_PARSE_ERRORand a document that parses but does not validate isYUME_STATUS_INVALID_ARGUMENT, andyume_endpoint_startreports the runtime’s typed outcome instead of collapsing every failure toYUME_STATUS_IO_ERROR. A refused bind is now distinguishable from a transient failure without parsing the diagnostic text. - The release lane is
linux-desktop-0.3.0. It accepted onlyv0.2.0tags while the product was0.3.0-dev1, so it could not ship the tree. - The plain
attachcontrol command is named truthfully. Its refusal called it “legacy” whileadmin.attachis a different operation, not a newer generation of the same one. - HPACK integers no longer wrap on the unauthenticated carrier probe.
decode_hpack_varintaccumulated shifts and additions without an overflow check, so a peer could declare a string length near2^64that madedecode_hpack_string’s bounds check wrap and pass, reachingstd::string::assignwith that length. The resulting exception escapedSession::on_h2_probe_readinto the Asio worker, which aborts the process (see the worker-containment entry below). A wrapped value could also resolve to a static-table index the wire never carried. The decoder now rejects both operands before evaluating them, compares remaining length by subtraction, and bounds the header count and aggregate bytes one block may retain. The probe read contains a decoder throw and serves the masquerade response. Reachable before authentication. - A throwing completion handler no longer aborts the process. The server
and client worker threads ran
io_context::run()as the thread body. An exception that escapes a completion handler propagates out ofrun(), and an exception that escapes a thread entry point callsstd::terminate, so any peer able to reach a throwing handler could abort the daemon. Both workers now go throughruntime::run_worker, which contains the exception, re-entersrun()for queued work, and records the occurrence in a rate-limited warning. The worker does not identify or close the offending session; another pending operation can still retain it. Containment is a backstop: a handler at a trust boundary must still reject bad input rather than throw. - One frame-payload bound, not three.
server::detail::kMaxFrameSizewas a third independent spelling of the same 16 MiB limit on the same header field, alongside the outbound constant and a bare literal in the prefetch classifier. All three now resolve toprotocol::kMaxFramePayloadBytes. - Control frames are bounded before allocation.
protocol::read_frameresized the payload to a peer-supplied 32-bit length, so a hostile server could ask a client for 4 GiB before anything parsed. The cap now belongs tocore/protocol, which defines the header, and is shared by the synchronous reader, the asynchronous transport path, and the prefetch classifier that previously repeated the number by hand. - The optional TLS fingerprint diagnostic is bounded. Its HTTP/1.1 fetch
appended to EOF and its HTTP/2 fetch allocated each peer-declared frame,
accumulated DATA, and waited for END_STREAM, all without a limit or a
deadline, so a redirected endpoint could hang or exhaust the client. The
fetch moved to
core/stealth/tls_verify_fetch.hppwith total, per-frame, and frame-count caps plus a wall-clock deadline, and the socket carries a receive timeout. It runs only underopenssl-diagnosticwithtls_fingerprint_verify. - MaxMind record parsing is valid C++.
MmdbValueheldstd::vector<std::pair<std::string, MmdbValue>>, andstd::pairdoes not admit an incomplete member type the waystd::vectoradmits an incomplete element type. GCC accepted it; Clang rejected it, which blocked every Clang-based analysis of the server tree. The key now lives on the value and the vector holds the element type directly. - The vendored nlohmann tree is complete, so CI builds again.
third_party/nlohmann_json/nlohmann/shippedjson.hppbut notjson_fwd.hpp. That directory is first on the include path, soconfig/v1/config.hpp’s#include <nlohmann/json_fwd.hpp>fell through to whatever the build host had installed whilejson.hppcame from the bundle. A host whose distribution nlohmann matched the bundled 3.11.3 built fine, which is why every developer and build-host lane was green while every CI lane failed inside the vendored header: Ubuntu 22.04 ships 3.10.5, which declaresbasic_jsonandjson_pointerdifferently. Upstream publishesjson.hppandjson_fwd.hppas two standalone release assets and only the first was vendored, so the second is now vendored beside it, byte-identical to the upstream asset. Take the release asset rather than the multi-header distribution’s file of the same name, which pullsnlohmann/detail/abi_macros.hppfrom a tree this directory does not carry.third_party/nlohmann_json/PROVENANCE.mdrecords both URLs and hashes, and a metadata test pins them and enforces that the vendored tree satisfies every<nlohmann/...>include in the sources and in the vendored headers themselves. The dependency and its version are unchanged, so the notices and SBOM are too. - The tray builds without a tray.
gui/platform/tray.cppdefinedwrite_icon_for_statusandremove_icon_filesunconditionally, but only the Linux AppIndicator path uses them. Any build without that path, which includes every host missinglibayatana-appindicator, left them orphaned and failed under-Werror=unused-function. They are now guarded byYUME_GUI_TRAY_LINUX, matching where they are used. The Windows, macOS and no-tray paths never referenced them. - The GUI icon refresh acknowledges
system()properly.gui/platform/app_icon.cppdeliberately ignores the status of four best-effort desktop-cache refreshes and said so with a(void)cast, which GCC does not accept as acknowledgement ofwarn_unused_resultthe way Clang does. It went unseen locally because Ubuntu enables_FORTIFY_SOURCEby default at-O2and Debian does not, and fortify is what markssystem()as__wur. The status is now assigned and discarded through one helper. The behaviour is unchanged: these are backgrounded commands whose status describes the shell, and a missing tool is the expected case. -Wstringop-overreadis demoted to a warning on GCC 11. GCC 11 propagates an exact string length proved at the call site into libstdc++’sstd::stringmove assignment, then reports an overread against the short-string branch a string of that length can never take. It fired onclient/transfer/share_file.cpp, where a 64-character secret is validated and then moved. The diagnostic’s location is inside the system header, so an in-source pragma does not suppress it, and GCC 12 and later do not emit it. Demoted rather than disabled, and only for GCC 11, so the release toolchain still prints it if it ever fires on something real. This had been invisible because every CI build lane failed earlier, in the vendored JSON header.- The CI OpenSSL guard counts lanes instead of assuming three.
release_preflight.pyrequired exactly three occurrences of the dependency-setup block and of-DYUME_STATIC_OPENSSL=ON. The invariant is that every lane which configures CMake carries both, so the literal count went stale as soon as a lane was added and would also have passed if one lane were deleted and another added in its place. It now derives the lane count and names the mismatch. - A Clang build of the whole tree links again.
yume_anonym_curl_transport_testusedyume_apply_warningswhere every other non-ABI target usesyume_apply_perf_opts, so it linked the LTO-builtyume_corewithout-flto. GCC hides that by using the linker plugin automatically. Clang does not, and GNUldreported “file format not recognized” on a bitcode archive. The ABI targets remain the deliberate exception, because their inputs are built without LTO. yume-packet-quickexamples are valid input.--listenrequires a numeric IPv4 address, which the helper renders into nftables and UFW rules, but the documentation, the parser’s own error message, and the test fixtures all used a hostname. The test was also never registered, so two failing cases passed CI; it now runs in CTest.
Removed
tools/yume_setup.pyandtools/yume_doctor.py. The native programs replace them, and the tests that specified them run against the programs.- The transport-v2 benchmark sandbox.
yume_bench_isolation.py,yume_bench_provenance.pyandyume_bench_exec_guard.pyhad no user after the v2 benchmark went. The capture scripts’ tests remain. - Code no shipped program reached. The reachability check found
Tunnel::status_age_msinyume-gui,ClientCircuit::hops,H2Carrier::capture_observer_active,H2Carrier::RejectCarrier(the front door answers a failed CONNECT from the cover, so a separate refusal would only mark the carrier), the four-argumentSessionEngine::async_openthat only tests called, and untested move assignments ofKitandLayerCipher, which hold secrets. - The stream CLI help writer. It served the transport-v2 CLI layouts, with interpolated runtime constants from a header that no longer exists. Every layout generates constant help and completion strings.
- Transport v2. Its wire, AUTH v2, security modes, client and daemon, GUI
facade and control API, federation, host controller, the Go Chrome TLS
helper, the v2 benchmark and self-test drivers, the multi-target and Windows
build scripts, and their documents and manuals are gone. Commit
9070b0ais the last that contains all of it. What YUME still needed moved first: the relay channel, file transfer, codecs, the share container, egress lists, per-client speed limits, forwards, the SOCKS5 upstream and the loopback fetch are native, andconfig/yume.jsonandconfig/yumed.jsonare now the schema-1 examples. - The transport-v2 desktop GUI. Its source in
src/guiwas built on the removed v2 facade and could not build since. Every page depended on that facade, so a native GUI is a new design rather than an update. The source and its two unregistered GUI test scripts are readable in commits up tocff6905, andYUME_BUILD_GUIis gone. - Inline
real_secretand the--real-secretflag. The cover-backend secret could be passed in argv, where the process table exposes it, or written into the configuration file. Both server loaders now refuse the key and namereal_secret_file, which is read at start or created with fresh random contents. - Five unused server configuration aliases.
allow_monero_rpc,codec_allow,deny_default,exposure_check, and the combinedmonero_rpc_backendspelling had no writer and named no consumer. The equivalent CLI flags remain, because a flag name and a config key are separate surfaces. docs/release/RELEASE-NOTES-1.1.md. It described a stable 1.1 release that never happened and was installed into every package. Git history keeps it.- Unused dependency-manifest parsing in CMake. The BaseFWX repository and
revision fields were parsed and never used;
ezbuild.shremains the enforcement point for the pin.
Fixed
-
Retained relay storage and file receives. History uses full SHA-256 names for every instance and peer, keeping literal IDs separate from digest names. Trust-commit retries repeat the directory flush before reporting success. File-receive failures clean up before allocating diagnostics, and unrepresentable receive deadlines are rejected before state is created.
-
Capture cleanup and evidence failures. Circuit and nDPI capture tools release earlier children and listeners when later startup or cleanup fails. Missed capture deadlines remain failures after forced cleanup. nDPI reports require complete packet accounting, and the TLS evidence relay keeps its forwarding writes within the idle deadline.
-
Setup rollback under allocation failure. Cluster edits and client enrollment remove exclusively created files when later bookkeeping fails. Files referenced by a published replacement remain available if a later directory flush fails.
-
Credential failure reporting. A refusal keeps its typed status when copying its diagnostic runs out of memory, instead of terminating startup.
-
Test-fixture failures. Module socket construction removes its temporary directory on failure. An unexpected circuit event-loop exception fails the test, while synchronous task exceptions still reach their callers.
-
Build-directory ownership. Builds and cleans lock the same canonical directory, including symlink aliases, and refuse to proceed without a usable shared lock directory.
-
ABI channel publication across restart. A stream or packet open/accept that finishes after endpoint stop is cancelled before publishing its handle, even when a new start has reused the same backend.
-
Sealed-kit input and cleanup. Directory reads stay beneath a pinned root, enforce the file-count limit while enumerating children, and read file bytes and executable permission from the same descriptor. Temporary file plaintext and generated code buffers are wiped on failure as well as success.
-
Circuit completion and receive limits. Circuit streams retain callback delivery after their circuit owner is released and report allocation failures through their status channels. Pool handoffs keep one completion owner through conversion and provider initiation. New streams and growing windows share the circuit’s aggregate receive budget, and the pool selects a circuit with room for the initial grant.
-
Shorter-route consent. Pending proposals are discarded when their session or verified routes view changes, fails or expires. Stale proposal IDs cannot authorize a shorter route. Previously accepted hop policies keep their documented lifetime until the configured route length works again.
-
Engine control records under a full outbound budget. A session no longer fails when data or pending writes fill its outbound byte budget and the engine must publish its own acceptance credit, credit return, FIN, terminal CLOSE or settlement. Ordinary records leave room for one such record, and the rest wait as bounded per-stream and per-connection obligations sent in order as earlier records complete. A window keeps at most
credit_returns_per_windowgrants waiting to be sent. Connection credit no longer competes with PONG formax_control_messages, closing streams stay counted until their owed records are sent, and a local OPEN that does not fit fails alone withResourceExhausted. -
Progress beside an unread stream. The receiver returns already-consumed connection credit when the peer has less than one maximum frame left, including when the last arriving record belongs to the unread stream. Previously the held stream could leave less usable credit than the normal return threshold, stopping useful streams indefinitely. Held records keep their credit, and queue budgets, receive windows and preset values retain their bounds.
-
Installed SDK discovery paths. The pkg-config gate now reuses the authenticated stream, route and packet probes already used by the CMake gate. Both consumer paths preserve selected language and sanitizer options, and C++ runtime link options stay out of C commands. Each fresh prefix retains command results and nested CMake inventory/JUnit evidence.
-
ABI authentication totals survive session end and stop. Restores the intended per-start lifetime of
sessions, including stopped backend replacement. An acceptedSTARTINGtransition begins a new counter generation at zero, including when that start fails, is cancelled or expires; calls rejected before that transition preserve the old generation. Device reconnections accumulate in the same generation and retain the matchingfailed_attemptsthrough stop. Sticky failure diagnostics and handle-wide byte totals keep their separate lifetimes. -
ABI session-end status. A client without a device publishes
ENDEDwith the ended session’s typed reason after recording its lifecycle line. A status read during notification delivery could previously pairENDEDwith a failure retained from an earlier start. - Release publication requires trusted signatures on the annotated tag, source commit, artifacts, aggregate checksums and final manifest. The manifest is generated after artifact signatures exist and classifies aggregate checksum signatures separately. Uploads refuse existing asset names and never replace them. The obsolete independent-review approval input is removed.
- Circuit failure reports survive queued delivery. A hop ends its backward write direction after its failure report instead of immediately aborting the stream and discarding unread cells at the previous hop. Crossed forward cells drain within the existing stalled-circuit timeout, preserving both failure attribution and bounded credit release.
- Server accept failures survive memory exhaustion. A served endpoint’s terminal accept failure reaches the main context through a reserved task, so an allocation failure cannot lose the typed notification and leave the daemon running with a closed endpoint. The task retains the runtime state through its callback.
- Kit queries respect callback restrictions. The C ABI’s sealed-kit
queries enforce the existing callback rule: file count returns zero,
and file access returns
YUME_STATUS_INVALID_STATEwithout changing the caller’s output. Reads outside callbacks are unchanged. -
Cover fixture startup cleanup. A reference server that misses its startup deadline is killed and reaped before the test reports failure, so its process and piped output cannot keep the test runner alive.
- ThreadSanitizer reported races inside OpenSSL. Concurrent handshakes
on one library context read and extended OpenSSL’s name map, which
publishes entries through atomics that an uninstrumented OpenSSL hides
from TSan, so CI’s
thread-sanitizejob failed now and then on a race betweenossl_ht_getandossl_ht_insert. The TSan lanes now build the pinned, patched OpenSSL with ThreadSanitizer, selected byYUME_OPENSSL_SANITIZE=threadinscripts/ensure-openssl.sh, into a separate static-only cache prefix. Nothing is suppressed, and TSan now checks YUME’s own OpenSSL use as well. yume-doctorrefused a circuits client on a cluster member. It checked each capability against the configured services only, whileyumedalso grantsyume.circuiton a member of a cluster, so a server with a client fromadd-client --circuitsfailed the doctor. It now follows the daemon’s rule and still refuses the grant on a server outside a cluster.- The release workflow’s tests did not link. Built as
release.ymlbuilds them, with LTO on Ubuntu 24.04’s GCC 13, the TUN network test lost itssd_busstand-ins, which nothing in the test calls, before the linker’s--wrappointed the library’s calls at them. They are now kept as used. CI’s jobs build the test without LTO and never showed it. cluster-signcould publish a list the nodes refuse. It now checks the signed list and routes view with the nodes’ own reader before replacing any copy, so a record edited into a host or name the reader refuses leaves every node and the serial unchanged.- The GUI could crash when a page came back into view. The kit list, the local listeners and the circuit routes are Repeater rows, rebuilt whenever their model changes, also while their page is hidden. Qt 6.4’s layouts can keep a destroyed row and dereference it when the page is shown again (QTBUG-111792, fixed in Qt 6.6). The rows now sit in plain Columns that the layouts see as one item. CI’s GUI job crashed once, and Qt 6.4.2 crashed in 1 of 43 runs, and at once under ASan.
- The Overview’s Local access card collapsed on Qt 6.4. On Ubuntu
24.04’s Qt 6.4.2 the card showed only its heading, and the SOCKS5 row drew
without its address, when the listeners arrived while the card’s row was
still hidden. The hidden row kept each card at its preferred width of one
pixel, Qt 6.4 did not lay the list out again once the row was shown, and
a Column skips rows of width zero. Every Repeater list now goes through
one
RowListthat takes its width from its parent, which also covers the circuit routes, the kit list and the Credits lists. - IPv4 literals with leading zeros. The configuration accepted
010.0.0.1as an IP literal. The TCP provider could not parse it as a number and passed it to the system resolver, which reads010as octal, so the client dialled 8.0.0.1. Configuration and cluster lists now refuse a leading zero, as the C ABI already did, and both use one check. - A stale OpenSSL error ended a busy TLS session. OpenSSL reports a TLS
call’s outcome through the calling thread’s error queue, and the TLS channel
did not empty that queue first. Any OpenSSL failure left on an I/O thread,
such as a reload refused for a bad list signature or a circuit handshake
with a malformed key, turned the next session’s ordinary wait for data
into a fatal error, and a cluster link carrying circuits closed. The
channel now empties the queue before each TLS call.
yume_tls13_secure_channel_testleaves an error before every step, and the four-daemon circuit test refuses a reload under load. - A circuit build outlived its pool. Closing the client’s circuit pool or giving it a new session left a circuit that was still being built running until its 30-second build bound, and an open cancelled while it waited for that build settled only when the build ended. Both now end at once.
- Callbacks that ran twice under libc++. Several components moved a
callback out of a member and later tested or settled that member again,
relying on the moved-from
std::functionbeing empty. libstdc++ empties it, but libc++, which Android links, leaves a small target in place. A closing TLS channel then delivered its spent handshake completion without end, so stopping an Android endpoint never returned. Records deferred across a rekey completed a second time with Cancelled, which ended a busy stream mid-transfer. A session’s start and a stream’s OPEN, the HTTP/2 carrier’s creation, sends and receives under allocation failure, a route bridge’s acceptance and a circuit stream’s open could each complete twice. Each now empties the member explicitly. The whole suite was run against clang and libc++, and against libc++ headers patched to abort on any use of a moved-from callback. Four tests that assumed libstdc++’s allocation sizes or moved-from state were corrected, and the tests’ allocation hook no longer fails the storage of the exception it throws. - yume-gui took a starting client for an ended one. Right after starting
yume, the window read an empty command line for a process inside execve, as a wrapper that runsyumeis for a moment, reported “yume stopped while starting” and failed the connect. A process with an empty command line now counts as running unless it has ended or is a kernel thread.
[Unreleased 0.2.0-dev6]
Product and transport versions were rebaselined to reflect YUME’s experimental
maturity. This transport is a hard break from 0.2.0-dev5, and dev6 has
one evidence-backed chrome151-node24-v1 identity. No dev5 compatibility or
downgrade mode exists.
Added
-
Generated transport-profile registry. Capture manifests and H2 evidence now generate immutable C++ and Go registries consumed through browser-neutral API. Bounded schema/path/uniqueness checks and negative metadata tests reject stale output or unsafe fixture references. The authenticated dev6 ID and all wire/crypto domains remain unchanged.
-
Single-source dependency manifest. BaseFWX repository, exact revision, and minimum compatible version now live in
config/dependencies.jsonand are consumed by CMake, local build scripts, CI, CodeQL, and release preflight. The dependency remains reproducibly pinned; duplicated workflow literals and the unsafe CI fallback to a floating branch are removed. -
Five-run Chrome 151 / Node 24 evidence. Five fresh normal-Chrome profiles cover TLS outcome, ordered H2 settings and headers, page/assets, RFC 8441, bidirectional bulk flow control, 42-second idle behavior, and graceful close. A committed validator separates stable identity fields from measured timing and flow-control distributions.
-
Authenticated transport profile. Admission HMAC input, schema-3 AUTH challenge/response/confirmation records, the signed TLS-exporter transcript, initial root, and per-frame AES-GCM AAD now all bind the exact
chrome151-node24-v1identifier. Stale identifiers, schema-2/dev5 records, missing fields, and configured profile mismatches fail closed without a fallback. -
Fail-closed Linux 0.2.0 release lane. The
linux-desktop-0.2.0profile builds only glibc Linux x86-64, statically embeds the checksum-pinned patched OpenSSL 3.5.7, and retains the exact-Go-1.26.5 Chrome helper as an optional comparison backend. It emitsyume-amd64-linux.tar.xzwith the client/helper, licensing, quick-start, and machine manifest, plus a separateyumed-amd64-linux. The preflight rejects other platforms and variants, incomplete bundles, version/tag drift, unequal clean helper rebuilds, or relaxed PQ, Argon2, and LZMA requirements. Publishing is disabled by default and requires explicit independent-review and RC-gate acknowledgements.
Changed
-
Experimental version rebaseline. Product, package, release-profile, and authenticated transport identity now use
0.2.0-dev6; AUTH v2, relay-v2, ABI v1, helper IPC v1, and crypto domain schemas remain independently versioned. The obsoletev1.0tag was withdrawn because it named an unstable, incorrect project state. -
Go helper dependency floor. The opt-in uTLS helper now requires Go 1.25 module semantics while retaining the exact Go 1.26.5 build toolchain. Its indirect
golang.org/x/cryptodependency moves from 0.36.0 to 0.52.0 andgolang.org/x/sysfrom 0.31.0 to 0.45.0, leaving GitHub’s reported vulnerable module ranges while preserving the pinned uTLS and wire protocol versions. -
Native OpenSSL ClientHello emitter and helper retention. The default
openssl-chrome151backend opts into an additive, default-off patch on the exact OpenSSL 3.5.7 source. Its emitted-byte gate closes all six pinned ClientHello structure rows across 12 SSL objects sharing one context, including per-connection GREASE, edge placement, extension shuffling,[0]point formats, both real key shares, and Brotli-only certificate compression. The pinned Go/uTLS helper remains an explicit optional comparison backend until the native path passes full handshake, exporter, validation, HRR/resumption, lifecycle, soak, same-session, classifier, and reproducibility gates. There is no fallback between backends. -
One coherent identity. Chrome is rebased to exact Google Chrome
151.0.7922.71and official Node24.18.0. The incomplete Firefox/Safari presets and dead rotation state were removed instead of being carried as unsupported claims. The native ClientHello structure is now closed, but complete-session qualification and a capture of the0x001balgorithm value remain explicit gaps. The helper has passed local wire/performance, lifecycle, process-scale, reconnect, and segmented-soak gates but remains opt-in pending matched WAN, same-session stealth, independent review, and the remaining RC gates. -
Bounded TCP producer backpressure. SOCKS and TCP forwarders now advance local reads on transport-write completion, preventing fast producers from overflowing the bounded application queue. The benchmark failure path is SIGPIPE-safe and always joins its receive workers, so failures are reported instead of ending in
std::terminate. -
Qualified helper boundary. The production Go entry point still applies Linux-only
no_new_privsand adopts connected TCP/IPC descriptors 3 and 4, while its connection core is injectable for real TLS qualification. Wrong CA, hostname, leaf pin, ALPN, and exporter behavior now fail through fixed, bounded IPC errors; detailed verification causes stay local. Native launcher tests cover unsafe file modes and symlinks, partial control and plaintext I/O, post-ready half-close/cancellation, child teardown, and repeated fd/zombie balance. Connection IDs use the BaseFWX RNG abstraction; the explicit no-BaseFWX build retains the YUME CSPRNG fallback. -
Bounded Linux process and soak qualification. At the exact clean signed lifecycle checkpoint, the 1/10/50/100/256-client Chrome-helper ramps completed with exact bytes and zero unexpected failures. The 256-client run held 256 clients plus 256 helpers concurrently and transferred 65,536 MiB. A 1,000/1,000 sequential reconnect storm transferred 2,000 MiB with no helper, zombie, fd, or thread growth. A 2,333-second full-speed batch moved 112,640 MiB per direction at 816.607 Mbit/s aggregate with zero byte mismatch, timeout, interruption, or unexpected server error. The soak used seven consecutive segments to preserve the signed endpoint’s 16,384 MiB per-invocation bound; it does not claim one uninterrupted >16 GiB connection or WAN behavior.
Fixed
-
Core receive backpressure and bounded queues. Both H2 roles transfer move-only receive-credit ownership through ordinary TCP, UDP, service, codec, packet, forwarding, and federation sinks. Slow consumers plateau and resume without automatic credit. Cover requests, pre-auth serialized wire, service writes, packet batches, and client UDP paths have independent bounded admission, cancellation, shutdown wakeup, and recovery behavior.
-
Strict peer-controlled schemas and stream ownership. Generic OPEN, client SOPEN/ROPEN, CONTROL registration/lifecycle, relay, codec, and auth metadata reject unknown fields, wrong scalar types, over-length strings, and invalid ranges without escaping the session boundary. Client and server stream IDs reserve atomically through map publication; collisions never replace local, pending, control, federated, or retired owners.
-
Durable configuration and authorization snapshots. Client/server config saves use owner-only temporary files and durable atomic replacement on the qualified POSIX path. Authorization-key and metadata operations share canonical persistent sidecar locks; daemon startup/reload parses one locked five-resource visitor/operator/admin snapshot before publishing it. ABI config/lifecycle/stream/packet outcomes now derive from typed internal statuses rather than diagnostic strings.
-
Relay and listener lifecycle hardening. Relay v2 binds composite peer identity, hybrid ML-KEM-1024/X25519 establishment, ratchet state, record counters, invite lifetimes, target policy, and trust persistence. Outbound files are hashed and streamed from one pinned regular-file descriptor, and POSIX receive publication is confined, no-clobber, bounded, and atomic. Federation duplicate peer IDs and reverse-listener accept ownership now fail without orphaning links, retaining sessions, or spinning cancellation paths.
-
EXEC fail-closed cleanup. Direct and relayed command execution remain unavailable regardless of reserved policy fields or build option. Clients do not advertise or enable inbound EXEC, and the former detached worker is removed pending a cancellable, joinable, sandboxed design.
-
Exceptional-path key lifetime. Superseded inner keys, AUTH transcripts, relay pending/KDF/channel state, ratchet roots/message keys, PSKs, exporters, and raw shared secrets are scoped or explicitly wiped across failure, throw, close, and teardown paths covered by the stabilization tests.
-
Sanitized ABI integration. Linux address-sanitizer builds resolve the selected compiler’s ASan and C++ runtimes and preload them in loader order for the Python/ctypes strict-C integration orchestrator. Instrumented
libyumeis therefore never loaded late into an unsanitized Python process, and the sanitizer suite does not depend on an undocumented caller environment. -
Parallel federation fixture isolation. The real two-node federation integration gives each daemon the fixture’s private HOME and XDG runtime, matching its client. Independent build matrices no longer contend for one user-level daemon instance lock when their federation tests overlap.
-
GUI facade synchronization boundary. Client status now exposes the configured security mode, composite/hybrid-ratchet posture, TLS backend, rekey window, peer fingerprint, and capabilities rather than obsolete light/heavy/off state. Chat open/send/close/history honor real channel and peer identities, and the unused callback promises are removed. Log subscribers run outside the sink lock with exception containment; the GUI resolver is owned through shutdown; and macOS bundle versions derive from the project version.
-
Hardened BaseFWX dependency pin. The pinned BaseFWX revision is now
e6ffbb79daa02bf62c31c3ae6513d5c603ec8dcd. It retains repeat-safe Argon2 discovery, fail-closed crypto/format limits, safer caller-buffer primitives, peer KDF policy coverage, and corrected Java streaming-test nonce discipline. It also supplies the owned explicit-nonce ChaCha20-Poly1305 operation used by YUME’s existing relay-history format, propagates Java benchmark worker failures, bounds whole-file benchmark concurrency, and defaults retired compatibility performance rows off. YUME’s consumed wire contracts are unchanged. -
Bounded operator-proof HTTPS. External proof URLs now use one strict HTTPS authority/target grammar across the in-process and static-Linux curl transports. The native path verifies the configured DNS name or IP literal, applies a 30-second end-to-end request deadline, constructs and parses HTTP with bounded Beast messages, and rejects bearer-token controls before any connection is attempted. Remote responses require an object containing a bounded canonical signature; untrusted API error text is not reflected into daemon errors.
-
Helper crash and truncation lifecycle. After
posix_spawn, the parent retained duplicate copies of the child-side IPC and connected TCP descriptors. Those copies masked EOF/HUP when a helper crashed or returned a truncated response, delaying failure until the handshake timeout and keeping the TCP peer open. The parent now closes both duplicates immediately after a successful spawn while preserving RAII cleanup on every failure path.
Remaining dev6 limitations
openssl-chrome151is the development default and runs without the helper; this is not a release-qualification claim. Matched WAN evidence, one uninterrupted >16 GiB connection, exact Chrome151.0.7922.71same-session recapture, external classifier/active-probe evidence, and independent security review remain release gates. Installed Chrome151.0.7922.108is functional-only evidence and does not replace or rewrite the dev6 fixture. Android, GUI, Windows, macOS, ARM, OpenWRT, static builds, and Debian archive publication are outside the first official 0.2.0 scope.
[Unreleased 0.2.0-dev5]
Hard break from 0.2.0-dev4: AUTH now carries each endpoint’s accepted ratchet
policy. Exact version equality rejects mixed dev4/dev5 peers; no compatibility
mode exists.
Added
- Authenticated security modes. Extreme preserves the dev4 default (256 KiB, 512 application frames, 500 ms sender-active). Normal uses 8 GiB, 262,144 frames, and 60 seconds. Soft uses 256 GiB, 8,388,608 frames, and 30 minutes. Ultimate accepts exact bounded JSON values, including arbitrary millisecond values such as 4281. Each direction uses the component-wise stricter local/peer policy. ML-KEM-1024, X25519, the random PSK, HKDF labels, AES-GCM/AAD, and one-use per-frame keys are unchanged. Challenge critical field 8 carries the server policy; response critical field 5 carries the client policy and moves the Ed25519 signature to field 6.
- Current BaseFWX 3.8 development pin. YUME now pins remotely reachable
BaseFWX commit
4692d4ce4edec2aa9835d04ad9ff6c3ad3ab9374; the consumed crypto APIs and wire contracts are unchanged from the prior pin.
[Unreleased 0.2.0-dev4]
Hard break from 0.2.0-dev2 and 0.2.0-dev3: the exact transport version changed,
so dev2, dev3, and dev4 binaries do not interoperate at admission or AUTH. No
compatibility mode exists or is planned.
Added
-
TLS-exporter channel binding for AUTH. The Ed25519 signature input and the establishment root now cover a 32-byte RFC 8446 section 7.5 exporter (
EXPORTER-yume/2.0/auth-channel-binding/v1) that each endpoint derives from its own live TLS object and never transmits. A malicious endpoint that terminates TLS with a client and holds compatible admission and inner PSK material can no longer forward that live AUTH exchange to a second server: the two connections have independent exporters, so the relayed signature does not verify at the far end. Both peers require TLS 1.3 and a finished handshake; a peer that cannot produce a 32-byte binding fails AUTH. There is no unbound mode to negotiate. The signature domain moves toyume/2.0/auth-signature/v2and the root label toyume/2.0/root/v2, so a dev3 peer fails loudly instead of downgrading. -
Bounded multi-epoch ratchet window. A direction may keep several authenticated, strictly contiguous future epochs in flight or prepared instead of one, negotiated per connection through the AUTH transcript and configured with
--rekey-window/rekey_window(1..64, default 8). A byte-saturated direction rises from 256 KiB towindow * 256 KiBper rekey round trip: roughly 35 Mbit/s to 280 Mbit/s at 60 ms RTT by the protocol model. Every per-epoch limit is unchanged — 256 KiB, 512 application frames, 500 ms of activity — and the receiver still enforces the byte and frame limits on authenticated plaintext. Gaps, duplicates, reordered ACKs, and offers past the advertised depth stay fatal. Offers are paced by application progress so a filling window does not emit a burst of rekey records. The public implementation status records what remains unmeasured.
Changed
- Stealth and cryptographic claims now match the implementation. One immutable Chrome 151/Debian 13 + Node 24 profile supplies the production TLS selection, User-Agent/client hints, capture-backed H2 opening, assets, and cover identity. Profile rotation remains rejected. Chrome/BoringSSL TLS parity and traffic padding remain evidence-driven work, and the 500 ms ratchet limit is sender-active rather than a twice-per-second wall-clock promise.
- The untrusted-server boundary is explicit. Client AUTH sends a public
Ed25519 identity plus transcript signature, not the private key; ephemeral
hybrid exchanges provide conditional forward secrecy against later
persistent-file compromise.
yumedremains the terminating single-hop proxy with access to decrypted YUME stream bytes. AUTH is now TLS-exporter-bound, so live forwarding by a malicious compatible endpoint is closed, but that does not make the terminating node blind to traffic it exits. - Client identity files are owner-only by contract.
--keys-genserializes to memory and creates both halves through the exclusive owner-only writer at mode0600, wipes the private PEM, and refuses to replace an existing path instead of silently overwriting an identity. Loading reads the private key through an already-validated descriptor and rejects symlinks, foreign ownership, group/world permission bits, and out-of-range sizes, so a poorly protected key can no longer sign an AUTH transcript. Enforcement is Linux/POSIX; Windows identity loading fails closed. - AUTH v2 records carry the negotiated window. The challenge gains critical field 7 and the response gains critical field 4, moving the Ed25519 signature to field 5. The client’s advertised depth is inside the signed record, so the negotiation is covered by the existing transcript signature.
- An acknowledged epoch is prepared, not entered. A direction advances when the current epoch can no longer carry the next application frame, so each prepared epoch delivers its whole byte budget.
Fixed
- Rekey ACKs are sealed in wire order.
SessionRatchet::Openused to seal the REKEY_ACK on the read path. A directional chain assigns sequence numbers at seal time, so a data frame sealed afterwards on the write path could reach the wire first and the peer would close the session with “replay or unexpected epoch/sequence”.OpenResult::control_responseis now plaintext and is sealed by the caller’s ordered write path like any other frame. The defect predates the window but was only reachable in practice with several exchanges outstanding. - Federation link stream ownership. The dialing loop’s TLS stream is now heap-owned and shared with the write path by a counted reference instead of a pointer into the loop’s stack frame. The lifetime was already guarded, but a counted reference removes the class of defect entirely.
[Unreleased 0.2.0-dev2]
Dev2 was the first hard-break 2.0 desktop transport line. It was superseded by dev3 before release; neither development version is a stable release.
Added
- Bounded server capacity controls. Administrators can set worker threads, aggregate tracked sessions, default bulk-key sessions, aggregate accepts per second, and an optional weighted-fair egress cap. Default tracked and bulk limits are 256 and 64 respectively; unlimited behavior must be explicit.
- Regular individual/bulk keys and a physical operator trust store. Shared bulk credentials are separately counted per connection and cannot receive controller, exec, LAN/full-control, privileged codec/service, or federation policy. Only individual keys in the separate operator store may receive outbound admin policy.
- External benchmark resource telemetry. Localhost, LAN, and WAN harnesses can sample server/client CPU time, fair per-core CPU, RSS/peak RSS, threads, and host CPU/RAM context. Multi-client runs are bounded by default and require explicit acknowledgement above the safe local ceiling.
Changed
-
Directional rekeys are pipelined without widening their blast radius. Each sender prepares the next authenticated ML-KEM-1024 + X25519 + PSK epoch while bounded current-epoch traffic remains. The existing 256 KiB, 512-frame, and 500 ms limits remain independently enforced; writes block only if the ACK has not arrived at the hard boundary. Exact version equality rejects dev1 peers rather than silently mixing the two scheduling contracts.
- AUTH policy lookup uses immutable snapshots. Regular and operator key/policy files are parsed and validated before atomic startup/reload publication. Failed reloads preserve the complete previous snapshot, and the same public key cannot appear in both stores.
- Capacity logic is modular. Thread-safe identity admission and weighted egress controllers are independent server runtime units with focused tests. The optional limiter is not constructed when egress shaping is disabled.
- Developer timing is centralized and absent from production binaries.
Debug/RelWithDebInfo builds share stopwatches, batched samples, asynchronous
intervals, and event macros across connection, AUTH, ratchet, scheduler,
TLS, H2, and WebSocket boundaries. Runtime collection remains opt-in.
Release/MinSizeRel preprocess the hooks and detail construction away, while
/procresource sampling remains external to the measured processes.
Security
- Authentication remains Ed25519 over the canonical transcript, and admission still occurs before ML-KEM decapsulation. Rekey pipelining does not change AEAD AAD, HKDF labels, hybrid secret composition, or directional usage limits. The dev2 version bump deliberately changes when the existing rekey records are scheduled and accepted.
[Unreleased 1.1 historical work]
Target: v1.1. Do not tag until the remaining release work and remote
validation are complete.
Added
- Static-site masquerade cover (
--real-root <dir>). yumed serves GET/HEAD for real files under one root with correct MIME,Content-Length,Last-Modified, nginx-styleETag, andAccept-Ranges, so the decoy is a coherent multi-asset site instead of “/=> 200, everything else => 404”. Path resolution rejects traversal, encoded-slash/backslash, control bytes, over-length targets, and symlink escape (canonicalized against the root); a per-response size cap bounds one cover reply. The same root/index backs both the HTTP/1.1 probe and the H2 decoy, with HTTP/1.1 keep-alive across a page’s assets (bodyless GET/HEAD only, bounded by a per-connection request cap and an idle timeout), conditional GET (If-None-Match/If-Modified-Since-> 304), and byteRangerequests (-> 206 Partial Content, or 416 when unsatisfiable). Implies--real; pair with--hide-in-the-crowd nginxfor the closest header fit.
Changed
- License: YUME source, apps, daemon, proxy, GUI, and libyume are AGPL-3.0-or-later. Build scripts and CMake entry points now carry matching source headers.
- Masquerade is now the active-probe boundary.
--public-noderequires obfs plus a nonempty shared secret. Missing, malformed, wrong-key, bad-order, or SNI/authority/listener-port-mismatched H2 admission stays outside AUTH. Empty-secret structural admission remains development-only. - The H2 opening is standards-oriented, not exact-browser branding. HPACK indexes, SETTINGS/ACK ordering, END_STREAM behavior, authority validation, mandatory client ACK of server settings, serialized fallback writes, and client decoy classification were corrected. Synthetic web profiles are not native nginx/Apache/etc. implementations.
- TLS profile rotation now advances after successful connections. The carrier User-Agent follows the active profile unless explicitly overridden.
Fixed
- Preauth privilege promotion. Self-signed peers admitted for configured
named services now persist as
PreauthServiceOnly; a central gate confines them to service.v1 OPEN/DATA/CLOSE and PING/PONG. - Caller-blind admin attach. Modern and legacy paths now share the trusted relay + caller outbound + target inbound authorization predicate.
- Bounded shutdown and queues. Added a five-second session-close deadline, 64-per-service/256-total pending service limits, four-worker client EXEC cap, and best-effort sensitive-buffer erasure. Sanitizer and soak validation remain outstanding; detached EXEC workers are bounded but not cancellable/joined.
[Withdrawn v1.1 development plan]
Added
--cluster-join <spec>+--cluster-bootstraponyumed. Friendly shorthand over the existing--peer '<json>'federation surface:--cluster-join [id@]host[:port][?pin=<sha256>]parses into the same FederationPeer JSON the daemon already consumes.--cluster-bootstrapmarks a node as a cluster entry point so federation works without an outbound peer list. Bracketed IPv6 is supported with an explicit validid@prefix. Implies--federation-enable.--cluster <host[:port]>onyume(client) as a friendly alias for--server+--port.--public-nodeonyumed: hardening preset for internet-facing daemons. Rejects--allow-exec/--allow-local-ip/--control-full/--no-inner/--no-obfs, requires--auth-keysand a nonempty--obfs-secret, defaults--hide-in-the-crowdtonginx, and applies bounded session/Argon2 defaults.--hide-in-the-crowd <profile>on both binaries: HTTP-layer disguise. Server profiles (nginx,nginx-stable,apache,caddy,cloudflare,express,gunicorn,none,yumed) are synthetic templates with profile-specific header order, charset, extras, and body shapes; they are not native server implementations. Client profiles (chrome,firefox,safari,edge,curl,wget,yume) set the User-Agent in stealth probes; when unspecified, the UA follows the active TLS preset.- Profile-driven disguise on every non-YUME probe. Pre-1.0 yumed closed the connection on HTTP probes when
--realwasn’t set — TLS-handshake-then-immediate-close is a textbook DPI signal. Now serves a profile-matching 404 even without--real, by routing throughSession::send_disguise_404. yume-net-mapnew read-only CLI tool: connects to a yumed admin socket and renders the current node + its federation peers as an ASCII fan/spoke diagram (Unicode box-drawing or--asciifallback).--jsonmode for downstream tooling. Auto-discovers local sockets under$XDG_RUNTIME_DIR/yume,/run/yume,/tmp/yume.scripts/yume_disguise_check.pyautomated profile-fidelity test: spins up yumed once per profile, probes with curl, validates Server header regex / extra headers / body length range / canonical body substrings.--dpimode adds nDPI flow classification (needstcpdumpcapability andlibndpi-bin). CI-runnable in <60 s.scripts/yume_bench_wan.pyvirtual-WAN benchmark with DPI comparison: twoip netnsconnected by a veth + tc-netem WAN profile, runs the same workload over yume and as a curl/chromium baseline, runsndpiReaderon captured pcaps, emits a side-by-side report (markdown or JSON).- Native embed C ABI in
libyume.so.1.include/yume/yume.hnow exposes opaqueyume_client,yume_server, andyume_streamhandles, JSON lifecycle/status helpers, fixed-buffer last-error reporting, and direct named service stream read/write calls for C/C++ embedders. - Authenticated named service streams. Clients can open project-neutral
proto: "service.v1"streams such asexample-service-v1; servers must explicitly enable the application-defined service in config, authorize it per key withallow_services, and register it through the C ABI before accepts are queued. - Stream peer metadata in the native ABI.
yume_stream_peer_jsonexposes the accepted stream’s service, authenticated Ed25519 SPKI SHA-256 fingerprint, peer/session ids, and remote address so embedders can bind sessions to their own device registry. - Embed JSON bind address support.
yume_server_start_jsonacceptslisten_addresswithlisten_port, allowing loopback-only native tests and embedded local services without a wildcard bind.
Changed
Browser profiles: The withdrawn v1.0 test-release block below documents the TLS profiles shipped at 1.0. Current builds use the bumped versions listed in this section.
- Historical release line moved to 1.1. At the time, the now-withdrawn
v1.0tag named the first test release and the tree targeted1.1as its first stable line. - BaseFWX pin currently names legacy-history 3.7.0 commit
ddf31617aab6bb9ac87129295564d6736e48c601, carrying Java Argon2id support, fixed Argon2 lane defaults across runtimes, secret-hygiene hardening, and the blackbox plugin ABI surface. The SHA remains fetchable but is not on the rewritten currentmain/v3.7.0lineage; choose and validate the intended canonical release commit before shipping 1.1. - Inner crypto secret hygiene. Introduced
basefwx::crypto::SecureBytes— move-only RAII owner that wrapsBytesand SecureClears on destruction. Replaces theSecretGuardraw-pointer pattern at every KEM-touching call site in yume (server_derive_key,validate_pq_keypair) and basefwx (filecodec.cpp4 sites,keywrap.cpp2 sites). Eliminates a use-after-free class fixed in 66153f6 by structural change rather than placement discipline. Existing string-secret SecretGuard sites are unchanged. - TLS browser-profile labels corrected to match the captured shapes: Chrome 131, Firefox 126, Safari 18, and Edge 123.
- JA4 diagnostics made canonical. Fingerprints now use FoxIO’s
a_b_cformat, GREASE filtering, sorted lower-case cipher/extension hex lists, SNI/ALPN exclusions, and original-order signature algorithms. Focused official-vector and match-threshold tests cover the implementation. X-Yume-BlobHTTP response header removed. It had zero consumers in the tree; the substring “Yume” was a passive fingerprint for any layer-7 inspector. The anonym blob still ships in the body (zero-width<span>+ HTML comment) where the actual readers look.
Fixed
- Use-after-free on KEM secret wipe. SecretGuard stores raw
Bytes*pointers and SecureClears them from its destructor. Reverse-construction-order destruction meant a SecretGuard declared BEFORE the locals it tracked accessed already-freed vector storage on scope exit. Manifested asmalloc(): unaligned tcache chunk detectedimmediately after PQ keypair validation on--pq-auto-generatestartup. Closed first by reordering (commit 66153f6), then by structural replacement with SecureBytes (commit 58c39a7). - Pre-existing yume_server link break from the
f6db161session.cpp split:epoch_now_mswas anonymous-namespace-local in session.cpp but referenced from session_control.cpp. Exposed at file scope in session.hpp; duplicate copy in federation_link.cpp removed. - Argon2 admission was not aggregate.
argon2_env_limits()previously returned{0,0,0}by default, making the parameter guard a no-op. It now seeds per-derivation ceilings (time=12, memory=512 MiB, parallelism=8), and positive environment values may deliberately lower or raise them. The server additionally reserves each Argon2 derivation against manager-owned aggregate memory/job limits before allocation; move-only RAII leases release accounting on all exits. Defaults are 512 MiB aggregate and four jobs, configurable through CLI or JSON. The unusedhas_argon2_limitsgate was removed. The client also checked its local cap in that implementation.
[withdrawn v1.0] - 2026-05-16
Former tag commit: https://github.com/FixCraft-Inc/yume/commit/82735dc12b17e7bc72592e32e74f52afd4b46247
Added
- First public test release of YUME (Yume Universal Multiprotocol Engine) — an open-source post-quantum stealth transport that tunnels TCP and UDP through real TLS 1.3 sessions using the project’s browser-oriented presets. The client (
yume), daemon (yumed), proxy, GUI, and libyume surface are AGPL-3.0-or-later and build from this tree. - Three-layer stealth stack stacked on top of TLS 1.3, all on by default and toggleable independently:
- Browser-oriented JA3 shaping plus the original project-local, pre-canonical JA4-like diagnostic via genuine OpenSSL 3.5
ClientHelloconfiguration.--profile chrome(Chrome 131) is the default;--profile firefox(Firefox 126) and--profile safari(Safari 18) are selectable, plus per-N-connection rotation via--tls-stealth-rotate/--tls-stealth-rotation-interval. - HTTP/2 carrier handshake (
--obfs) with the then-current project SETTINGS,WINDOW_UPDATE, and aHEADERSframe opening aPOSTto/<token>/<nonce>. The token isHMAC-SHA256(K, sni || hour_epoch || "yume-obfs-v2")truncated to 16 bytes hex; 1.0 allowed optional peer-pinning via--obfs-secretand accepted ±1 hour of clock skew. - Real HTML facade (
--real) so a browser hitting the same:443withGET / HTTP/1.1is served a real HTML page (or a Wikipedia redirect by default). YUME and a normal website coexist on a single port.
- Browser-oriented JA3 shaping plus the original project-local, pre-canonical JA4-like diagnostic via genuine OpenSSL 3.5
- Post-quantum KEM/DEM inner crypto via BaseFWX 3.6.4 (separate library;
current dependency metadata is centralized in
config/dependencies.json):- ML-KEM-768 (NIST FIPS 203 / Kyber-768) encapsulation for session keys when a master public key is configured.
- AES-256-GCM AEAD with a 12-byte nonce and 16-byte tag.
- Argon2id or PBKDF2-HMAC-SHA256 as an optional work factor over the high-entropy ML-KEM shared secret. The YUME transport handshake does not mix in a user password / PSK.
- HKDF-SHA256 for all subkey derivation.
- Ed25519 for client authentication via
--auth/--auth-keys.
- Live key hopping at 1–4 Hz (
--hop-interval, default ~500 ms). Each window encrypts with anHKDF(master, hop_index)derivative. This separates derived hop keys but is not forward secrecy: compromise of the retained master reveals every window. Disable for latency-sensitive paths with--no-hop. - Five routing modes, combinable:
--socks [addr:]port— SOCKS5 listener on the client side.- Local TCP/UDP forward (
--proxy host:port -> local:port). --run <cmd …>— spawn a command and pipe its stdio through the tunnel.- Android VPN capture (separate yume4a APK that captures all OS-level traffic).
- Server-side reverse port tunneling via
yumed --reverse-port-min/--reverse-port-max.
- Server-side egress options: direct, Tor outbound (
yumed --proxy tor://…, optional obfs4 bridge), and yumed-to-yumed federation links over mutual TLS 1.3 (--federation-enable,--federation-auth-key,--federation-anonym-ca,--peer). - Anonym / no-log mode (
yumed --anonym) for run-by-third-party endpoints: drops connection-level logs before disk, decoupled CA / sub-key flow (--anonym-ca-key,--anonym-sub-key), optional--anonym-tokenrate-limiting that doesn’t tie tokens back to user identities, operator key management via--operator-keys. - Optional Dear ImGui-based desktop GUI (
yume-gui) for Linux, macOS, and Windows. Ships as a portable single-file.exeon Windows (static MinGW runtime +x64-mingw-staticvcpkg triplet). - 9-target release matrix: linux-amd64 (dynamic + static), linux-armv7, linux-armv8, busybox-amd64-static, busybox-armv7-static, busybox-armv8-static, openwrt-mips (maintainer-attached), macos-arm64, windows-amd64 (CLI tarball + portable GUI .exe). Android via yume4a (~28 MiB arm64-v8a APK).
- Minimal footprint: the static BusyBox build runs on routers with as little as 128 MiB of RAM and no glibc.
- Release pipeline with full pre-publish self-test:
light-testshost-build +yume --version/yumed --versionsmoke pre-gate.- GUI cross-builds for the three desktop OSes (continue-on-error so a GUI dep regression doesn’t block CLI release).
- Static-link assertion on every
*-staticartifact (filemust not say “dynamically linked”;readelf -dmust show noNEEDEDentries). --versionself-test on every published binary (native amd64 directly, ARM viaqemu-aarch64-static/qemu-arm-static, MIPS viaqemu-mips-static, PE via wine if available else PE32 header check, tar.xz CLI bundles extracted and tested).- GPG-signed
*.sigper artifact plus aggregateSHA256SUMS.txt,MD5SUMS.txt,release-manifest.json.
- Project documentation included the main README, setup, security, routing, operations, packaging, permissions, release notes, and project website.
Changed
- The 1.0 test build included its wire, authentication-key, anonym CA/sub-key,
and
yume-obfs-v2token formats. The proposed 1.1 stable line was withdrawn. - Server-to-server federation links are pinned to TLS 1.3 only (no fallback to earlier TLS versions) — see commit
f13fbdb. - Build pipeline rewritten end-to-end vs the BETA cycle: smoke-gate before heavy builds, GUI added for desktop OSes, dynamic “busybox” artifacts dropped (they were misleadingly named — a glibc-dynamic binary can’t run on a real busybox/musl target), only verified-static
*-busybox-staticships, macOS job is matrix-ized so Intel can be added in a follow-up by uncommenting one matrix entry. - Website’s
assetMapaligned with the new release-artifact set: GUI download cards for Linux / macOS / Windows added; dynamic-busybox cards dropped.
Fixed
- Federation TLS regression: an earlier commit used the wrong TLS-method constant (
tls_clientinstead oftlsv13_client); pinned inf13fbdb. - 240-commit history through the ALPHA → BETA cycle accumulated and addressed: TLS-fingerprint correctness, HTTP/2 obfs token rotation, key-hop window seam handling, federation handshake edge cases, anonym-mode metadata leaks, Android VPN capture restart bugs, Windows MinGW cross-build deps, BusyBox static-link reliability across glibc/musl toolchains, GUI font rendering on Wayland vs X11 (URW Gothic Demi double-bold fix), CodeQL high-severity findings, OpenWRT SDK feed wiring, and assorted CI/website hash-rendering bugs.
Notes
- Threat model recap (full version in
docs/STEALTH.mdanddocs/EXPLAINED.md): YUME defends the transport. The route you choose decides who can see the client, who can see the target, and how much trust is placed in the YUME server. YUME does not by itself provide anonymity — combine with Tor egress, Tor-over-YUME, or YUME-Tor-YUME for that. - OpenWRT MIPS is intentionally not built in CI because cross-builds against the OpenWRT SDK are slow and brittle on hosted runners; maintainers attach the MIPS artifacts manually when a release is cut. The static BusyBox builds cover most embedded use.
- Intel macOS had no 1.0 build. Rosetta 2 runs x86-64 software on Apple silicon; it cannot run an arm64 binary on an Intel Mac.
- Windows GUI cross-build is best-effort in 1.0: marked
continue-on-error: truebecause the GUI-specific vcpkg packages (Freetype, GLFW3) on a fresh runner can take significantly longer than the CLI path and may time out. If the cross-build fails the CLI tarball still ships and the GUI lands in a follow-up. - Performance: one April 2026 WAN run measured about 234 Mbps download and 36 Mbps upload on the 1.x SOCKS path. CPU cost was not isolated, so the prior “<1 % typical, <5 % always” wording was not supported by that dataset. This historical result does not describe the current transport.
- The proposed 1.x compatibility policy was withdrawn with that development line. Current transport contracts are documented separately.
- License: AGPL-3.0-or-later across YUME binaries, source, and libyume. Bundled BaseFWX code follows its own split policy: LGPL library/API/runtime, GPL standalone tools, MIT OR Apache-2.0 example plugin templates. See
LICENSE.