YUME source map
yume, yumed and the C ABI run native YTP/1 over one set of components.
Transport v2, the protocol before YTP/1, has been removed, and commit
9070b0a is the last that contains it.
IMPLEMENTATION_STATUS.md owns the open gates.
Vocabulary
| Term | Means |
|---|---|
| YTP/1 | YUME Transport Protocol 1. The wire protocol yume and yumed speak. Version 1 of a new protocol, not “YUME 1”. Its kernel is src/ytp/ and its contract is protocol/YTP_1.md. |
| schema 1 | The strict configuration schema for both roles, parsed by src/config/v1/. |
| ABI v1 | The role-neutral C interface in include/yume/yume.h. Its version is independent of every wire and product version. |
| admission | The cheap, replay-protected proof a client sends before its carrier is promoted. A request that fails this check stays in cover handling and never reaches AUTH. |
| admission key | The 32-byte key the server and its clients share for admission. It must differ from every access PSK. |
| access PSK | The 32-byte secret one authorized client identity shares with the server. It enters the YTP/1 key schedule. |
| cover | The static site yumed loads at start and serves for ordinary HTTP/2 GET and HEAD requests. It refuses other methods with a fixed 405. There is no built-in fallback site, and a configured reverse-proxy cover is refused as not implemented yet. |
| carrier | The outer connection YTP records ride inside: TLS 1.3, then HTTP/2 with an RFC 8441 extended CONNECT stream of WebSocket binary frames. |
| relay channel | The end-to-end channel between two users that the relay module library keeps for the planned chat and file modules. Its labels come from transport v2. See protocol/RELAY_CHANNEL.md. |
| BaseFWX | A separate pinned repository of cryptographic primitives, checked out at basefwx/. Only the module libraries build on it. yume, yumed and libyume do not link it. |
| evidence profile | A captured browser identity such as chrome151-node24-v1. It describes geometry only. It is not a wire version, and recapturing one does not change the protocol. |
The product (0.3.0-dev1), YTP/1, schema 1, ABI v1, the evidence profile and
the relay channel protocol (version 2) are independent version axes. Do
not bump one to match another.
Directories
Implementation paths below are relative to src/. The candidate header in
include/yume/ is relative to the repository root.
| Path | Owns |
|---|---|
engine/ |
Dependency-clean contracts and the session: ByteChannel, SecureChannel, Carrier, FrontDoor, RouteProvider, StreamHandler, SessionEngine and SessionBootstrap |
ytp/ |
YTP/1 protocol kernel and security domains. Protocol and AUTH/key-schedule codecs share internal big-endian operations in ytp/detail/byte_order.hpp, and each codec owns its bounds checks and error order. It may not include the engine |
circuit/ |
The circuit 1 codec: cells, handshake encodings and relay messages with their bounds, and its vectors and generator under circuit/testdata/. Like ytp/ it performs no I/O or cryptography and builds only on YTP/1 and common/ |
providers/ |
OpenSSL session security, internal deterministic ytp1_crypto constructions, TLS channels, Asio TCP/UDP routes and client/accepted TCP channels, the H2 carrier, the native FrontDoor and configured static cover. loopback_http_fetch.* fetches GET and HEAD from a loopback web server for a reverse-proxy cover, and socks5_upstream.* reaches a server through a SOCKS5 proxy. child_process.* starts a helper program with one passed descriptor and watches it through a pidfd, and process_privileges.* drops a helper’s privileges before it works. composite_keys.* parses composite keys, signs and verifies composite signatures and hashes tagged fields in a private OpenSSL context, for cluster lists and circuit hops, and circuit_crypto.* holds circuit 1’s handshake and layer constructions on ytp1_crypto’s context, whose X25519 and ML-KEM wrappers both protocols share |
runtime/ |
Native application composition, credentials, adapters, control and circuits. See runtime owners |
setup/ |
yume-setup and yume-doctor. kit.cpp writes kits and client bundles, kit_layout.* holds the files a kit contains, and cluster.cpp keeps an operator’s cluster record, link secrets and signed list. material.* makes every key and certificate in one private OpenSSL context and reads each back with the composite_keys readers before it is written, and files.* creates owner-only files exclusively, stages new directories and publishes them with a no-replace rename. doctor.* parses a configuration with config/v1 and checks the files it names. Before signing is published, cluster.cpp checks the list and routes view with runtime/cluster_list.*, which the yume_cluster_list library owns apart from the TLS provider. The tuning presets come from config/tuning_presets.json, which the build embeds |
abi/ |
The opt-in C ABI. yume_c.cpp owns handles, input validation, diagnostics, callback rules and backend leasing. It accepts only schema-1 documents, parses them through config/v1 and reaches the runtime only through the embedding seam endpoint_backend.hpp. native_backend.cpp is yume_embed, which runs NativeEndpoint on its own execution thread and links no BaseFWX. It also keeps each endpoint’s message feed, opens sealed kits, and composes the device bridge and session keeper for a client with a device. compatibility_manifest.hpp is the version view the ABI reports |
common/ |
Dependency-clean contracts shared across layers and the ABI: the canonical service-name grammar and bound, canonical destination networks, the egress address classes that configuration and native policy share, the UDP datagram budget the SOCKS5 UDP relay uses, hex encoding, best-effort secret erasure, the development timing helpers, the opt-in benchmark probes, and the product and protocol version constants |
admission/ |
Shared path/authority parsing, private-context HMAC and bounded monotonic replay reservations, with no protocol or runtime dependency |
stealth/ |
The browser-shaped outer layers: the Chrome TLS client profile applied to OpenSSL, the active cover profile with its generated registry, and the HTTP/2 and WebSocket carrier with its wire profile and outer observer. clienthello_dump.cpp and h2_opening_probe.cpp are evidence probes that the tests run |
fs/ |
Bounded regular-file reads, and reading and writing owner-only secret files. Reads open nonblocking and refuse special files before reading |
config/v1/ |
The strict schema-1 parser with RFC 6901 error pointers |
modules/ |
Module programs, which link no YUME library, and the libraries they build on. echo/ is the example module that the tests run. relay/ is the relay channel between two users (handshake, records and hybrid ratchet), the stores its users keep (encrypted chat history, the password-derived relay secret and peer trust), and the invite and directory records with the policies that check them. files/ holds relay file transfer on top of it: an outbound source pinned to one opened file and a confined receiver that publishes only a verified file. codecs/ is the application codec core for a planned codec module: the HTTP request and response envelope, backend dialing rules and the Monero RPC descriptor. The libraries build only with YUME_BUILD_BASEFWX_MODULES=ON |
gui/ |
The optional Qt 6 Quick desktop program yume-gui, built with YUME_BUILD_GUI=ON. control_client.* speaks control protocol 1 over the owner-only socket, tunnel.* starts a kit’s yume detached, polls it and stops it, kit_store.* lists kits and imports them through yume --import-kit, tuning.* delegates validated settings writes to yume-setup and sequences restart, and app_state.* is what the QML pages under qml/ see. headless.* and capture.* are its display-free lifecycle and page capture. It includes no other YUME layer, and no other layer includes Qt |
test_support/ |
Allocation-failure hooks, generated TLS identities, JA3/JA4 ClientHello analysis and the in-memory circuit fixtures (an execution thread, packet stream pairs, an echoing destination and node identities) for isolated tests, with no production allocator or runtime dependency |
include/yume/ |
The public candidate C header. It is unfrozen, and development SDK installation is opt-in |
Runtime owners
Paths here are relative to src/runtime/.
| Area | Source | Responsibility |
|---|---|---|
| Credentials and sessions | native_credentials.*, native_endpoint.*, accept_scheduler.hpp |
Protected credential files, identity grants, provider/bootstrap/session composition and paced accepts. NativeServerShare shares listener bounds, replay, credentials, egress limits and identity session counts across event loops |
| Programs and local listeners | native_cli.cpp, native_server_runtime.*, native_client_runtime.*, local_listener.*, native_socks5.*, native_socks5_udp.*, native_forward.* |
Client/server lifecycle, SOCKS5 and forwards. The local adapters share one loopback TCP and private UNIX accept loop |
| Egress policy and pacing | native_egress_policy.*, egress_lists.*, egress_limiter.*, paced_stream.* |
Destination checks, address-range lists including MaxMind country data, and the server’s weighted per-identity egress rate |
| Modules | module_connector.* |
Validates each module socket and connects once per authorized service stream |
| Control and evidence | control_socket.*, control_status_text.*, message_log.*, outer_carrier_evidence.* |
Owner-only control requests, text status, printed-message history and payload-free outer-carrier reports |
| Kits | sealed_kit.* |
Sealing and opening client kits for --seal-kit and --import-kit |
| Reconnection and devices | session_keeper.*, device_bridge.*, device_nat.* |
Retried client and cluster sessions. The device bridge carries TUN traffic as stream/packet OPENs; dependency-clean NAT rewrites TCP segments so the host kernel terminates each connection |
| Cluster state | cluster_list.*, cluster_state.* |
Verification and parsing of signed lists/routes, and the highest serial a server has loaded |
| Circuit services | circuit_node.*, circuit_client.*, circuit_host.* |
The node’s yume.circuit service, client circuit construction/carriage, the yume.routes view and exit egress policy |
| Client routes | client_routes.*, circuit_routes.*, circuit_pool.*, circuit_status.hpp, plain_http_guard.* |
Route-view anti-rollback, route selection, pooling, shorter-route proposals, status and the plain-HTTP rule |
| Circuit test client | circuit_probe.cpp |
Client of yume_native_circuit_test; it is not installed |
Composition and open gaps
The native endpoint composes every component below. Follow actual target inputs, not file names:
| Boundary | Current source evidence | Still open |
|---|---|---|
| Server ingress | providers/h2_web_front_door.* implements native listening, configured static cover, exporter/replay admission and retained H2/TLS promotion. Its H2WebListener accepts on one context and hands each connection to a served front door on another, with the listener’s connection and promotion bounds shared through H2WebListenerShare |
Composed through runtime/native_endpoint.* by yumed and the schema-1 ABI backend. yumed without a cluster accepts through H2WebListener and serves on every event loop. Qualification remains |
| Client admission | providers/h2_duplex_carrier.* owns a 32-byte key and emits a fresh exporter-bound proof through ytp1_h2_admission.* |
Composed by runtime/native_endpoint.* and reached through the schema-1 ABI backend and yume |
| TLS profile | providers/tls13_secure_channel.cpp applies stealth/tls_client_profile.*, and a published YTP channel still requires TLS 1.3 and H2 |
Qualify actual provider output and full-session behavior. Profile configuration alone does not establish circumvention |
| Session composition | runtime/native_endpoint.cpp calls SessionBootstrap::create for both roles, retains active engines and composes configured direct TCP/UDP adapters whose schema-1 destinations runtime/native_egress_policy.* enforces |
abi/native_backend.cpp drives named streams/packets and routed client OPENs for the ABI. Schema-1 adapters and the control socket are refused there, because they are standalone application components |
| Embedding | abi/yume_c.cpp accepts schema-1 documents only and runs the native backend, which carries named byte streams, named packets and destination-routed TCP/UDP OPENs |
Supported-platform SDK and production qualification |
| Applications | yumed and yume run direct routes, forwards, SOCKS5 CONNECT/UDP ASSOCIATE, modules, Linux managed TUN and circuits through one cluster over NativeEndpoint, and yume-gui drives yume over its control socket |
Packaging, the GUI’s remaining features, Android and production qualification |
yume_h2_carrier is the single build owner of the stealth/ H2,
WebSocket, wire-profile and observer sources, and the native adapter
yume_h2_duplex_carrier links it. It propagates the diagnostics setting to
callers because public declarations depend on it. Release and MinSizeRel
compile those diagnostics out. yume_cover_profile owns profile data with no
library dependency, and yume_tls_client_profile owns SSL_CTX profile
configuration. The H2 library links profile data, nghttp2, OpenSSL Crypto and
platform threading, with no SSL, BaseFWX or session-engine dependency.
yume_bounded_files owns the bounded and confined reader that the cover site,
and the egress lists use. Every archive the shared ABI takes
in, these two included, gets hidden, PIC, non-LTO preparation first.
yume_native_endpoint links yume_direct_route_handler directly for
configured destination adapters. That archive depends only on the engine
contracts.
admission/h2_admission.* owns shared path/authority parsing, HMAC and
monotonic replay reservations. It links only OpenSSL Crypto and Boost headers,
and layering rejects dependencies on either protocol or runtime.
providers/ytp1_h2_admission.* owns the distinct exporter-bound
YTP admission construction. It deliberately
omits wall time and evidence-profile IDs. providers/h2_web_front_door.*
combines those helpers with received TLS SNI, a locally derived exporter, a
caller-shared replay cache, configured cover and one promotion per TLS lifetime.
providers/cover_site.* loads an immutable operator-supplied static site
through runtime::FileRoot, including an index and explicit not-found file.
Requests select preloaded routes without filesystem access or DNS. There is
no built-in fallback site or reverse-proxy implementation yet.
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.
AsioTcpAcceptedChannelOwner adopts already-connected TCP or UNIX stream
sockets using the same bounded channel state and registry as client TCP. It
supplies no listener or cover policy. The FrontDoor uses it for every accepted
socket, settles its output and timer before transfer, and passes the same TLS
and H2 objects to the carrier. Cover streams whose request side remains open
transfer with their existing parser and cover ledger. Published carriers
retain the accepted-channel owner and configured cover independently of the
listener’s lifetime. AsioExecutionContext owns the concrete single-runner
execution and reserved control dispatch used by ingress TCP, direct TCP/UDP
routes and promoted H2 carriers. yume_asio_execution_context owns that shared
implementation, and the two Asio socket-provider targets link it
independently. The shared providers/control_task.hpp mailbox retains
embedded task owners without allocation and adds no Asio dependency to H2 or
the engine. See the ByteChannel lifecycle for
initiation and drain requirements. The FrontDoor binds H2 dispatch to its
context. Its caller must contain runner exceptions, close owners, call
finish() and drain completions. Ingress performs no resolution.
Qualification must exercise sustained allocation failure as well as ordinary
cancellation.
providers/system_resolver.* owns name lookup for both Asio network
providers through a killable helper process, described in the
ByteChannel lifecycle. It starts the helper
through providers/child_process.*.
providers/system_resolver_protocol.hpp is the private message format and
providers/system_resolver_helper.* the helper side, which links only libc
and threads. The standalone yume-resolver program
(providers/system_resolver_main.cpp) serves embedding hosts. yume and
yumed dispatch to the same helper when started under that name. The test
helper yume_resolver_stall_helper adds three names: one that never resolves,
one that fails as unknown, and one that resolves to IPv4 loopback, whatever
the host’s DNS does. A remote resolver (SystemResolver::create_remote)
has no helper of its own: it posts each lookup to an owner on another
context and posts the answer back, so the event loops of one yumed share
one helper.
EndpointBackend has one endpoint interface, and its service operations are
directional: a server registers and accepts, and a client opens. YTP’s engine
can open services in either role. The schema-1 backend keeps the ABI direction
because a server-initiated open needs an authenticated way to choose one of the
server’s sessions, which the endpoint API does not offer. A schema-1 client
therefore refuses OPENs from its server. This is an ABI decision, not a
protocol rule.
Data through the components
The native endpoint composes the following path. The schema-1 ABI backend
supplies named byte streams to it, and the SOCKS5 adapter supplies CONNECT
byte streams and UDP ASSOCIATE packets. runtime/native_packet_adapter.*
bridges a leased TUN through directional IP policy,
runtime/linux_tun_network.* owns Linux addresses, routes and DNS, and
providers/linux_tun_packet_channel.* owns asynchronous device I/O.
| Stage | Data and operation | Source owner |
|---|---|---|
| Application to session | A named service opens a byte stream or packet channel. Writes consume stream and connection credit, then become canonical DATA or PACKET frames. | engine/session_engine.cpp, ytp/protocol.cpp |
| Record protection | The security provider seals the canonical frame with AES-256-GCM. A directional epoch and sequence identify a one-use key/nonce pair. The provider caches the fixed HKDF extract beside its directional root, wipes both together, and expands each record with the unchanged binding and token. The engine prefixes the ciphertext with that token. | engine/session_engine.cpp::protect_frame, providers/openssl_security_provider.cpp |
| H2 carrier | A 12-byte length envelope preserves each record across partial reads. The carrier sends it in WebSocket binary frames on an RFC 8441 extended CONNECT stream and accounts for receive credit. | providers/h2_duplex_carrier.cpp, stealth/h2_carrier.cpp, stealth/websocket_codec.cpp |
| Outer connection | OpenSSL TLS 1.3 encrypts H2 bytes. Memory BIOs exchange encrypted bytes with a ByteChannel. The Asio client provider reads and writes the TCP socket. | providers/tls13_secure_channel.cpp, providers/asio_tcp_byte_channel_provider.cpp |
| Peer delivery | The reverse path removes TLS and carrier framing, verifies the record token/tag, decodes the canonical frame and dispatches its stream ID. Invalid protection or framing terminates the session. | TLS/H2 providers, engine/session_engine.cpp |
| Destination access | OPEN authorization produces an AuthorizedRouteRequest. The Asio provider checks every selected numeric address through an explicit policy before socket creation. The direct-route handler bridges the stream to a TCP or UDP route, retains credit until delivery and closes both sides on terminal failure. |
engine/session_engine.cpp::process_open, providers/direct_route_handler.cpp, providers/asio_direct_route_provider.cpp |
The egress endpoint decrypts YUME records and can read their contents. An application’s own HTTPS remains application data inside the tunnel and keeps its separate browser-to-origin TLS protection. Reply traffic uses the opposite directional record keys and independent credit accounting.
yume_private_files owns fs/secret_file.cpp. The native credential loader
and the module libraries link it, and it links no BaseFWX.
The embedding backend also links yume_native_credentials directly for
yume_config_validate_client_credentials: synchronous client credential
loading and a private-key-derived local fingerprint without an endpoint.
The C ABI and JNI adapters contain no independent credential parser.
Authentication and cryptographic ownership
TLS server authentication, carrier admission, YTP peer authentication and
service authorization are separate checks. The native FrontDoor verifies
admission, runtime/native_credentials.* loads protected schema-1 credentials
and per-identity authorization, and runtime/native_endpoint.* composes
sessions.
| Mechanism | Purpose and implementation |
|---|---|
| TLS 1.3 | tls13_secure_channel.cpp verifies the configured trust and hostname and selects H2 with ALPN. Distinct TLS exporter labels bind YTP authentication and H2 admission to this connection. Its client applies the browser profile. Full-session stealth remains unqualified. |
| Ed25519 + ML-DSA-87 | openssl_security_provider.cpp::verify_composite requires both signatures for the composite identity. The client checks its configured server identity. The server selects an authorized client identity and its access PSK. |
| SHA-256 and HMAC-SHA256 | SHA-256 hashes canonical transcript/identity inputs. HMAC authenticates the per-identity PSK proof and key confirmation. Hashing alone provides no confidentiality. The admission key and access PSK have different roles. |
| X25519 + ML-KEM-1024 | Hybrid establishment contributes both shared secrets. derive_initial_roots binds them with the access PSK, TLS exporter, transcript, roles, identities and capabilities through the canonical key-schedule encoding. |
| HKDF-SHA256 and AES-256-GCM | HKDF derives directional roots and per-record key/nonce material. GCM encrypts and authenticates application frames. Suite, direction, epoch and sequence enter the authenticated data. Rekey uses its own domain and authenticated exchange. |
| Primitive library | The YTP session-security provider uses a private OpenSSL 3.5 context and its default provider. Its target links OpenSSL Crypto, engine and YTP, with no BaseFWX or liboqs dependency. The relay module library takes ML-KEM-1024, X25519, HKDF and AES-GCM from BaseFWX and its signatures from OpenSSL. |
Exact encodings, domain strings, AUTH flights and the special REKEY_ACK protection rule belong to YTP/1. Qualification and measurement boundaries belong to implementation status.
Layering, and where it is enforced
engine + ytp1 -> session_engine -> session_bootstrap
ytp1 -> circuit1 -> providers (circuit crypto) -> circuit_runtime
engine -> providers (security, TLS, H2 carrier, FrontDoor, routes, resolver)
config_v1 + session_bootstrap + providers -> native_endpoint
config_v1 + composite_keys + cluster_list -> setup -> yume-setup, yume-doctor
native_endpoint + circuit_runtime -> native_runtime -> yume, yumed
native_endpoint -> embed -> abi (libyume)
basefwx -> modules (relay, files, codecs)
cmake/YumeLayering.cmake runs two checks
during configuration, and CTest yume_03_layering_check reruns the include
check. yume_03_layering_guard_test feeds both checks fixtures that must
pass or fail.
yume_assert_exact_link_dependencies pins a target’s exact direct link list.
Every static or shared library in src/ and src/gui/ must carry one, or
configuration fails naming the library.
yume_check_03_source_layering treats each directory under src/ as a
layer, and each layer names the layers its production sources may include. A
directory that is not declared fails, and so does a source directly under
src/. Tests (*_test.cpp and anything under a tests/ directory) may reach
across layers to build fixtures.
| Layer | May include |
|---|---|
common/ |
no other layer |
ytp/ |
common/ |
circuit/ |
common/, ytp/ |
engine/ |
common/, and ytp/ from session_engine.cpp only |
config/, fs/, stealth/, admission/, gui/ |
common/ |
providers/ |
common/, ytp/, circuit/, engine/, fs/, stealth/, admission/ |
runtime/ |
common/, ytp/, circuit/, engine/, config/, fs/, stealth/, providers/ |
setup/ |
common/, ytp/, config/, fs/, stealth/, providers/, runtime/, and no layer includes it |
abi/ |
common/, config/ and the public header, and engine/, providers/, runtime/ from native_backend.cpp only |
modules/ |
common/, fs/ and BaseFWX |
test_support/ |
common/, ytp/, circuit/, engine/, fs/, stealth/, admission/, providers/, for tests only |
engine/, ytp/, circuit/ and common/ also may not touch Boost, Asio,
OpenSSL, nghttp2, JSON, <filesystem> or sockets, tests included, and only
src/gui/ includes Qt.
Build reachability is checked in CI rather than at configuration.
scripts/check_reachability.py preprocesses every compile command of a Debug
-O0 tree built with one section per function and linked with
--gc-sections, then requires every tracked source to be compiled and every
first-party function to survive in yume, yumed, yume-setup,
yume-doctor, yume-resolver, the echo module, yume-gui or libyume.so.
Code kept on purpose, such as the planned module libraries and test
observers, is listed in config/reachability.json with its reason, and an entry that no longer matches anything fails too.
Where to look
| Task | Start at |
|---|---|
| Wire format, frame flags | ytp/protocol.hpp, YTP/1 |
| Handshake, record protection, rekey | ytp/security.hpp, providers/openssl_security_provider.cpp, engine/session_engine.cpp |
| Browser-shaped TLS/H2 | stealth/, providers/tls13_secure_channel.cpp, providers/h2_duplex_carrier.cpp |
| Front door and cover | providers/h2_web_front_door.cpp, providers/cover_site.cpp |
Starting yume and yumed |
runtime/native_cli.cpp, runtime/native_client_runtime.cpp, runtime/native_server_runtime.cpp |
| Egress policy | runtime/native_egress_policy.cpp, runtime/egress_lists.cpp, providers/asio_direct_route_provider.cpp |
| SOCKS5 and forwards | runtime/native_socks5.cpp, runtime/native_socks5_udp.cpp, runtime/native_forward.cpp |
| Managed TUN | runtime/native_packet_adapter.cpp, runtime/linux_tun_network.cpp, providers/linux_tun_packet_channel.cpp |
| Modules | runtime/module_connector.cpp, debian/yume-daemon.yume-module@.service, MODULES.md |
| Embedding YUME | include/yume/yume.h, ABI.md, abi/stream_probe.c |
| Adding a transport backend | abi/endpoint_backend.hpp |
| Configuration keys | config/v1/config.hpp, config/v1/config.cpp |
| Kits and checks | setup/kit.cpp, setup/cluster.cpp, setup/doctor.cpp |
Tests
C++ unit tests sit next to their source as *_test.cpp and are registered
individually in src/CMakeLists.txt. Integration and system tests live in
tests/. The end-to-end ABI data paths are yume_abi_stream_integration,
yume_abi_packet_integration and yume_abi_route_integration. Each
provisions a real server and client and moves data over a named stream, a
named packet channel or a routed destination.
Documentation tooling
docs/src/en_US/ owns documentation text and publication metadata, and
scripts/yume_doc_spec.py parses it once for Markdown, roff and web renderers.
scripts/yume_docs.py collects and validates all outputs before replacing
changed files. docs/diagrams/*.json owns figure topology and labels, while
@diagram in a document owns placement. Website colors and fonts also drive
standalone SVGs. See the authoring guide.