YUME architecture
This page describes how native YUME is put together: which component owns what, which way dependencies point, and where the runtime boundaries are. The source map says where each piece lives in the tree.
Why YTP/1
YUME has to work as a standalone client and server and inside other applications. YTP/1 (YUME Transport Protocol 1) meets that with a session engine that owns its state explicitly and does not depend on sockets, TLS libraries, configuration parsing or application policy. Providers plug those in from outside.
The HTTP/2 and WebSocket carrier code and the browser profile came from
transport v2, the protocol before YTP/1, which has been removed. The relay
channel, file transfer and codecs live on as module libraries, and sealed
kits replaced the share container. The transport-v2 GUI was removed. The native
desktop GUI is a separate Qt program that talks to yume only
through its control socket.
A new protocol name proves neither better speed nor stronger security.
Implementation status lists the open gates.
Composition
Client and server compositions depend only downward:
client: ByteChannel -> SecureChannel -> Carrier ---------+
+-> SessionEngine
server: FrontDoor -> AcceptedCarrier --------------------+ -> StreamDispatcher
-> StreamHandler
-> RouteProvider
An application may supply or consume the endpoints at these boundaries. The session engine does not know about sockets, TLS libraries, HTTP/2 libraries, JSON, configuration files, command-line parsing, or GUI state.
Core contracts
ByteChannel
ByteChannel owns a reliable, ordered asynchronous byte path. Buffers are
move-only, reads and writes are bounded before admission, accepted operations
complete exactly once on a declared executor affinity, and cancellation is
distinct from terminal close. Its idempotent write shutdown is ordered after
accepted writes and preserves the read direction; providers that cannot
implement a genuine half-close fail instead of substituting a full close.
The session engine and native TCP write queues charge each buffer’s retained capacity to their memory budgets. Wire lengths and flow credit use its logical size. Shrinking a buffer does not allocate or return its reserved capacity, so a short payload can still exhaust a queue’s memory budget.
The native TCP provider uses a shared AsioExecutionContext. Its source-level
factories take that owner; socket adoption uses its concrete executor type.
The caller runs exactly one execution thread at a time and initiates create,
read, write and write-shutdown there. Wrong-context initiation accepts no work;
the throwing methods reject synchronously, while write-shutdown returns a typed
failure. Completions may occur inline on the declared context. Thread-safe
cancel/close use embedded, coalesced control tasks and a reserved scheduler
operation, without an allocating strand or caller-thread completion fallback.
The execution owner contains no thread: after closing providers and channels,
the runtime calls finish() and keeps running until all completions drain.
stop() interrupts execution and does not establish successful cleanup.
Like Asio, run() and poll() can propagate delivery exceptions; the runtime
must contain them at its runner boundary and resume to drain reserved failure
tasks. This concrete lifecycle is separate from the platform-independent
engine contract.
Name lookup does not run in the process that holds session keys.
SystemResolver sends each lookup over a private socketpair to a helper
process. The helper calls the system resolver with its unchanged NSS and DNS
configuration, one thread per lookup, so a slow name does not delay others.
getaddrinfo cannot be interrupted. Cancellation therefore settles a lookup
at once, and closing the resolver kills and reaps the helper, so final drain
never waits for a stalled system call. A cancelled lookup keeps its slot until
the helper answers. When every slot is taken and some belong to abandoned
lookups, the helper is replaced and its live lookups fail. The helper holds
only its standard streams and the socketpair. It drops every capability and
sets no-new-privileges before serving, which keeps system DNS and NSS parsing
away from key material. yume and yumed re-execute their own image as the
helper. Embedding hosts name the installed yume-resolver program. A numeric
host never starts the helper. A name without a configured helper fails closed,
and there is no in-process fallback.
SecureChannel
SecureChannel adds exporter-quality channel binding and bounded outer-channel
peer evidence to a byte channel. That evidence may be unauthenticated: in the
normal server-authenticated TLS shape, the server has no TLS client credential.
SecureChannelPeerEvidence records what the outer channel actually established
and is never application identity or dispatcher authorization. The default
provider is TLS 1.3. Its client applies the browser profile in
stealth/tls_client_profile.*. The browser’s offer can include TLS 1.2 and
HTTP/1.1, but YTP
accepts only negotiated TLS 1.3 and H2; profile application never relaxes that
boundary. Both native emitters require the pinned patched OpenSSL.
FrontDoor
FrontDoor owns listening, ordinary HTTP behavior, replay-protected cheap
admission, and promotion to a ready AcceptedCarrier. Missing, invalid,
replayed, or resource-exhausted admission follows the genuine configured cover
path. The front door does not emit a YUME-specific public rejection.
The ready-carrier boundary is necessary for genuine HTTP/2. Admission occurs
inside an established H2 connection and stream; reducing it back to a raw
SecureChannel would lose SETTINGS, stream, parser, and flow-credit state.
AcceptedCarrier transfers the carrier with immutable provider provenance and
keeps provider-specific shared connection state behind typed interfaces. It is
not an opaque context handle. Promotion also requires all front-door I/O,
cover fetches and H2 output to settle before transfer. At most one promotion
is allowed per TLS connection. The admission contract
defines the exporter-bound proof and replay rules.
The native H2WebFrontDoor implements this boundary with a native TCP listener
and AsioTcpAcceptedChannelOwner. Its configured immutable static site loads
through runtime::FileRoot, requires an index and explicit not-found file,
and serves ordinary and rejected-admission requests from the same content.
SNI and exporter bytes come from the accepted SSL connection.
Ordinary cover uses HTTP/2 over TLS 1.2 or TLS 1.3. HTTP/1.1-only ALPN offers fail the TLS handshake. A connection without ALPN completes TLS, receives the Node reference’s fixed 239-byte HTTP/1.0 403 response and closes.
YTP/1 promotion requires TLS 1.3, HTTP/2 and valid exporter-bound admission, and occurs at most once per TLS connection.
Promotion transfers the same TLS/BIO and H2 parser state after cover output
and the connection timer have drained. The carrier retains the accepted TCP
owner, bounded cover state and provider/executor provenance. A previously
answered cover stream need not wait for the peer to close its request side;
that stream transfers with the existing parser and cover ledger. Ordinary H2
requests still receive cover after promotion, and listener destruction does
not close a published carrier. This is a source-level ingress provider. The
experimental schema-1 ABI backend and the development standalone runtimes
compose it through NativeEndpoint.
The caller supplies the single-runner AsioExecutionContext. The front door
binds promoted carriers to that context’s ordinary and reserved control
dispatch. The runtime contains runner exceptions and resumes execution, closes owners, calls finish() and
drains completions. This ingress performs no DNS resolution.
Carrier
Carrier maps secure plaintext onto YTP record units and owns outer flow
credit. On the client, the exact frozen carrier provider constructs it from a
SecureChannel. On the server, the front door returns the already-promoted
carrier so its admitted application-protocol state remains intact. The first
provider uses a duplex HTTP/2 exchange. HTTP/2 is not a session-engine
dependency, and transport-profile geometry is not a YTP field.
The experimental h2-duplex provider uses the libnghttp2 and RFC 8441 state
machine in stealth/h2_carrier.*. It performs the
client’s genuine priming and extended-CONNECT acceptance sequence, while the
server construction seam accepts only an already-admitted live H2 carrier so
SETTINGS, HPACK, stream, and flow-credit state are not reconstructed. Its
private fixed 12-byte length envelope preserves YTP record boundaries across
arbitrary WebSocket and secure-channel fragmentation. Send completion means
the complete record has drained through H2 flow control and the secure-channel
write queue, not merely that it entered an internal queue.
The source-level H2Dispatch separates ordinary operation submission from
reserved control delivery. Ordinary submission may reject before accepting an
operation. Close, cancellation and move-owned credit return use an embedded,
coalesced task whose submission must not allocate or invoke inline. Both paths
run serially on the declared affinity; the caller drains the executor through
final owner release. Asio and deterministic provider tests share the same
intrusive control mailbox. Allocation failure retains typed errors and
exactly-once completion even when diagnostic text cannot be retained.
close() and cancel() both end a carrier, and pending operations settle
when the close starts. A live h2-duplex client carrier first sends the
captured browser’s close and waits for the server’s echo, and a server carrier
whose client closed writes its echo and waits for the client to end the
connection. STEALTH describes the sequence.
The optional H2Dispatch::defer timer bounds either wait to three seconds.
Without it, and for every other close, the carrier ends the connection at
once. The native endpoint therefore does not cancel the client’s TCP I/O when
it closes: each session ends its own connection.
SessionBootstrap
SessionBootstrap is a dependency-pure, one-session orchestration seam. A
client consumes the frozen byte-channel, secure-channel, and carrier providers;
a server borrows a shared persistent front door until one accept settles. It
validates the YTP/1 role, the suite’s explicit TLS 1.3 requirement, exact
accepted-carrier provider ID/API/capabilities, and equal front-door, carrier,
and secure-channel executor affinity before constructing SessionEngine.
Every layer transfers single ownership into the next accepted asynchronous
operation. Cancellation requests the active operation’s token and waits for its
one completion, closes a late returned object, and only then reports one
terminal completion. Success is reported only after SessionEngine reaches
Active; the reusable server front door is neither cancelled nor closed by a
session bootstrap.
Native endpoint composition
runtime::NativeEndpoint composes schema-1 configuration, protected credential
files, per-identity service policy, client TCP/TLS/H2 or server FrontDoor, and
SessionBootstrap on one caller-owned AsioExecutionContext. Its two roles
share the same session engine.
The endpoint clears each stored one-shot startup completion before delivery,
so a completed callback cannot retain its caller and execution context through
the session slot.
Credential parsing uses a private OpenSSL context and passes canonical DER to
the independent security factory. Admission, traffic access and administrator
authority remain separate. yume_private_files owns the protected-file reader
shared by credential loading and the module libraries, without importing
BaseFWX.
Creation validates the mapped session limits and requires capacity for the
complete 64 KiB AUTH envelope before opening listeners. Static cover loads a
bounded directory snapshot with index/404 pages and the active browser profile’s
priming assets; links, hidden entries and special files fail startup. IPv6
listeners use IPv6-only sockets so configured IPv4 and IPv6 wildcards coexist.
Every configured service needs a handler for its exact name and kind. The
immutable credential policy and the handler independently authorize each OPEN.
An optional NativeEndpointOptions::route_provider enables explicitly bound
DirectTcp/DirectUdp handlers. It must use the endpoint context; the engine
passes this selected instance to each routed OPEN and the endpoint owns its
cancellation. Handlers no longer select or retain a second provider. Exact
provider ID, API and capabilities remain checked. An explicitly bound handler
carries its caller’s destination policy, and its provider needs a
resolved-address policy for DNS egress. Schema-1 direct_tcp and direct_udp
declarations create handlers for their service names and kinds when the provider
is supplied. Each declaration requires destinations: public unicast addresses,
explicit networks, or both. runtime::NativeEgressPolicy checks them after
credential authorization and before DNS or socket creation, and a provider
built with the same policy checks every address a name resolves to.
Unspecified, multicast and reserved addresses are never permitted, and
IPv4-mapped IPv6 is evaluated as IPv4. An optional route_authorization
callback runs only after the configured destinations permit an OPEN and can
only refuse more. No destination authority exists without configuration or an
explicit handler. Explicit service bindings cannot also own a declared direct
adapter, and an unused route callback is refused. A route request is made only
by SessionEngine for an OPEN, or by engine::RouteAuthority for a stream
that reached the node through another protocol layer, a circuit exit’s, after
the caller applied the same destination policy. A server may also bind
services it provides itself, such as yume.circuit and yume.routes, whose
reserved names no configuration may declare. The credential policy and
egress pacing wrap them like configured services, and a service may cap its
streams’ receive windows below the session’s, which changes nothing on the
wire. Other services still require
explicit bindings. SOCKS5 declarations require a caller that explicitly owns
those adapters; NativeClientRuntime supplies that ownership. Packet declarations
require the caller to supply the matching packet binding and retain its adapter.
The native runtimes own Linux TUN addresses, routes and optional per-link DNS.
Reverse-proxy cover remains unsupported. The C ABI composes named channels and
routed client OPENs; standalone SOCKS5/TUN adapter declarations remain outside
its application-owned interface.
The endpoint bounds active sessions and pending starts and retains their engines.
When a session ends, the engine settles its pending callbacks and queues, then
notifies the endpoint through a reserved control task. The endpoint releases the
slot before calling session_ended on its context, so the callback can start a
replacement. Startup failures use the start completion alone. Endpoint close
also delivers session-ended callbacks; owners keep the endpoint alive through
close and drain. This notification does not mean OS cancellation has drained.
Retaining a closed engine also retains its carrier’s admission reservation;
release old engine handles so they do not consume front-door capacity.
A server may hand accepting to the endpoint. It keeps a set number of starts
pending on every listener within that bound, re-arms after each settlement and
waits a retry delay after a refused or immediately failed start. Manual and
automatic server starts are exclusive. The FrontDoor pauses OS accepts for one
second after descriptor or memory exhaustion. Other accept failures, or a
retry timer that cannot be scheduled, close the listener. If a listener stops
accepting, or an endpoint start retry cannot be scheduled while its listener
has nothing pending, the endpoint closes and reports that failure once. The
schema-1 ABI backend uses this loop.
Client startup has one absolute dial/TLS/carrier/AUTH deadline. A server starts
its session-creation/AUTH deadline after validated carrier promotion; idle accept
waiting does not consume that budget. FrontDoor independently bounds connections
and work before promotion. Expired startup cannot succeed because timer delivery
was delayed. Cancellation waits for owned bootstrap operations to settle before
releasing their slots. Close uses reserved
control dispatch; repeated close and destruction after final drain are inert.
The caller contains runner exceptions, closes the endpoint, calls finish()
and drains. An explicit dial address can differ from the authenticated DNS
name. A dial name uses the endpoint’s SystemResolver, which endpoint close
also closes.
yumed runs endpoint.event_loops execution contexts, each on its own
thread, and one NativeEndpoint on each. The first context owns the
listening sockets, signals, the control socket and the resolver helper.
A served endpoint’s terminal accept failure reaches that first context through
a reserved control task even when allocation fails. The runtime reports the
first such failure once, retaining its state through the callback; the caller
closes the runtime and drains its contexts.
It accepts each TCP connection before TLS and hands its
descriptor to the context serving the fewest sessions, whose front door
then runs TLS, cover, admission and the session for the connection’s whole
life, so no TLS or session state moves between threads. The listener’s
bounds hold across contexts: one count of connections waiting for
promotion pauses accepting, and one promotion budget admits carriers.
Services every context reaches have one owner each and never run a
callback under their lock: the admission replay cache, the credential
policy and security factory that a reload loads once and publishes, the
per-identity session registry that max_sessions evicts through from any
context, the direct-route connection budgets, the egress limiter and the
resolver helper, which takes requests posted from other contexts and posts
each answer back. A module stream connects to its module’s socket and runs
its bridge on the session’s context. Each context publishes its status, and the control
socket reports their sum. A configuration with a cluster or a packet
adapter runs one context, since circuits relay cells between sessions and
a packet adapter serves one device to one session. yume and the C ABI
run one context.
SessionEngine and StreamDispatcher
SessionEngine owns YTP/1 authentication, the key schedule, directional
ratchets, record protection, stream IDs, multiplexing, capabilities,
backpressure, and teardown. Stream zero is session control; clients own odd
application stream IDs and servers own even IDs.
notify_when_closed accepts one observer for the engine’s lifetime. It runs
outside engine locks after teardown, or inline if registered afterward, and
contains callback exceptions. Callers dispatch onto their own executor when
needed. The native client uses the endpoint notification to reconnect when its
session ends. Failed attempts, and sessions shorter than the longest backoff,
wait for exponential backoff.
A server NativeEndpoint can reload its credentials on its context. The
engine graph stays immutable. Reload publishes the new session security
factory and authorization policy through shared holders; later sessions use
the new factory and established sessions check the current policy for each
OPEN. Reserved control tasks reach every serving context to end revoked
sessions and apply lowered identity session bounds without allocating after
publication. A
session that finishes AUTH against the previous graph after its identity was
removed is refused at admission.
traffic() returns cumulative payload and carrier-record byte counts from
relaxed atomics, readable from any thread after termination too. They carry no
content or key material. The native client runtime folds them into its
thread-safe status() snapshot for status displays.
Only a successful complete YTP authentication creates PeerEvidence. That
post-YTP evidence represents the authenticated application peer and is the
identity passed to the dispatcher, stream handlers, and route policy. Outer
SecureChannelPeerEvidence and post-YTP PeerEvidence are distinct types so a
TLS observation cannot be promoted accidentally into application authority.
The dispatcher checks the authenticated capability and calls the selected
handler’s authorization policy for every OPEN. It reserves stream, pending,
queue, packet, credit, control, and rekey resources before allocation or
expensive work. A capability advertisement never bypasses per-open policy.
Service dispatch is keyed by (canonical name, service kind), so the same name
may intentionally expose one byte-stream handler and one packet handler without
colliding or weakening either policy. Names use a bounded lowercase ASCII
namespace grammar shared by the wire, config, engine, and ABI; authorization
never depends on Unicode normalization or case folding.
Stream reads report authenticated FIN as EndOfStream after delivering prior
records. The responder retains that result across normal stream retirement.
Its thread-safe terminated() observation overrides retained EOF after an
abort, local close or session loss, including for adapters holding delivered
records. Reading part of a delivered record retains that record’s carrier
credit until the remainder is consumed or discarded.
StreamHandler and RouteProvider
StreamHandler receives an authenticated byte-stream or packet-channel open.
It owns service-specific authorization and resource policy. RouteProvider
implements egress such as direct TCP or UDP, but its request type can be
constructed only after the dispatcher has authenticated and authorized the
peer, service, and destination.
The provider-level DirectRouteHandler is the reusable adapter between those
contracts. It opens egress only from async_route, forwards at most one bounded
operation in each direction, retains inbound carrier credit through the exact
route write completion, preserves packet boundaries, and maps byte-stream EOF
to directional write shutdown. Provider errors, partial completions, and
cancellation close both sides once.
Native byte-channel reads distinguish orderly EOF (Closed) from a socket
read failure (Internal). A socket failure in either direction closes the
native channel before its failing callback runs. Outstanding and later reads
then report Internal, and queued writes settle once with their actual byte
counts. The
direct TCP readiness probe preserves a reported socket error because a probe
can consume it before the subsequent read.
The dispatcher supplies the graph’s validated route provider to async_route;
the bridge retains that instance through asynchronous settlement. Creating a
handler requires destination authorization but does not select a provider.
If asynchronous acceptance returns PermissionDenied, the engine sends an
unauthorized CLOSE. This preserves policy refusals that can be decided only
after DNS resolution. Other acceptance failures send a handler-failure CLOSE.
The native AsioDirectRouteProvider is the first concrete egress
implementation. Its factory requires an explicit resolved-address policy in
addition to the handler’s service/name authorization. Before opening any socket,
it passes every selected numeric destination and the original authenticated
request to that policy. A refusal rejects the whole OPEN, including mixed DNS
answers. IPv4-mapped IPv6 is checked as IPv4; scoped IPv6 is refused because
the route contract has no scope field. Exceptions and reentrant cancellation
fail closed. This supplies the enforcement boundary; the embedding application
still owns its destination rules. It retains a bounded number of DNS results, applies the
instance-local socket protector after open and before connect, enforces
pending-open, active-connection, read, write, packet, resolution-time, and
connect-time bounds, and exposes TCP as ByteChannel and connected UDP as
PacketChannel. It shares the concrete single-runner AsioExecutionContext
with ingress. Initiation stays on that context; cross-thread cancellation and
close use reserved control tasks. Channel adoption can report allocation
failure, and final closed-handle release schedules no new cleanup. Destination
names use the shared SystemResolver. A canceled lookup returns the open’s
reservation at once, while its resolver slot stays taken until the helper
answers or is replaced, so abandoned DNS work stays bounded. This
provider also caps each authenticated route identity at half of its pending
and active limits, rounded up, across all of that identity’s sessions and
TCP/UDP services. A circuit exit accounts the previous hop, not the hidden
client. Failed, canceled and closed opens release both reservations.
This build-tree-only provider can be explicitly composed into NativeEndpoint.
The development yumed composes it with NativeEgressPolicy, and the
schema-1 ABI backend composes no route provider.
Provider composition
TransportSuiteDescriptor is immutable composition metadata: exact provider
IDs, provider API versions, required capabilities, service kinds, and resource
requirements. EngineBuilder registers provider instances locally, validates
the required role-specific graph, and freezes after its first successful build.
The suite declares provenance for all layers. Clients require byte/TLS/carrier
factories; servers validate the actual accepted carrier at bootstrap instead.
A front-door factory is optional because bootstrap borrows the actual listener.
Named-only handlers need no route instance; a DirectTcp/DirectUdp handler
requires a concrete route provider with that capability. Every supplied
optional instance still undergoes exact ID, API and capability checks.
There is no process-global mutable registry, reflection-based provider selection, dynamic loading in key-holding processes, fallback, or partial build. A configured provider that is absent, incompatible, or missing a required capability causes a typed hard failure.
Trusted custom providers may use the experimental source-level C++20 provider interfaces. Cross-language applications target the C ABI candidate after its functional and freeze gates pass. An out-of-process plugin protocol is intentionally deferred.
First suite
YTP/1 ships one mandatory suite:
| Layer | Required implementation |
|---|---|
| secure channel | native TLS 1.3 |
| front door | genuine HTTP/2 static website (a loopback reverse proxy is planned) |
| carrier | bounded duplex HTTP/2 |
| session | YTP/1 hybrid security and multiplexing |
| authentication | Ed25519 and ML-DSA-87 |
| establishment | X25519, ML-KEM-1024, per-identity access PSK, TLS exporter |
| records | directional ratchet, one-use AES-256-GCM keys |
| routes | explicit direct TCP and UDP through dispatcher policy |
YTP/1 contains no suite negotiation. A second suite cannot be added as a fallback; useful reviewed alternatives require a later wire version with an explicit negotiation design.
Source and target boundaries
Native source is organized by dependency:
| Path | Ownership |
|---|---|
src/engine/ |
dependency-pure channels, providers, builder, bootstrap, dispatcher, and session state |
src/ytp/ |
dependency-pure YTP/1 codecs, domains, and canonical vectors |
src/config/v1/ |
strict immutable schema-1 parsing; no secret loading |
src/providers/ |
native session security, browser-shaped TLS, client/accepted TCP channels, native FrontDoor/static cover, H2 admission/carrier and direct routes |
src/runtime/ |
protected schema-1 credentials, immutable per-identity authorization, native endpoint/session lifetimes, configured egress policy, and the yume/yumed runtimes with their SOCKS5 and TUN adapters |
src/admission/ |
protocol-neutral H2 path/authority parsing, HMAC and replay reservations; each protocol owns its encoding |
src/abi/ |
experimental exception-contained C ABI handles, validation, diagnostics, and backend leasing. It accepts schema-1 documents only and reaches the runtime through the embedding seam |
src/abi/native_backend.cpp |
experimental schema-1 embedding backend that runs NativeEndpoint on its own thread behind the blocking ABI |
src/modules/ |
module programs and the libraries planned modules build on: the relay channel with its stores, file transfer and application codecs. Only these libraries use BaseFWX |
tools/ |
provisioning and evidence tooling |
The installed yumed and yume build from src/runtime/ with every native
provider. The source map lists
what is composed and what is still open.
The foundational CMake targets enforce the following dependency rule:
yume_engine + yume_ytp1 + no OpenSSL, nghttp2, socket, JSON, CLI,
yume_session_bootstrap + filesystem, or GUI dependency
yume_circuit1
yume_config_v1 nlohmann JSON only
native providers engine/YTP plus their explicit system libraries
native runtime config, bootstrap, providers, protected files
yume_embed native endpoint, OpenSSL security provider and
threads, no BaseFWX
C ABI config_v1 plus the embed backend, no private-header API
module libraries BaseFWX, OpenSSL and JSON, never linked by the
programs or the C ABI
future adapters/executables C ABI or explicit application layer
YUME owns YTP authentication, domains, transcript construction, key schedules,
ratchet semantics, admission and authorization. The concrete YTP/1 security
provider calls OpenSSL 3.5 directly. BaseFWX supplies primitives and secret
containers to the module libraries only. It is a separate ignored checkout
pinned by config/dependencies.json. The source map
connects each mechanism to its implementation and explains the data path.
Public ABI boundary
The explicitly enabled candidate builds as unversioned
libyume.so. It exposes opaque runtime, config, endpoint, stream, and packet
types but is not a frozen installed product ABI. The surface is role-neutral
and has no JSON operation bus. A runtime owns callback delivery and coordinates
child endpoints; execution resources belong to the selected backend. An
immutable config owns validated values, an endpoint owns one backend selection,
and stream/packet handles own their application I/O lifetimes. Native packet
handles preserve record boundaries and credit. Development SDK installation is
a separate opt-in, not an ABI freeze.
The ABI accepts schema-1 documents only. Enabling its build also enables the
complete native provider graph, which yume_embed composes. The
schema-1 backend owns one execution thread per started endpoint and performs
every engine and provider call on it. Application threads hand requests over
through allocation-free control tasks. Received records keep their receive
credit until the application has copied every byte.
The ABI contract defines thread safety, one-reader/one-writer rules, callback re-entry, cancellation, timeouts, shutdown, destruction, peer identity, and handle-scoped diagnostics. No exception or private C++ type crosses it.
Ingress and evidence boundary
Captured browser geometry lives in config/transport_profiles.json and
immutable fixtures. It may change independently of YTP/1. Profile qualification
requires exact TLS/H2 semantic gates, active-probe cover behavior, immutable
captures, and held-out classifier evidence for the named environment. A
fingerprint string or successful request is not whole-session equivalence.
Trust boundary
The default topology is single hop:
What each part does
- Application
- Reaches yume through its SOCKS5 listener, a local forward or the managed TUN device, or embeds YUME through the C ABI.
src/runtime/native_socks5.cppsrc/runtime/native_forward.cppsrc/runtime/native_packet_adapter.cppinclude/yume/yume.h - yume
- Holds one authenticated YTP/1 session to the server and carries each connection or packet flow as a stream inside it.
src/runtime/native_client_runtime.cppsrc/runtime/native_endpoint.cpp - yumed
- Decrypts the session, checks that the client may use the service and reach the destination, and opens the outbound socket. It sees what it forwards.
src/runtime/native_server_runtime.cppsrc/runtime/native_egress_policy.cppsrc/providers/asio_direct_route_provider.cpp - Destination
- Gets an ordinary connection from the server, and sees the application's own traffic, such as its TLS.
Text version
+----------------------------------+
| Application |
| browser, curl, any program |
+-----------------+----------------+
|
v
+-----------------+----------------+
| yume |
| client |
+-----------------+----------------+
|
v ==YUME==>
+-----------------+----------------+
| yumed |
| ends the tunnel, applies policy |
+-----------------+----------------+
|
v
+-----------------+----------------+
| Destination |
| sees yumed's address |
+----------------------------------+
The server terminates YTP cryptography and is the explicit exit. A client can instead carry its streams through a circuit of two or three servers of one operator’s cluster, where the entry knows the client but not the destinations and the exit knows the destinations but not the client. The operator runs every server of a cluster and can match what they see. Circuits across operators and a decentralized directory are planned. Chat and file relay are planned as modules on the relay channel library.
See YTP/1, C ABI, and the threat model for the normative boundaries.