trystero is a header-only port of Trystero 0.26.0’s serverless WebRTC rooms for C++20, one of the libraries of webcpp. Peers find each other over public Nostr relays and exchange messages directly, over WebRTC data channels. The wire is Trystero’s: a room joined from C++ and the same room joined from Trystero’s JavaScript are one room.
This page teaches trystero section by section: what a room is, how a program joins one in the browser and natively, how its actions carry messages, where its memory comes from, what each backend asks of its platform, the wire it speaks, and what it guarantees. Every program it shows is one of the library’s examples, which the build compiles and links on its target. None is run by the build, since each needs the public relays or a browser, so the page shows no output of theirs. The reference lists every name the headers declare.
Overview
trystero lets programs meet in a room and exchange messages directly, peer to peer, over WebRTC data channels. There is no server of the application’s own: peers find each other by publishing short signed messages on public Nostr relays, exchange the WebRTC offer and answer through them, and from then on talk over the connection the browser or the network stack established.
It ports the protocol of Trystero 0.26.0, and keeps its wire byte for byte. A room joined from C++ and the same room joined from Trystero’s JavaScript are one room: the peers see each other, and an action sent from one side arrives on the other.
The library provides:
-
room, which joins a room of an application over Nostr relays, reports the peers that join and leave, measures round trips, and leaves with Trystero’s farewell; -
action<T>, a named channel of messages: text, JSON (boost::json::value), binary, or any of the three, with optional JSON metadata, of any size, sent inline or in chunks with backpressure; -
one immutable password per app, part of every room’s sealed signaling key and challenge handshake; separate passwords use separate apps and ids;
-
every asynchronous operation as an Asio initiating function, so it completes with a callback,
boost::asio::use_awaitable,boost::asio::deferredor any other completion token; -
an
appper program in one application and password domain, with one identity and one connection per remote peer shared by its rooms; -
a resource for the app and an optional resource for each room, which receive their allocations (Allocation);
-
a backend interface that separates the room from the platform, and its two implementations, for the browser and for native programs.
Two backends
<webcpp/trystero.hpp> is the rooms themselves, which build on both of
trystero’s targets, native and emscripten, and include neither backend.
Each backend is a header of its own:
| Backend | Target | Built on |
|---|---|---|
|
emscripten |
the page’s |
|
native |
libdatachannel for WebRTC, Boost.Beast for the relays’ WebSockets, OpenSSL for TLS and AES-GCM, and Asio’s timers (The native backend) |
Both run the same room, and both meet Trystero JS peers. Every name is in the
namespace webcpp::trystero.
Requirements
-
A C++20 compiler.
-
Boost 1.92: Asio, Container (its
pmrbase class), Core, Hash2, JSON and System; and Beast for the native backend. -
libsecp256k1 0.8.0, with its
schnorrsigandextrakeysmodules, for the BIP-340 signatures of Nostr events: every room needs it. -
In the browser: Emscripten 6.0.11, the version webcpp pins, and a page served from a secure context (https, or http on
localhost), wherecrypto.subtleexists. -
Natively: libdatachannel 0.24.6 and OpenSSL 3, and exceptions (Exceptions).
trystero raises no exception of its own: an operation that can fail completes with an error code. The rooms, the protocol and the browser backend compile without exceptions; so do the native timer, sealer and WebSockets.
Quick start
The two smallest programs that join a room: one for the browser, one native.
Both use an empty app password and join the room lobby of
webcpp-trystero-quick-start, so they
meet each other, and any Trystero JS page that joins the same room,
joinRoom({appId: 'webcpp-trystero-quick-start'}, 'lobby'), meets them
both. Running the examples builds and runs them.
In the browser
A browser program keeps five things alive for as long as its page: an
io_context, the browser::event_loop that drives it from the page’s
JavaScript events, the browser::backend that implements the platform over
the browser’s APIs, the app that owns its identity and shared connections,
and the room joined through that app. Actions are made from the room by name and
payload type:
#include <webcpp/trystero.hpp>
#include <webcpp/trystero/browser/backend.hpp>
#include <webcpp/trystero/browser/src.hpp> // once per program
#include <boost/asio/as_tuple.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/use_awaitable.hpp>
#include <cstdio>
#include <iostream>
#include <optional>
#include <string>
namespace asio = boost::asio;
namespace trystero = webcpp::trystero;
/** The room the page joined, and its actions. */
struct lobby {
trystero::room room;
trystero::action<std::string> chat = room.make_action<std::string>("chat");
trystero::action<std::string> echo = room.make_action<std::string>("echo");
};
/** What the page keeps alive: event loop, backend, app, and the room it joined. */
struct application {
asio::io_context io;
trystero::browser::event_loop loop{io};
trystero::browser::backend backend{loop};
trystero::app app{loop.get_executor(), backend, {.app_id = "webcpp-trystero-quick-start"}};
std::optional<lobby> joined;
/** Joins the lobby and makes its actions; null, with the reason printed, when the app refuses
* (a room of that id joined already, or 64 rooms). */
lobby* join() {
auto result = app.join("lobby");
if (!result) {
const std::string reason = result.error().message();
std::printf("not joined: %s\n", reason.c_str());
return nullptr;
}
return &joined.emplace(std::move(*result));
}
};
<webcpp/trystero/browser/src.hpp> is included in exactly one source file
of the program: it defines the functions the page calls into the library
with.
Handlers are set once the application exists. This program greets every peer
that completes the handshake, prints every chat message, and starts a
coroutine that answers the echo action:
int main() {
auto* app = new application; // a page's program lives as long as the tab
auto* lobby = app->join();
if (lobby == nullptr) {
return 1;
}
lobby->room.on_peer_join([lobby](std::string_view peer) {
trystero::send_options to_peer{.targets = {trystero::peer_id(peer)}, .metadata = {}};
lobby->chat.async_send("hello", std::move(to_peer), [](boost::system::error_code ec) {
if (ec) {
std::cerr << "not delivered: " << ec.message() << '\n';
}
});
});
lobby->chat.on_message([](const std::string& text, const trystero::message_info& info) {
std::cout << info.from << ": " << text << '\n';
});
asio::co_spawn(app->loop.get_executor(), echo_back(lobby->echo), asio::detached);
}
The coroutine awaits messages with async_receive and answers with
async_send. boost::asio::as_tuple turns the error into a value, so
leaving the room ends the loop instead of throwing:
/** Answers every message of the "echo" action, one at a time. */
asio::awaitable<void> echo_back(trystero::action<std::string>& echo) {
for (;;) {
auto [ec, text, info] = co_await echo.async_receive(asio::as_tuple(asio::use_awaitable));
if (ec) {
co_return; // the room was left
}
co_await echo.async_send("echo: " + text,
trystero::send_options{.targets = {info.from}, .metadata = {}},
asio::use_awaitable);
}
}
main returns at once. Nothing blocks in a browser: every JavaScript event
that reaches the library runs its handlers and then whatever became ready on
the io_context (The browser backend).
Natively
The same app and its joined room, natively, whole: a program that greets every peer that joins
and prints what it receives on chat. A native program runs its
io_context itself, since there is no event loop of a page to drive it, and
the native backend posts what libdatachannel’s threads report to it:
#include <webcpp/trystero/app.hpp>
#include <webcpp/trystero/native/backend.hpp>
#include <webcpp/trystero/room.hpp>
#include <boost/asio/io_context.hpp>
#include <iostream>
#include <string>
#include <utility>
namespace trystero = webcpp::trystero;
int main() {
boost::asio::io_context io;
trystero::native::backend backend(io.get_executor());
trystero::app app(io.get_executor(), backend, {.app_id = "webcpp-trystero-quick-start"});
auto joined = app.join("lobby");
if (!joined) {
std::cerr << "not joined: " << joined.error().message() << '\n';
return 1;
}
trystero::room room = std::move(*joined);
auto chat = room.make_action<std::string>("chat");
room.on_peer_join([&](std::string_view peer) {
trystero::send_options to_peer{.targets = {trystero::peer_id(peer)}, .metadata = {}};
chat.async_send("hello from a native peer", std::move(to_peer),
[](boost::system::error_code) {});
});
chat.on_message([](const std::string& text, const trystero::message_info& info) {
// flushed: into a pipe too, since the program runs until it is stopped
std::cout << info.from << ": " << text << '\n' << std::flush;
});
io.run(); // lint-run: the program's network I/O; libdatachannel's threads post to it
}
io.run() returns only when the program is stopped: the room keeps its
relays and its peers for as long as it lives.
Building
trystero is header-only, and is developed inside the webcpp superproject, as a Boost library is developed inside Boost. Its native TLS and WebRTC dependencies are built from pinned sources. Only libsecp256k1 needs an external installation:
| Library | Needed by | Found where |
|---|---|---|
libsecp256k1 0.8.0, with its |
every room: it signs the Nostr events peers meet through |
|
libdatachannel 0.24.6 |
the native backend |
|
OpenSSL 3.5.9 |
the native backend |
|
The libsecp256k1 -s option names a directory with the library’s include/ and lib/, on b2’s
command line; the environment is never read, so that a variable a shell keeps
from another day never replaces .local/ without a word. Without it, the
library is looked for in the directory of .local/ that the table names,
where webcpp’s continuous integration installs it, when that directory holds
the library’s header, and else, natively outside Windows, in /usr/include
or /usr/local/include, where a system install is legitimate. A relative
-s is read from b2’s working directory. A -s that names a directory without the library’s
header stops the build, naming it. A library found nowhere leaves trystero
out: its tests and examples are skipped, the build says so once, naming the
library and where it looked, and every other library builds; with
webcpp-require-external=on, as the continuous integration builds, it stops
the build instead. With MSVC, libsecp256k1 uses CMake’s
libsecp256k1.lib, linked statically with SECP256K1_STATIC defined.
trystero’s build.jam declares that external dependency so:
local secp256k1-root = [ webcpp.external trystero : libsecp256k1
: SECP256K1_ROOT : secp256k1-native : secp256k1.h : native ] ;
local secp256k1-emscripten-root = [ webcpp.external trystero : libsecp256k1
: SECP256K1_EMSCRIPTEN_ROOT : secp256k1-emscripten : secp256k1.h : emscripten ] ;
lib secp256k1 : : <name>secp256k1 <toolset>emscripten
<search>$(secp256k1-emscripten-root)/lib
: : <include>$(secp256k1-emscripten-root)/include ;
lib secp256k1 : : <name>libsecp256k1 <toolset>msvc
<search>$(secp256k1-root)/lib
: : <include>$(secp256k1-root)/include <define>SECP256K1_STATIC ;
lib secp256k1 : : <name>secp256k1 <search>$(secp256k1-root)/lib
: : <include>$(secp256k1-root)/include ;
# Searched libraries are used only through usage requirements.
explicit secp256k1 ;
In a b2 project
A b2 project uses the rooms and the browser backend through
/webcpp/trystero//trystero, which adds trystero’s headers, Boost’s,
libsecp256k1 and Boost.JSON’s definitions, compiled once per variant, and,
on emscripten, embind and BOOST_ASIO_DISABLE_THREADS, since the page has no
threads:
alias trystero : : <conditional>@trystero-dependencies : : <include>include <library>/boost//headers <library>/webcpp//boost_json <library>/webcpp/config//config <library>secp256k1 <toolset>emscripten:<define>BOOST_ASIO_DISABLE_THREADS <toolset>emscripten:<linkflags>-lembind ;
embind is linked as a flag, -lembind, rather than with b2’s own
<embind>on: that feature is not free, and b2 ignores a feature that is not
free among usage requirements, so a program that used it would not link.
A native program uses /webcpp/trystero//native, which adds libdatachannel
and the same source-built OpenSSL used by Asio and Beast:
alias native : : <conditional>@native-dependencies : : <library>trystero <library>/webcpp/third_party//libdatachannel ;
The native backend uses worker threads, so each consumer declares
<threading>multi. Its OpenSSL dependency is the matching threaded variant;
a single-threaded native backend is rejected before compilation.
The native backend’s WebRTC also needs exceptions (Exceptions),
and b2 lets no usage requirement override a build’s
exception-handling=off, so each program and test that uses it declares
<exception-handling>on itself, as the examples do:
webcpp.link native_quick_start : native_quick_start.cpp : <library>/webcpp/trystero//native <threading>multi <exception-handling>on : native ;
and as trystero’s own tests of the native backend’s connections do:
webcpp.run native_peer_connection : native_peer_connection_test.cpp : <library>/webcpp/trystero//native <threading>multi <exception-handling>on : native ; webcpp.run native_room : native_room_test.cpp : <library>/webcpp/trystero//native <threading>multi <exception-handling>on : native ;
A browser program is linked for the web, with a memory that grows:
webcpp.link quick_start : quick_start.cpp : <linkflags>-sENVIRONMENT=web <linkflags>-sALLOW_MEMORY_GROWTH : emscripten ;
The emscripten toolset
The emscripten target is b2’s emscripten toolset, registered against
Emscripten 6.0.11 from emsdk, as a user-config.jam declares it, where
$(emsdk) is the emsdk’s directory and $(node) the node that runs a
program:
local emsdk = /path/to/emsdk ; local node = /path/to/node ; using emscripten : : $(emsdk)/upstream/emscripten/em++ : <nodejs>$(node) ;
With it, libsecp256k1 installed and the pinned source submodules initialized, from the root of a webcpp checkout, these build the tests and the examples, natively and on emscripten:
b2 libs/trystero/test libs/trystero/example
b2 toolset=emscripten libs/trystero/test libs/trystero/example
Preparing the dependencies
libsecp256k1 is built twice: natively, and with Emscripten for the browser. Its small tables keep the browser’s download small:
git clone --branch v0.8.0 https://github.com/bitcoin-core/secp256k1.git
options="-DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF \
-DSECP256K1_ENABLE_MODULE_SCHNORRSIG=ON -DSECP256K1_ENABLE_MODULE_EXTRAKEYS=ON \
-DSECP256K1_ECMULT_WINDOW_SIZE=4 -DSECP256K1_ECMULT_GEN_KB=2 \
-DSECP256K1_BUILD_TESTS=OFF -DSECP256K1_BUILD_EXHAUSTIVE_TESTS=OFF \
-DSECP256K1_BUILD_BENCHMARK=OFF"
cmake -S secp256k1 -B build-secp256k1-native $options \
-DCMAKE_INSTALL_PREFIX=$PWD/webcpp/.local/secp256k1-native
cmake --build build-secp256k1-native && cmake --install build-secp256k1-native
emcmake cmake -S secp256k1 -B build-secp256k1-emscripten $options \
-DCMAKE_INSTALL_PREFIX=$PWD/webcpp/.local/secp256k1-emscripten
cmake --build build-secp256k1-emscripten && cmake --install build-secp256k1-emscripten
where webcpp is the checkout of the superproject: each is installed in the
directory of .local/ where the build looks for it.
Initialize the native dependencies from the superproject root:
git submodule update --init --recursive third_party/libs/openssl third_party/libs/libdatachannel
Install CMake 3.21 or newer, Perl and Make (native Windows Perl and nmake with MSVC). B2 builds OpenSSL, libdatachannel and its pinned nested libjuice, usrsctp and libsrtp sources for the configured compiler, standard library, runtime and build variant. Upstream sources are verified and copied to a disposable build directory; the checkout is never patched. The resulting headers and separate static archives stay in B2’s variant directory, and the final link contains exactly one OpenSSL archive pair.
libdatachannel’s own WebSocket, examples and tests are disabled; media
transport remains enabled. System OpenSSL and installed libdatachannel
binaries are never fallback providers. A combined Asio/Beast/Trystero
application uses the same native <threading>multi configuration for all
three libraries.
Running the examples
The three examples need the public relays or a browser, so the build links
them and never runs them: native_quick_start.cpp, natively;
quick_start.cpp, a .js and a .wasm to embed in a page; and the chat,
a page whose room is C++ (A page and its program). The quick starts
meet in the room lobby of the app webcpp-trystero-quick-start, over the
public relays; the chat joins the app and the room its user types, the room
lobby of the app webcpp-trystero-chat unless the user changes them, and
meets the quick starts when the user types theirs.
The native quick start is built as a program of its own, and runs until it is stopped:
b2 libs/trystero/example//native_quick_start-program
`find bin/libs/trystero/example -name native_quick_start-program -type f`
The chat is a page and its program, which the target site copies into a
directory of its own under the build directory, out of the tree, and prints.
The page is served from localhost, where crypto.subtle exists:
b2 toolset=emscripten libs/trystero/example/chat//site
python3 -m http.server 8000 -d `find bin/libs/trystero/example/chat -name site -type d`
At http://localhost:8000, joining the app webcpp-trystero-quick-start, in
place of the page’s webcpp-trystero-chat, and the room lobby brings the
native quick start in as a peer within seconds: its greeting appears in the
chat, and what the page sends is printed in its terminal. On another toolset, site stops the build, saying that the page is
built on emscripten alone.
Rooms and actions
A room is what Trystero’s joinRoom returns, and an action what its
makeAction returns: this section is the C++ form of both.
Joining
An app is this program in one application and password domain: one peer id,
one immutable password, and one connection
to each remote peer, which every room of the app it shares with that peer
uses, as a Trystero JS page shares them. Constructing it takes the executor
every handler and completion runs on, the backend, which must outlive it,
and its configuration, app_config, moved. A room is joined with join,
which returns a boost::system::result<room> to test, or an error:
error::already_joined while a room of that id is joined in the app, and
error::too_many_rooms when 64 rooms count, since a peer drops a connection
that advertises more. Joining an id in its 99 ms farewell finishes that
farewell first and joins afresh.
join and the app’s other methods run where its executor runs. An app and
all its rooms use that executor; a multithreaded io_context needs a strand.
Destroying or moving over the app handle releases it; its rooms keep the
app’s state alive and continue. Two app objects, even with the same app
id, are two peers. Rooms with different passwords use separate apps with
distinct self ids. A room has no password override; changing the domain
password requires a new app. No registry or identity is kept in static storage.
app_config member |
What it is |
|---|---|
|
The application’s id, Trystero’s |
|
One id for every room of the app; empty, 20 random characters drawn once.
An id longer than 64 bytes is replaced and reported. This id links the
app’s rooms: rooms that must not be linked to one program use an |
|
The immutable password shared by every room of this app, copied into the app’s resource. It contributes to each room’s sealed signaling key and challenge handshake. Empty, the app is passwordless. Different passwords require separate apps and distinct self ids; they share no connections, presence or tokens (What remains). |
|
The Nostr relays. Empty, |
|
The number of default relays to use, 5. |
|
The STUN and TURN servers; empty, |
|
How long a connection with no rooms stays after a departure, 123,333 ms; 0 closes it immediately. A negative value becomes 0 and is reported. A connection whose last binding was kicked closes at once. |
|
One sink for the app and its rooms, called with a category ( |
|
The app’s resource, |
app_config is move-only because its log sink is a handler: pass it
moved, or build it in place. Each room adds room_options:
room_options member |
What it is |
|---|---|
|
How long a binding has to finish the handshake, 10 s. |
|
The most bytes the room reserves for incoming transfers across peers, 256 MiB. |
|
The room’s own resource; null uses the app’s resource. |
Moving from room_config
room(executor, backend, room_config, room_id) and room_config are gone:
a room is joined through an app.
| Previous use | New use |
|---|---|
Standalone room construction |
Construct |
|
|
|
|
|
|
|
|
Configuration reports from |
|
Two rooms with separate peer ids to meet Trystero JS in both |
Two calls to |
Two peers in one process |
Two |
Rooms with different passwords, or rooms that must not be linked to one program |
Separate apps with distinct self ids; set each password in its |
Changing a room’s password by leaving and joining again |
Construct a new app with the new domain password and a distinct self id |
Peers
on_peer_join and on_peer_leave set what is called with each peer whose
handshake completed, and with each joined peer that leaves; peers() lists
the joined peers. on_error sets what is called with each problem with one
peer that the room handled itself, as Trystero’s onPeerError: a peer with
signals under another password, a malformed room proof, a handshake that
timed out, a connection the backend
refused. It is called with an empty peer id for a count of signals
that did not open with the room key (Bounded signaling). Each
handler replaces the one set before, and every view it receives is valid for
the call.
async_ping measures the round trip to a joined peer, from sending
@_ping to receiving its @_pong.
async_leave says goodbye to every peer, Trystero’s @_leave, gives the
farewell 99 ms, as Trystero waits, then detaches its bindings and closes its relay
sockets, and every pending operation completes with error::room_left.
Destroying a room leaves at once, without the 99 ms, and so does assigning
another room over a joined one: every peer is told, and every pending
operation completes with error::room_left, even while an action of it
lives.
A kick or a peer’s @_leave ends only that room’s binding: the app’s other
rooms keep using the connection. When it stays live, rebinding waits one
step after the end, initially the larger of handshake_timeout and 10 s,
plus 1 s, doubling after each further end to at most 5 minutes. A completed
handshake or the connection’s end resets the count. After its own kick the
lower id refuses bindings until that time and keeps frames; the higher id
drops frames until it sends presence, but accepts the lower id’s presence.
A binding also requires the peer to have advertised the token itself.
A peer on a protected app’s shared connection already authenticated with
that app’s password through the initial sealed negotiation. A peer that
knows it can still send a wrong room proof: only the lower id reports that
proof as wrong_password; the higher id answers and sees a departure.
Malformed challenge data is rejected by its receiver too. This room
handshake retains Trystero JS 0.26.0’s one-way wire behavior.
Actions
An action is a named channel of messages, wire compatible with Trystero’s
makeAction. Its name has 1 to 32 UTF-8 bytes and no NUL; with another,
every send fails with error::invalid_action_name. Its type says how
messages travel and how they are received:
| Type | Trystero format | Received as |
|---|---|---|
|
text |
a copy of the bytes |
|
text |
a copy of the bytes, in the room’s resource |
|
text |
a view of the frame it arrived in, valid during the handler |
|
JSON |
parsed, in the room’s JSON storage |
|
binary |
a copy of the bytes |
|
binary |
the transfer’s buffer, moved when resources match, otherwise copied once into the room’s resource |
|
binary |
a view of the frame, valid during the handler |
|
any |
a |
A message of another format than the action’s type is not delivered to it. Several actions of different types may share a name: one receives as views, another sends by moving strings, as the chat does (A page and its program).
Messages arrive at the handler on_message sets, with a message_info: the
sender’s id and the metadata it passed. Without a handler they queue, up to
1024 per action, the oldest dropped first, for async_receive, which needs
an owning type: a view would outlive the message. Frames of an action that
arrived before it was made are kept, 64 per peer for 120 s, and delivered
to it once it is.
async_send sends to send_options::targets, or to every joined peer, with
optional JSON metadata. A payload that fits one 16 KiB frame with its header
and metadata travels inline; a larger one is offered, accepted by the
receiver within 120 s, and sent in chunks of at most 16,370 bytes. A frame
or a chunk is handed to the data channel only while the channel buffers at
most 64 KiB, waiting at most 10 s for it to drain. async_send completes
once each target’s local data channel has taken the inline frame, or the
last chunk of the transfer the receiver accepted, with the first failure if
any, as Trystero’s send resolves: nothing confirms that the receiver has
the message. A receiver takes one transfer per peer at a time, accepts it only for
an action with a receiver, and reserves at most max_receive_bytes across
peers.
Completion tokens and ownership
Every asynchronous operation is an Asio initiating function. With a callback
it calls it on the room’s executor; with boost::asio::use_awaitable it
resumes a coroutine; with boost::asio::deferred it returns an operation
started later.
Because a deferred operation starts after the call, the arguments of an
asynchronous operation are owned: async_send takes its value by value,
moved when the type owns its storage, copied once at the call into the room’s
resource when it is a view, and serialized at once when it is a
boost::json::value; send_options owns its strings; and async_ping
copies the peer id at the call. Everything else that only reads text takes a
std::string_view.
Errors
Operations complete with a boost::system::error_code in the category
webcpp.trystero, of the enumeration error:
| Value | Meaning |
|---|---|
|
the room was left while the operation was pending |
|
the target is not a joined peer |
|
the peer went away before the operation finished |
|
an action’s name is not 1 to 32 UTF-8 bytes without NUL |
|
the metadata does not fit one frame |
|
the receiver refused the transfer, or a message had another format than the awaited type |
|
the receiver neither accepted nor refused within 120 s |
|
the data channel stopped draining for 10 s |
|
a peer’s signals or handshake used another password, or a handshake message had the wrong shape |
|
a connected peer did not finish the handshake in time |
|
reserved: nothing reports it today |
|
the browser or the native stack refused an operation |
|
the app dropped an unusable configuration value and continues without it |
|
a room of that id is already joined in the app |
|
the app already holds 64 rooms, including farewells |
Allocation
A webcpp library lets its user choose where its allocations come from.
trystero does it through std::pmr, header-only, with an app resource and
an optional resource for each room.
The app’s resource and the room’s
-
app_config::memory,std::pmr::get_default_resource()by default, receives the app’s state, configuration copy including its password, self id, registry, shared connections, advertised tokens, kept frames, drain order and presence frames. Peer connections, data channels and received frames use it even while a room negotiates them. The logger, sink and every sentence of the app and its rooms use this resource too. It must be non-null, checked byBOOST_ASSERTat app construction. -
room_options::memory, the app’s resource when null, receives the room’s state, bindings and their drain waiters, negotiation records, rebinding counts and backoffs, sealed texts, relays, actions, queues, outgoing frames, transfers, JSON storage and handlers. The signing context is made there withsecp256k1_context_preallocated_create. -
Adoption replaces negotiation handlers with the app’s; ending a negotiation clears the room’s handlers. Detachment fails and frees a binding’s drain waiters and removes its registrations before returning. After a room ends, the app retains no allocation in that room’s resource.
-
A received frame starts in the app’s resource. A
pmr_bytespayload moves from its message buffer when that buffer and the room use the same resource; otherwise it is copied once into the room’s resource. A chunked transfer assembles its buffer in the room’s resource. Receivedstd::pmr::stringtext is always copied once into the room’s resource. JSON is parsed in the room’s own JSON storage; the app parses no JSON.
All resource access runs on the app’s executor. A resource need not be
thread-safe when that executor runs on one thread; several io_context
threads require a strand.
The allocation test builds every part of app_config in its counted
resource before the counting window, including strings, relay URLs, ICE
servers and log sink; app_config_in and options_in name both resources:
/** An app's configuration, every part of it in `memory`, made before the window. */
trystero::app_config app_config_in(counting_resource& memory, std::string_view self_id,
std::string_view relay, std::size_t& lines) {
return trystero::app_config{
.app_id = std::pmr::string("allocation", &memory),
.self_id = std::pmr::string(self_id, &memory),
.password = std::pmr::string("a password", &memory),
.relay_urls =
std::pmr::vector<std::pmr::string>({std::pmr::string(relay, &memory)}, &memory),
.ice_servers = std::pmr::vector<trystero::backend::ice_server>(&memory),
.log =
{
std::allocator_arg,
std::pmr::polymorphic_allocator<>(&memory),
[&lines](std::string_view /*category*/, std::string_view /*text*/) { ++lines; },
},
.memory = &memory,
};
}
/** A room's options, naming `memory` explicitly. */
trystero::room_options options_in(counting_resource& memory) {
return trystero::room_options{.memory = &memory};
}
How long the resource lives
The app’s resource must outlive its state: the app handle, every room of
it, retained values allocated there and every pending operation. A room’s
own resource must outlive that room, its retained std::pmr::string,
pmr_bytes and boost::json::value values, and its pending operations.
Destroying a handle closes its work but queued completions can still free
memory. Declare both app_config::memory and room_options::memory
resources before the io_context, then construct the backend, app and
rooms. Drain or destroy the io_context before destroying either resource.
In the browser, keep them until all pending promises settle: the page’s
event_loop registrations retain their handlers, so in practice resources
live for the page’s life.
No check in debug builds catches the misuse: trystero holds a raw pointer and cannot see the resource end, and the use comes after the room is gone, from an operation of Asio’s or of the browser’s. A resource that asserts nothing is out when it is destroyed, as the test’s counting resources do, is the check, and it is the user’s.
A room allocates and frees on every event, a frame, a parsed text, a post or
a log line, so a resource that reuses what is freed, a pool such as
std::pmr::unsynchronized_pool_resource, suits a long-lived room; a
std::pmr::monotonic_buffer_resource grows with every event and suits a
short-lived one, or a test.
Completion handlers
A completion handler is the user’s, and is allocated as its own associated
allocator says: the room never binds its allocator over it.
boost::asio::bind_allocator gives one the room’s resource. The allocation
test makes the allocator of a resource:
const auto in = [](counting_resource& memory) {
return std::pmr::polymorphic_allocator<>(&memory);
};
and binds it over the completions of a transfer and of a ping, so that they too are allocated in the sender’s resource:
a.make_action<trystero::pmr_bytes>("file").async_send(
trystero::pmr_bytes(100'000, 7, &a_memory),
boost::asio::bind_allocator(in(a_memory), [](boost::system::error_code /*ec*/) {}));
a.async_ping(z.self_id(),
boost::asio::bind_allocator(
in(a_memory),
[&pings](boost::system::error_code ec,
std::chrono::milliseconds /*rtt*/) { pings += ec ? 0 : 1; }));
When the room erases a completion handler’s type (an
any_completion_handler), it records whether the handler’s associated
executor is the room’s, which only the concrete type tells without
allocating; such a completion runs from an operation the room posts to its
executor, allocated with the handler’s allocator. One bound to another
executor is handed to that executor by Asio, whose type-erased executor wraps
it with std::allocator.
Containers and shared objects
Every container trystero declares is a std::pmr one: std::pmr::string
(and peer_id), std::pmr::vector (and backend::bytes,
protocol::bytes and pmr_bytes), std::pmr::deque, std::pmr::map,
std::pmr::set, std::pmr::unordered_map and std::pmr::unordered_set,
built over their owner’s resource; app_config, room_options, send_options,
message_info and backend::ice_server hold them too. Two types stay the
user’s choice of std::allocator: trystero::bytes, a
std::vector<std::uint8_t>, and trystero::payload, a variant of
std::string, boost::json::value and bytes. A received bytes, or the
binary of a payload, is a copy of the frame, which starts in the app’s
resource.
A struct that holds std::pmr containers takes the room’s
std::pmr::polymorphic_allocator<> in its constructor, and a container of
such structs is given it when it makes one, with try_emplace(key,
allocator). A std::pmr container is never copied without an allocator:
its copy constructor takes the default resource, so a copy names the
resource, std::pmr::string(text, allocator). Moving keeps the resource.
Shared objects are made with std::allocate_shared over
std::pmr::polymorphic_allocator<>, never with std::make_shared,
std::make_unique or new; a timer is a std::shared_ptr for that reason.
Handlers
std::function takes no allocator since C++17, so trystero holds every
handler it keeps in handler<R(Args...)>, a move-only callable that
allocates through a std::pmr::polymorphic_allocator<>. Constructed with
handler(std::allocator_arg, allocator, f), it holds f inline when f
fits in three pointers and is nothrow-movable, and in storage of the
allocator otherwise. It replaces every std::function of the backend
interface (the handlers of its timer, WebSocket, data channel, peer
connection and sealer), of the room and of the log’s sink. Constructed from
f alone it uses the default resource; the app moves app_config::log
into its resource with the allocator-extended move.
A move assignment carries the source’s allocator with its target.
Asio, the backends, Boost.JSON and the protocol
Asio. Every post, dispatch, timer wait and any_completion_handler
the room makes carries boost::asio::bind_allocator over its resource. A
post through boost::asio::any_io_executor is wrapped by Asio in a function
object allocated with std::allocator, from Asio’s per-thread recycling
cache, which calls std::aligned_alloc on a miss (measured: two per post,
in every round of five posts, natively and on emscripten). So the room posts
through the executor of an io_context when its executor is one, where Asio
allocates the operation as the handler’s allocator says; on another
executor, as the browser’s event_loop executor, the wrapper stays Asio’s.
The backends. Each method of backend::factory that makes an object takes
a std::pmr::memory_resource* as its last parameter. Peer connections,
data channels and received frames use the app’s resource; relay sockets,
sealers and room timers use the room’s. Objects and their handlers allocate
through that supplied resource.
Boost.JSON allocates through json_resource, a
boost::container::pmr::memory_resource that forwards to a
std::pmr::memory_resource, header-only: the global resources of
boost::container::pmr are compiled, and webcpp’s CI installs Boost’s headers
alone. The room’s JSON storage is made once, when the room is constructed,
since a value a handler keeps may outlive the room. Text is parsed by a
boost::json::parser over that storage, never by boost::json::parse,
whose temporaries beyond a stack buffer come from the default resource, and
JSON is written by a boost::json::serializer whose stack is the resource.
The protocol (protocol/) takes a std::pmr::memory_resource* as the
last parameter of every function that returns what it allocates, with the
default resource as its default, and returns std::pmr types.
protocol::nostr::verify uses secp256k1_context_static and allocates
nothing.
What stays outside the resource
-
libdatachannel’s and OpenSSL’s own allocations, with the
std::stringandrtc::Descriptionlibdatachannel’s API takes and returns; -
what
emscripten::valholds, in the JavaScript heap; -
the counter of the room’s JSON storage, which Boost.JSON allocates with
operator new, once per room; -
Asio’s own once-per-context allocations: its services and the scheduler’s first operation;
-
the wrapper Asio puts around a post through an executor that is not the executor of an
io_context; -
the native backend’s posts from libdatachannel’s threads, which carry Asio’s default allocator, since the resource is touched only on the room’s executor;
-
Boost.Beast’s and Asio’s state inside the native WebSocket: its stream, its resolver’s results, its coroutines’ frames;
-
the payload types
std::string,bytesandpayload, withstd::allocator, the user’s choice; -
on Emscripten, the C library’s
select(), which Asio’s reactor calls on every wait of anio_contextwith a timer or a socket, and which callocs an array on every call.
The browser’s event_loop and the test’s memory relays are shared by every
room of a page or of a test: they allocate the entries of their
registrations and their relays through the resource they are constructed
with, and a registered callback stays in its own resource, a room’s for a
room’s object.
The proof
test/allocation_test.cpp, on both targets, counts the global
operator new, which it replaces, and the C library’s allocation functions,
where the platform lets a program see them: glibc’s are replaced and forward to
__libc_*, Emscripten’s dlmalloc has weak definitions the test replaces,
and macOS reports each to malloc_logger; MSVC’s are not counted, nor any
under AddressSanitizer, whose own allocator reports to none of these. Only
the thread main runs on is counted.
-
Two rooms over the memory relays exchange a payload, a
std::pmr::string, apmr_bytesand aboost::json::valuein turn, while both counts see none and the counting resources of the two configurations see the rooms’. -
Two rooms with a password, a log sink and the relays’ state in a resource of its own are made, join, exchange a payload that travels in chunks, measure a round trip, one says goodbye, both are destroyed and the
io_contextis drained. From the first construction to the drain,operator newsees exactly the two rooms’ JSON storage counters and the C library nothing. Once the rooms are destroyed their resources still have memory out, the operations they left pending, and none once theio_contexthas drained. -
Two apps with two rooms each exchange binary payloads, one room using the app’s resource and one its own. Size-specific counters prove one payload copy into the distinct room resource, and no extra copy when resources match. From construction through the drain the global counter sees only four JSON storage counters, and every resource ends with nothing out.
The counting resources also count a return of memory they never gave, a
std::pmr object freed through the wrong resource, which must be none.
Asio’s once-per-context allocations are made before anything is counted;
without them the count is 8, not 2. On Emscripten the test also replaces
select() with one that keeps its array on the stack, so that the count
sees the rooms’ allocations rather than the C library’s wait. The test is
declared in test/Jamfile:
webcpp.run allocation : allocation_test.cpp : <toolset>emscripten:<linkflags>-sALLOW_MEMORY_GROWTH ;
The browser backend
In the browser, browser::backend implements the backend interface over the
page’s own WebSocket, RTCPeerConnection, RTCDataChannel, setTimeout
and crypto.subtle, through embind’s emscripten::val, and
browser::event_loop runs the room’s executor from the page’s events. One
of each serves every app and room of the page. Apps with different
passwords use distinct self ids and share no connection or presence.
Driving Asio from the page’s event loop
In a browser nothing may block, and the page’s JavaScript event loop is the
only source of events. An io_context never runs on its own there:
browser::event_loop drives it without ever calling run().
-
Every JavaScript event that reaches the library, a socket’s message, a settled promise, an ICE candidate or a timer, is dispatched to its handler, and is followed by
io_context::poll(), which runs everything that became ready. -
Work given to the loop’s executor,
browser::executor, while no JavaScript event is being handled posts to theio_contextand schedules onepoll()in a microtask. -
The loop holds a work guard on the
io_context, so apoll()never leaves it stopped.
So nothing runs while nothing happens, and a handler runs as soon as its
event does. Two alternatives were rejected: poll() from
emscripten_set_main_loop costs 60 wake-ups a second and stops in hidden
tabs, where requestAnimationFrame does; poll() on a fixed interval costs
wake-ups and up to an interval of latency. The backend’s timers are
setTimeout, so their expiry is a JavaScript event like any other.
How JavaScript reaches C++
The page calls one function, trystero_dispatch, which
<webcpp/trystero/browser/src.hpp> exports. A JavaScript handler is that
function bound to the event loop, a registration and a slot. An object
registers a callback with the loop and removes it when it is destroyed, so a
late event for a destroyed object finds nothing; a destroyed loop marks every
handler it made as dead.
Text that crosses into JavaScript is decoded straight from the WebAssembly
memory a view points into, by a TextDecoder the loop keeps, and text that
comes back is encoded by a TextEncoder straight into a string of the object’s
resource: no std::string is made to carry either.
No JavaScript exception enters WebAssembly
The browser backend makes every call into JavaScript that can throw, a
constructor, JSON.parse, a send, a WebRTC or Web Crypto method, through
one function defined once in browser/src.hpp with EM_JS, which runs it
inside try and returns a value or the exception as a value; the C++ turns
that into the error its interface has. Every event of the page enters C++
through a function of the same file that catches what still escapes, puts
WebAssembly’s stack pointer and the loop’s depth back as they were before the
call, reports it and lets the loop poll again. With exceptions,
browser::dispatch catches a C++ exception first, which frees it: one that
reached JavaScript would keep its object for good. So a program that uses
the browser backend may be built with exceptions or without them, and a
peer’s malformed message never stops the page’s loop.
What a page bounds
-
A data channel message that is not an
ArrayBuffer, as a text message a peer sends, or that is larger than any room frame (protocol::max_room_frame_bytes), is dropped before it is read: neither can be a frame. -
A relay’s text longer than 1 MiB (
protocol::max_relay_message_bytes) is not copied into WebAssembly: the socket closes, and reports the close. -
A text that would take the socket’s
bufferedAmountbeyond 4 MiB (protocol::max_queued_relay_bytes), as when a relay stops reading, drops the socket, which reports one close at once. A page’sWebSocketcannot ping, so the browser has no idle timeout, which the native backend has (The relays). -
A page makes at most
browser::backend::max_peer_connections(256) peer connections across all apps and rooms it serves, counted from their construction until the garbage collector frees them, as Chromium counts them against its own 500 (measured with headless Chrome: the 501st throws "Cannot create so many PeerConnections", and a closed one counts until a collection). Beyond, the rooms report each negotiation they cannot start, and the rest of Chromium’s count stays free for what the page makes without a room.
Peer connections, data channels and received frames use the app’s resource; relay sockets and sealed signaling texts use the room’s. Presence or reuse binds another room without making another peer connection.
What a page needs
-
A secure context: https, or http on
localhostor127.0.0.1. Elsewherecrypto.subtle, which seals the signaling, does not exist, and every sealing operation fails witherror::backend_failure. -
No threads: Emscripten’s threads need a cross-origin isolated page, so trystero uses none.
/webcpp/trystero//trysterodefinesBOOST_ASIO_DISABLE_THREADSon emscripten, and the loop drives Asio on the page’s one thread. -
A program that lives as long as the page: the event loop must outlive every room and object it serves.
-
Boost.JSON’s definitions, which a program links from
/webcpp//boost_json, which/webcpp/trystero//trysterobrings and which builds on emscripten as on every target.
A page and its program
The chat example is a page whose room is C++: the program joins and sends,
and the page only draws. It keeps the loop, the backend and the room for the
page’s life, and makes the action chat twice: once as std::string_view,
to receive a message as a view of its frame, and once as std::string, to
send by moving the text:
struct chat {
boost::asio::io_context io;
trystero::browser::event_loop loop{io};
trystero::browser::backend backend{loop};
std::unique_ptr<trystero::app> app; // made at the first join, with that join's app id
std::unique_ptr<trystero::room> room;
// The action "chat" twice: received as views of the frame, and sent by moving the text.
std::unique_ptr<trystero::action<std::string_view>> messages;
std::unique_ptr<trystero::action<std::string>> outgoing;
};
The page calls join with the app and the room its user typed, and the
program joins and sets its handlers; a received message goes from its frame
straight into a JavaScript string:
void join_room(const std::string& app_id, const std::string& room_id) {
auto& c = the_chat();
if (c.room) {
return;
}
if (!c.app) {
trystero::app_config config;
config.app_id = app_id;
config.log = [](std::string_view category, std::string_view text) {
ui("log", category, text);
};
c.app =
std::make_unique<trystero::app>(c.loop.get_executor(), c.backend, std::move(config));
}
auto joined = c.app->join(room_id);
if (!joined) {
ui("log", std::string_view("chat"),
std::string_view("not joined: " + joined.error().message()));
return;
}
c.room = std::make_unique<trystero::room>(std::move(*joined));
c.messages = std::make_unique<trystero::action<std::string_view>>(
c.room->make_action<std::string_view>("chat"));
c.outgoing =
std::make_unique<trystero::action<std::string>>(c.room->make_action<std::string>("chat"));
c.room->on_peer_join([](std::string_view peer) { ui("peerJoined", peer); });
c.room->on_peer_leave([](std::string_view peer) { ui("peerLeft", peer); });
// A view of the received frame: the text goes from the frame straight into a JavaScript string.
c.messages->on_message([](std::string_view text, const trystero::message_info& info) {
ui("message", std::string_view(info.from), text, false);
});
ui("joined", c.room->self_id(), std::string_view(room_id));
}
send shows the message at once, and reports a failure in the page’s log:
void send_message(std::string text) {
auto& c = the_chat();
if (!c.room || text.empty()) {
return;
}
ui("message", c.room->self_id(), std::string_view(text), true);
c.outgoing->async_send(std::move(text), [](boost::system::error_code ec) {
if (ec) {
ui("log", std::string_view("chat"), std::string_view("not delivered: " + ec.message()));
}
});
}
The two functions are what the program exports to the page, through embind:
EMSCRIPTEN_BINDINGS(chat_example) {
emscripten::function("join", &join_room);
emscripten::function("send", &send_message);
}
Any Trystero JS page, or any other page of the chat, that joins the same app and room is in the same chat (Running the examples).
The native backend
Natively, native::backend implements the backend interface over
libdatachannel for
WebRTC, Boost.Beast for the relays’ WebSockets, OpenSSL for TLS and AES-GCM,
and Asio’s timers. A native room is the same room as a browser’s or Trystero
JS’s: they meet over the same relays and exchange the same frames. One
backend serves every app and room of the program, with separate app
objects and distinct self ids for different passwords, beside the executor it runs
on, and a native program runs that executor’s io_context itself, blocking
on it or polling it (Natively).
Exceptions
trystero raises no exception of its own, but libdatachannel reports errors by
throwing, and the native backend’s data channel and peer connection catch
what it throws on refused input and complete with error::backend_failure.
So they need exceptions: without them, or with WEBCPP_NO_EXCEPTIONS or
WEBCPP_TRYSTERO_NO_EXCEPTIONS defined, native::data_channel,
native::peer_connection and native::backend, which makes their objects,
do not exist, and code that uses them does not compile. The native timer,
sealer and WebSockets, and the rest of trystero, are unchanged.
<webcpp/trystero/config.hpp>, which every header includes first, defines
WEBCPP_TRYSTERO_NO_EXCEPTIONS when webcpp has no exceptions
(WEBCPP_HAS_EXCEPTIONS is not defined: the compiler has none, or the
program defined WEBCPP_NO_EXCEPTIONS); a developer may define it for
trystero alone in a build that has them.
Each program and test that uses the native backend’s WebRTC declares
<exception-handling>on itself, whatever the build asks
(In a b2 project). One function of the backend throws: its
constructor, a boost::system::system_error when OpenSSL cannot make the
TLS context or load the system’s certificate authorities.
Threads
libdatachannel delivers its events on threads of its own. The backend never
runs a handler there: each event copies what it carries and is posted to the
room’s executor, through a weak reference, so an event that arrives after its
object is gone does nothing. Every handler of the room and every completion
runs on the executor, as in the browser. An application that runs the
io_context on several threads gives the app a strand, shared by all its rooms.
A received data channel message is copied once, out of libdatachannel’s
std::vector<std::byte> into the app’s resource, on the executor. A view
handler reads it there. A binary payload moves when its message buffer
and the room use the same resource, and is copied once otherwise; chunked
transfers assemble their buffer in the room’s resource. A text message, as a peer can
send, or a binary one larger than any room frame, is dropped on
libdatachannel’s thread: neither can be a frame.
Valid binary frames that arrive before the message handler is installed wait in a FIFO in the app’s resource. The queue holds at most 64 frames for at most 10 seconds from its first frame. Overflow or expiry closes the channel; closing or destroying it discards the pending frames. The existing native timer invalidates an expiry already queued when the queue drains or closes. Installing a receiver posts delivery on the executor with the app’s allocator. It never calls the receiver inline, and delivery checks the current handler and channel before each frame, so a handler may replace itself or close the channel. A message already queued ahead of that delivery drains older frames first when a receiver is available.
If a transport closes during adoption, before the room obtains a binding, the room forgets that negotiation and restarts announcements. Its later close event cannot leave signaling marked connected with no binding.
What the backend asks of libdatachannel
-
Automatic negotiation is off, so an offer or an answer exists only when the signaling asks for one, as in the browser.
-
Every object libdatachannel hands out is held by the backend: libdatachannel keeps only weak references.
-
Local candidates leave as the JSON of an
RTCIceCandidateInit, the form Trystero JS adds withaddIceCandidate. -
The
rtc::PeerConnectionobject and its control block are made in the app’s resource; what that object allocates inside libdatachannel is libdatachannel’s (What stays outside the resource).
Its configuration
native::backend_config is what the backend makes its peer connections
with, beside the app’s ice_servers:
| Member | What it is |
|---|---|
|
The local address every peer connection binds to, as |
|
The most peer connections the backend makes in any minute, for every room in every app it serves together, 256: beyond, it makes none, and a room reports each negotiation it cannot start. A room makes at most 64 in any 57 s however its peers behave (Bounded signaling), so the default lets four rooms do so at once. A connection still counts against its negotiating room’s 57 s window after adoption by the app; presence and reuse make none. |
|
What the relays’ WebSockets tolerate of a relay that stops reading or
answering, |
The relays
The relays are reached over Boost.Beast: ws:// in the clear, wss:// over
TLS with SNI and host name verification. The backend verifies relays against
the system’s certificate authorities; tls_context() gives the
boost::asio::ssl::context to an application with authorities of its own. A
relay whose certificate does not verify is unreachable, and the room retries
it later as it would any other. The WebSockets are Beast’s rather than
libdatachannel’s own, so the relays run on the room’s executor like
everything else.
A relay is a third party, and the backend bounds what one can make the room hold:
-
A text longer than 1 MiB (
protocol::max_relay_message_bytes) closes the connection before it is copied, as in the browser. -
A relay that stops reading is dropped once the text held unsent for it would pass 4 MiB (
websocket_limits::max_queued_bytes, which isprotocol::max_queued_relay_byteson both backends). -
A relay that stops answering is dropped once
idle_timeout(30 s) passes with its keep-alive ping unanswered: halfway through, the connection sends a ping. The timeout counts bytes received, and a ping waits for the write in flight, so a relay that reads under about 2 KB/s is closed during a 64 KiB text. Signaling texts are 2 to 4 KB, so a real room meets it only with a relay slower than about 200 B/s.
Each drop reports one close, and the room reconnects with its backoff
(Discovery over Nostr). A page has no idle timeout: its
WebSocket cannot ping (What a page bounds).
The protocol
A room keeps Trystero 0.26.0’s wire byte for byte. The protocol’s pure
functions are in <webcpp/trystero/protocol/>; their tests compare them,
byte for byte, with values Trystero JS computed itself
(Testing). What a Trystero JS peer or a page sees never changes:
the topic prefixes Trystero@ and Trystero:, the handshake’s
__trystero_pw, every constant and message layout, and the default relays
in their order.
Discovery over Nostr
| Part | What it is |
|---|---|
Root topic |
Trystero’s |
Peer topic |
the same of |
Event |
kind 20000 plus the sum of the topic’s UTF-16 code units modulo 10000,
ephemeral; the topic in an |
Subscription |
one per relay, to the root topic and the peer’s own topic |
Announcement |
|
Relays |
the configured ones, or 5 of Trystero’s 29 defaults, shuffled with
|
A relay that answers rate-limited: is skipped for 60 s, doubling to 15
minutes while the replies persist; one that answers blocked:,
restricted:, auth-required: or pow: is retired. A lost relay is
reconnected after a random share of 3.3 s, doubling to 60 s, and the delay
comes back to 3.3 s only once a connection stayed open for 60 s, so that a
relay that closes the room at once after each open is tried less and less
often.
Signaling
Of two peers, the one with the lower id offers, on the relay that carried the
other’s announcement. The higher one answers an announcement by announcing
itself on the other’s peer topic, so the lower one offers: at most once a
second per peer, and once in 5.3 s per relay for the whole room, Trystero’s
announce interval, so that a flood of low ids does not have the relays
rate-limit the room. Offers, answers and candidates are sealed with AES-GCM
under the SHA-256 of <password>:<app>:<room>, using the immutable
app_config::password, empty for a passwordless app, with a 16-byte IV, as the text <iv>$<ciphertext>: the IV’s bytes
in decimal joined by commas, and the ciphertext with its tag in base64.
Candidates follow their offer or answer, trickled. A negotiation ends when
the data channel opens: the app adopts the connection, and signals from a
bound peer are ignored. A live connection is reused for another room only
after the peer advertised that room’s token itself on the connection.
The data channel
Every message is a room frame: 1, the length of the room token (16 bits,
big-endian), the token, the hexadecimal SHA-256 of Trystero:<app>:<room>,
then an action frame. A presence frame, 2, present or not, the token’s
length and the token, announces or withdraws the room.
Presence advertises every room of the app on each connection. It records which rooms the remote peer joined and binds matching local rooms; a withdrawal detaches only that room. The connection routes by token:
| Frame | What happens |
|---|---|
An envelope that does not decode |
The connection ends for every room. |
|
Record it; a 65th distinct token ends the connection. Bind the matching local room when its binding conditions permit. |
|
Bind the matching unbound local room when permitted. |
|
Forget the token and its kept frames, and end its binding as a departure. |
Room frame, token bound |
Deliver it to that room’s action wire. |
Room frame after the higher id’s own kick, before it sends presence |
Drop it: it belongs to the ended binding. |
Room frame for another room of the app, not bound to this connection |
Keep it, at most 64 frames per connection, all expiring 10 s after the first. Bind only when the peer advertised the token and no backoff runs. |
Room frame for any other token |
Drop it silently; keep the connection. |
On binding, kept frames arrive before later frames; a kept @_leave
discards itself and everything kept before it for that token. There is no
buffer for a bound room awaiting its handler: delivery is installed in the
same call that makes its binding.
Waiting sends take turns by binding on each channel drain, preserving each binding’s waiter order and the 10 s stall bound. Chunked transfers send one chunk per drain, giving waiting sends from other rooms a turn. Inline sends at a buffered amount of at most 64 KiB go straight to the channel: they have no trystero rate bound, so a flood of pings can delay every room’s traffic to that peer, and that peer only.
Action frames are Trystero’s action wire, version 2: an inline frame carries
the payload whole; a larger payload is an offer, accepted or refused by the
receiver, then chunks of at most 16,370 bytes, in order. Integers and
float64 values are big-endian, as JavaScript’s DataView writes them.
| Frame | Bytes |
|---|---|
inline (kind 0) |
2, the kind and the format shifted by four, the name (32 bytes, NUL padded), the metadata’s length (uint16), the metadata, the payload |
offer (kind 1) |
the inline header, the transfer’s id (uint32), the payload’s size (float64), the metadata |
chunk (kind 2) |
2, 2, the transfer’s id (uint32), the offset (float64), the bytes |
accepted (kind 3) |
2, 3, the transfer’s id (uint32) |
rejected (kind 4) |
2, 4, the transfer’s id (uint32), the reason (UTF-8, at most 200 bytes) |
cancelled (kind 5) |
2, 5, the transfer’s id (uint32) |
The payload’s format is text (0), JSON (1) or binary (2). Internal actions run the room:
| Action | What it carries |
|---|---|
|
the password handshake: the lower id sends a challenge, and the other answers with the hexadecimal SHA-256 of |
|
the sender’s side of the handshake passed; a peer joins when both sides sent it |
|
round trips |
|
the sender leaves the room |
The media actions, @_stream, @_track and @_signal, are not ported
(Differences from Trystero): a frame of one is ignored.
Bounded signaling
Announcements are not sealed, so anyone who knows the app and room ids can announce any number of peer ids, and anyone who reads the relays can send any signal in any peer’s name. Trystero JS bounds none of this; trystero bounds what each costs a room.
-
Nothing a peer sends changes a negotiation before it opens with the room key: an offer, an answer or a candidate that does not open is counted, reported once per negotiation, and changes nothing else. A flood of fresh ids makes each a negotiation, so the reports are paced: the first of a quiet 10 s goes to
on_errorat once, with its peer, and the others of those 10 s in one report, with no peer and their number. -
A room holds at most
detail::signaling::max_unconnected_peers(32) negotiations with peers it is not connected to. A negotiation whose offer and answer expire without a connection is dropped, an idle one gives its place to a new peer, and a new peer beyond the bound is refused and logged once. -
A room makes at most
max_connections_per_window(64) connections in any 57 s, however soon they end, and a page at most 256 (What a page bounds); the native backend makes at most 256 a minute (Its configuration). -
A signal’s fields are bounded in
protocol/signal.hpp: the peer id (64 bytes; Trystero’s are 20), the offer id (64 bytes), a sealed offer or answer (64 KiB) and a sealed candidate (16 KiB, above the 8192 bytes of plaintext Trystero takes). -
A relay is a third party too: its texts are bounded at 1 MiB, what the room holds unsent for it at 4 MiB, and, natively, its silence at 30 s (The relays).
| Bound | Scope | Value and consequence |
|---|---|---|
Joined rooms |
App |
64, counting farewells until withdrawal; |
Advertised room tokens |
Connection |
64 distinct tokens; a 65th ends the connection. |
Kept frames for unbound local rooms |
Connection |
64, at most 64 x 16,515 bytes; expire together 10 s after the first. Other tokens retain nothing. |
Room frame and token |
Connection |
16,515 bytes, token 1 to 128 bytes; an invalid envelope ends the connection. |
No bindings |
Connection |
|
Handshake |
Room and peer |
|
Earlier challenges |
Room and peer |
Last 8 since the last completed handshake; a correct answer to any passes. |
Pending action frames |
Room and peer |
64 for 120 s; one incoming transfer per peer, |
After a kick or the peer’s @_leave, a live connection is rebound one step
after that binding ended. The initial step is the larger of
handshake_timeout and 10 s, plus 1 s (11 s by default), doubling at each
further end to 5 minutes. Only a completed handshake or the connection’s
end resets it. After its own kicks the lower id refuses bindings until its
backoff ends, ignores signals and keeps frames; the higher id drops frames
until it next sends presence and accepts the lower id’s presence at once.
The room sends presence again on reuse or scheduled rebinding, making the
other side bind without a relay response. Announcements restart only when
no live connection remains. A forged announcement never binds a peer that
has not advertised that token itself.
A connection counts against its negotiating room’s 64-per-57-s window even
after the app adopts it; presence and reuse make no connections. Fresh ids
that connect and depart keep at most 64 x ceil(idle_timeout / 57 s) idle
connections per room, 192 by default, within the backend’s own bound.
Leaving a room with many peers retains no more connections than the app
already held. Kept frames and tokens cost at most about 1 MiB and 8 KiB
per connection; each of at most 64 bound rooms retains its own peer bounds.
A flood spends its room’s queues, transfers and reservations; routing never
holds one room’s frame for another room.
With the lower id an invalid room response proof causes at most six kicks and reports in
the first ten minutes, then one per 5 minutes. The higher id can be rebound
by a lower-id peer at that peer’s pace, but reports at most one handshake
timeout per handshake_timeout, and does not verify a response proof.
Malformed challenges are rejected by their receiver and can also report
wrong_password; their report rate follows the malicious peer’s repeated
bindings, rather than the timeout bound.
Replayed offers and answers
A sealed offer or answer holds neither its offer id nor a peer id
(Trystero’s wire), so one recorded from the relays still opens when anyone
resends it in its sender’s name, and so does the room’s own, sent back to it.
A room remembers, by the SHA-256 of their opened text, the last 1024 offers
and answers it made or acted on
(detail::signaling::remembered_descriptions), and refuses one that comes to
it as a peer’s: it changes nothing and is logged. It makes or acts on at most
two per connection it makes, so each is remembered for at least an offer’s
lifetime (57 s), and in a quiet room for its whole life. So a room refuses a
sealed offer or answer it acted on, resent, and one it made itself, sent back
as a peer’s offer or answer.
What remains
Sharing preserves Trystero JS 0.26.0’s wire. Each C++ app is one password domain: it copies one password at construction, and all its rooms use it. The initial sealed signaling authenticates that password before the data channel is adopted. Later room binding reuses this authenticated connection. There are no per-room overrides, and different passwords require separate apps with distinct self ids. This protects admission to the C++ app’s rooms without changing the room handshake on the wire.
A watcher not connected to the app, without the key, can keep two peers apart. A sealed offer or answer the room neither made nor acted on still opens and is acted on. Such texts are not scarce: every peer seals a fresh offer for any id announced above its own, and announcements need no key. So anyone who reads and publishes on the relays, without the key, can keep any two peers of a room apart from their first meeting for as long as it keeps at it, each attempt costing the other peer a connection of its window. A watcher not connected to the app cannot read or alter anything the peers exchange, join the room, or touch peers already joined; a joined peer that drops is a new peer again. The room refuses only what it made or already acted on. Only a wire that seals the offer id and both peer ids closes this, which Trystero would have to share (reported upstream).
A flooder can delay new peers. Joined peers, memory and connections stay safe under a flood, but a flooder who keeps sending new ids takes each place, or each connection of the window, as it frees, so new peers can be kept out for as long as the flood lasts, each place up to one offer’s lifetime (57 s) at a time. A joined peer whose connection drops comes back as a new peer, with the same id, and is kept out the same way: the room has no renegotiation that would keep its place. Closing it needs announcements that prove the room key, a wire change Trystero would have to share.
A passwordless app’s rooms are open to its connected peers. Presence
gives a peer connected through any room the token of every room of that
app. Relay events reveal their topics in the clear, and announcements
reveal the app’s peer id. The token alone binds the existing connection,
and the empty app password goes straight to @_hsready, so that peer can
join another room without knowing its id. A watcher not connected to the
app still needs the room id to derive its topics and signaling key.
Protected rooms belong to another app with a password and a distinct self
id. Its presence, tokens and connections never reach the passwordless app.
The upstream one-way handshake remains. The responder to a room challenge answers and passes its side; only the lower id verifies the password proof. A Trystero JS page that itself mixes different passwords on one app’s shared connection can therefore admit a peer to its own higher-id room without that room’s password, and can expose a challenge response for offline guessing. The C++ app-domain boundary does not repair such a JS page. Configure all rooms of one JS app with one password, and use separate page contexts for different password domains. The wire has not gained bilateral room proofs.
A domain member can send an invalid room proof. A peer already connected to a protected C++ app knows its domain password from the initial sealed negotiation. It can nevertheless answer a later room’s challenge incorrectly or send malformed handshake data. The room kicks that binding and applies the documented backoff; only the lower id checks and reports a wrong response. These checks isolate faulty peers within an authenticated domain, rather than admitting another password domain.
One identity links the app’s rooms. Each room announces the same
peerId in clear, and connected peers see it across shared rooms. Relay
observers and peers can link those rooms to one program. Separate apps
provide separate identities and connections for rooms that must remain
unlinked. A forged announcement in a connected peer’s name binds nothing:
only the peer itself, on its encrypted connection, can advertise the token.
It causes no report or rebinding count against that innocent peer.
Two transient cases remain with a Trystero JS lower id. If it binds by reuse during the higher C++ id’s dropping window, its challenge is dropped and its handshake can time out once. With the default 10 s timeout the next rebinding joins by at most 43 s after the first kick plus one-way delay, with no false password error on either side. If a JS page’s timer that expires a stale answer runs later than the 1 s margin before presence arrives, JS can flush the old answer into a new handshake and report a false password error. One-minute timer lag can repeat this at the 11, 22 and 44 s steps, at most three times, before the 88 s step exceeds that lag. The C++ side reports nothing false in either case.
Design
Layers
| Layer | Headers | Knows |
|---|---|---|
Protocol |
|
pure functions over bytes and text: no I/O, no platform |
Backend interface |
|
the WebSocket, peer connection, data channel, timer and AES-GCM sealer a platform provides |
App |
|
one identity, the room registry, shared connections and their routing |
Room |
|
the Nostr strategy, signaling, bindings, action wire and room API |
Browser backend |
|
|
Native backend |
|
libdatachannel, Boost.Beast, OpenSSL and Asio’s timers |
Everything above the backend interface is the core, which builds on both targets: the same room runs over the browser’s APIs, over libdatachannel, and over the memory relays of the tests (Testing). The core never includes a backend, libdatachannel, OpenSSL or Emscripten: a boundary of the library’s own, which webcpp’s lint checks, keeps it so.
Connections shared by the rooms of an app
| Object | Owns | Lives | Resource |
|---|---|---|---|
|
A reference to |
While the user keeps its handle |
None |
|
Configuration and immutable password, self id, logger, room registry by token and id, connections and user count |
While any shared reference lives; its last user closes its connections |
App’s |
|
One peer’s connection and channel, advertised tokens, kept frames, idle timer and plain pointers to bindings and their drain order |
From adoption until its end |
App’s |
|
One room’s use of a connection: a weak reference, drain waiters and delivery to its room |
Until detachment, owned by its room’s peer state |
Room’s |
|
A reference to |
While the user keeps its handle; destruction leaves |
None |
|
Signaling, strategy, actions, peers, bindings, handlers, rebinding counts, backoffs and a shared reference to the app |
Until its state and pending operations end |
Room’s, the app’s by default |
-
A room holds the app strongly; the app reaches rooms weakly. A binding holds its connection weakly, and the connection holds plain pointers to bindings. Detachment removes those pointers and every drain registration before returning. Ending a room removes its registry entry, so the app keeps no allocation of that room’s resource after it ends, and no cycle keeps the objects alive.
-
Sharing is scoped by the
appthe user constructs, with no registry outside it and no static state keyed by the backend. Two apps with the same application id in one process are two peers. Each app has one immutable password. Different passwords use separate apps with distinct self ids, so their connections, tokens and presence are never reused. -
The action wire continues to write its room token and send through a
channel_link. The binding implements that link: each room keeps its own actions, transfers, queues and bounds.
Why abstract classes for the backend
The backend is a handful of abstract classes rather than template
parameters. Each of its operations is a call into JavaScript or into a
network stack, so a virtual call costs nothing measurable, while the room’s
code stays free of templates over the platform and of #ifdef, and the test
backend and the browser backend are interchangeable at run time. Every
handler a backend invokes runs on the room’s executor, after the call that
caused it has returned, never inside it: the room calls these operations from
inside handlers whose state a completion run there would change under them.
Why the browser’s own WebRTC
libdatachannel’s WebAssembly port, datachannel-wasm, has no media and hides
the RTCPeerConnection it wraps. Binding the browser’s objects directly
through emscripten::val is smaller than that wrapper, reports every error,
and leaves the browser’s whole WebRTC stack (capture, codecs, playout) within
reach for media.
Text is a view, a copy is a decision
A parameter that only reads text is a std::string_view, bytes are a
std::span, and a string is used where something must own the text: a map
key, a member, a capture of a handler that runs later.
-
The digests the protocol takes are fed the parts of their input: nothing is concatenated to hash it.
-
Signals and relay messages are read as views into the parsed document; the JSON the room writes goes into one reserved buffer, escaped by Boost.JSON’s serializer, and a Nostr event’s id is hashed over those same bytes.
-
A frame is written once, inside its room envelope, and shared by every target; a received frame becomes the buffer of the message delivered from it, and a view handler reads it in place.
-
A sentence of the log is composed only when there is a sink.
The asynchronous operations are the exception, for the reason Completion tokens and ownership gives.
Optional native dependencies
libdatachannel is under the MPL 2.0 and OpenSSL under the Apache License
2.0. Both are needed only by the native backend, which a browser program
never includes: /webcpp/trystero//trystero depends on neither, and
/webcpp/trystero//native adds them, as OpenSSL is optional for Asio.
Licensing
The library is distributed under the Boost Software License. It ports Trystero’s protocol and was written with Trystero’s source open, so Trystero’s MIT notice comes with it (Acknowledgements).
Guarantees
What trystero promises, and the test that holds each promise, which fails
when the promise breaks. A guarantee keeps its number, and the anchor named
after it, so a comment of the code or of a test cites it by that anchor:
(doc: #trystero-invariant-3) sends its reader to the third.
b2 libs/trystero/doc, which builds this page, fails on a citation that
names no anchor of it. The tests are those of Testing.
-
The wire is Trystero 0.26.0’s. A C++ peer, in the browser or native, and Trystero JS peers in one room join, exchange text, JSON and binary messages, files both ways with metadata, and pings, leave, and form a mesh, with either side offering. Held by the
interoplane,interop_browseron emscripten andinterop_nativenatively, against Trystero 0.26.0 itself in headless Chrome. -
Every protocol value equals Trystero JS’s. Topics, room tokens, the relay shuffle, sealed text, Nostr event ids, inline and chunked action frames, control frames, envelopes and presence are what Trystero’s own functions compute from the same inputs, byte for byte. Held by
protocol, against the values theoraclelane has Trystero 0.26.0 write, and by theoraclelane, which fails when Trystero would write other values than those committed; and bynative_sealer, which opens what Trystero JS sealed with Web Crypto. -
A peer with another password never joins a protected C++ app’s rooms. Its initial signals do not open under the app’s password, so it obtains no shared connection into that domain. Every room of that app uses the same immutable password. This promise concerns the C++ app’s admission; an upstream JS page that mixes passwords retains its one-way handshake exposure (What remains). Held by
room(the_same_password_joins_and_another_does_not),native_room(another_password_never_joins), and password-domain tests ofsharedandinterop. -
A payload larger than a frame travels in chunks, both ways. It is offered, accepted and sent in chunks of at most 16,370 bytes, and arrives whole, from either peer. Held by
room(large_payloads_travel_in_chunks_both_ways, 100 KB each way), and the file transfers of theinteroplane. -
Every allocation of an app and its rooms goes through their resources, but what What stays outside the resource lists: from its construction until its
io_contexthas drained, the globaloperator newsees only the room’s JSON storage counter, and the C library nothing. Held byallocation, natively and on emscripten. -
No unit test reaches beyond the loopback. The rooms of the unit tests meet over the memory relays, the native tests’ peer connections bind to 127.0.0.1 and gather their candidates there alone, and the WebSocket test talks to a server of its own on 127.0.0.1. Held by the container lanes,
gcc-14andclang-linux-18: every unit test, built with GCC 14 and with clang 18 againstlibstdc++, passes in a Linux container that has no network but the loopback (docker runwith the networknone). -
A room refuses a replayed or reflected offer or answer. A sealed offer or answer it made, or acted on, that comes back to it as a peer’s changes nothing (Replayed offers and answers). Held by
room(a_replayed_answer_is_refused,a_replayed_offer_is_refused,a_reflected_offer_is_refused_as_an_answer,a_reflected_offer_is_refused_as_an_offeranda_reflected_answer_is_refused_as_an_answer), and the replay and reflection scenarios of theinteroplane. -
A relay that stops reading is dropped. Once the text held unsent for it would pass 4 MiB, the connection is dropped, and reports one close, after which the room reconnects with its backoff, as it does to any relay that closes. Held by
native_websocket(a_relay_that_stops_reading_is_dropped_at_the_queue_bound, a close before 2000 texts of 64 KiB, and one only) natively, andbrowser_websocket_boundon emscripten, with exceptions and without them, which also checks that the socket never holds more than 4 MiB; the reconnection byroom(a_relay_that_closes_at_once_is_tried_less_and_less_often). -
One connection per remote peer per app is shared by its rooms. Each room keeps its own membership and actions on that connection. Held by
shared, cases 1 to 3, and the interop’s first shared scenario. -
A room’s departure leaves the app’s other rooms joined. They continue sending over the same connection. Held by
shared, case 8, and shared interop scenario 5, with either side leaving. -
Different passwords share no C++ app connection or presence. Protected rooms use a separate app with a distinct self id; a peer of a passwordless app gains no binding to that protected app by echoing tokens. Held by the password-domain cases of
sharedand shared interop scenario 4.
Testing
Testing a program that uses trystero
A room needs relays and peers, and a test needs neither the network nor
another machine. The backend interface is the seam: a program’s tests run its
rooms over a backend of their own, in memory, as trystero’s own tests run
whole rooms over test/support/memory_backend.hpp, a backend whose Nostr
relays forward ephemeral events to matching subscriptions, whose peer
connections pair through their descriptions, whose data channels deliver in
order, and whose sealer opens only what was sealed under the same key, each
event posted to the io_context. It is in the namespace
webcpp::trystero::test, outside the library’s public headers, and a
program may copy it.
The memory network identifies connection ends with std::size_t ids.
on_made records construction and ICE servers, and peer_of identifies the
paired end. A filter delivers, drops or holds frames; a held frame holds
all later frames from its sending end behind it, preserving channel order
until release. Deferred open calls open each end separately; inject
and fail supply malformed frames and transport failure.
With channel_capacity enabled, the channel queues bytes and drains up to
drain_per_post per post. It delivers a frame only after its last byte
drains, and buffered reports the exact unsent amount. Set virtual_clock
before making timers: advance fires due timers in order and runs ready
work between them; step and poll_ready give tests finer control. Timeouts,
backoffs and late frames are tested without wall-clock sleeps. Optional
fault hooks allocate outside the measured resources only when enabled;
the allocation test leaves them disabled.
A test of the native backend’s own WebRTC binds its connections to the
loopback, native::backend_config::bind_address set to "127.0.0.1", and
names a STUN server where nothing answers, stun:127.0.0.1:9, in place of
Trystero’s public ones: the loopback’s host candidate is then the only one,
and it is enough.
How trystero is tested
trystero’s tests are under test/, each declared in test/Jamfile, and each
guarantee of Guarantees names the tests that hold it. They run
on two targets: 75 tests in all, 54 natively and
60 on emscripten, under node. The protocol, the rooms and
the allocation test run on both:
webcpp.run protocol : protocol_test.cpp : <testing.input-file>fixtures/expected/expected.json ; webcpp.run room : room_test.cpp ; # Rooms of one app that share one connection per remote peer; once as the # build asks, once without exceptions, on both targets; and, natively, once # with the standard libraries' checks, so that a kept frame that kicks during # a binding, or a waiter failed during a connection's end, fails at once when # it touches what it removed. webcpp.run shared : shared_test.cpp ; webcpp.run shared_no_exceptions : shared_test.cpp : <exception-handling>off ; webcpp.run shared_hardened : shared_test.cpp : <define>_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE <define>_GLIBCXX_ASSERTIONS : native ; # The memory backend's hooks, which the tests of shared connections drive. webcpp.run memory_backend : memory_backend_test.cpp ; # The shared connection alone, over a memory pair. webcpp.run connection : connection_test.cpp ; # The state an app and its rooms share, alone, over the memory backend. webcpp.run app_impl : app_impl_test.cpp ; webcpp.run allocation : allocation_test.cpp : <toolset>emscripten:<linkflags>-sALLOW_MEMORY_GROWTH ;
| Test | What it checks |
|---|---|
the public headers |
Each compiled alone, on each target that builds it: the core on both, the native backend natively, against libdatachannel and OpenSSL, and the browser backend on emscripten. |
|
|
|
The protocol against values Trystero JS 0.26.0 computed itself: topics, room tokens, the relay shuffle, sealed text, Nostr event ids, inline and chunked action frames, control frames, envelopes and presence, and the bounds of a signal’s fields. |
|
Whole rooms in one process, over the memory relays: every payload format, 100 KB transfers both ways, ping, passwords right and wrong, leaving and returning, a room moved over a joined one, ICE servers checked as WebRTC checks them, floods of announcements and of forged, replayed and reflected signals, a relay that closes at once or rate-limits, a three-peer mesh, coroutines and view handlers. |
|
Configuration copying and validation, resource ownership, weak room registration, shared adoption, stale replacement, reentrant adoption, last-user closure and quiet destruction with pending work. |
|
Shared transport routing, advertised token and kept-frame limits, binding detachment, departures and kicks, idle expiry and drain ordering. |
|
Virtual timer ordering, cancellation, held-frame order, deferred opens, injection and failures, and byte-accurate buffering and drain. |
|
Multiple apps and rooms with one connection per remote peer, adoption races, presence and reuse, room isolation, departures, idle connections, password-domain isolation, invalid room proofs, transient handshake recovery, fairness, bounds and resource ownership. The suite also runs without exceptions and with standard-library hardening; sanitizers check lifetimes and detachment. |
|
Every allocation of two apps and their rooms goes through their resources (The proof). |
|
The native backend, part by part, natively: the sealer opens what Trystero JS sealed with Web Crypto; the WebSocket, against a Beast server in the same process, opens, sends in order, closes once, and drops a relay that sends too much, stops reading or stops answering, while one that answers its pings stays; the timer never fires once cancelled, restarted or destroyed. |
|
Two libdatachannel connections wired to each other, each handler on the executor, and whole native rooms over real libdatachannel connections, bound to 127.0.0.1. A controlled executor holds real channel callbacks to prove pre-handler FIFO delivery, a 65th message ahead of the posted flush, handler replacement and destruction, the 64-frame and 10-second bounds, and allocation and release through the channel’s resource. |
|
The browser backend, on emscripten under node, which has the page’s WebSocket, Web Crypto and microtasks but no WebRTC: the loop and its recovery from exceptions that escape a callback, the WebSocket’s URLs, its bound on what a relay holds unsent and its silence once dropped, the sealer’s completions, a data channel’s refusal of what is no frame, and the page’s peer connections, made or refused, and their census. |
The native backend’s connections need exceptions whatever the build asks (Exceptions), and each browser test is also built without exceptions, as a program that uses the browser backend may be:
webcpp.run browser_event_loop : browser_event_loop_test.cpp : : emscripten ; webcpp.run browser_websocket : browser_websocket_test.cpp : : emscripten ; webcpp.run browser_websocket_bound : browser_websocket_bound_test.cpp : : emscripten ; webcpp.run browser_websocket_late_events : browser_websocket_late_events_test.cpp : : emscripten ; webcpp.run browser_sealer : browser_sealer_test.cpp : : emscripten ; webcpp.run browser_data_channel : browser_data_channel_test.cpp : : emscripten ; webcpp.run browser_peer_connection : browser_peer_connection_test.cpp : : emscripten ; webcpp.run browser_event_loop_no_exceptions : browser_event_loop_test.cpp : <exception-handling>off : emscripten ; webcpp.run browser_websocket_no_exceptions : browser_websocket_test.cpp : <exception-handling>off : emscripten ; webcpp.run browser_websocket_bound_no_exceptions : browser_websocket_bound_test.cpp : <exception-handling>off : emscripten ; webcpp.run browser_websocket_late_events_no_exceptions : browser_websocket_late_events_test.cpp : <exception-handling>off : emscripten ; webcpp.run browser_sealer_no_exceptions : browser_sealer_test.cpp : <exception-handling>off : emscripten ; webcpp.run browser_data_channel_no_exceptions : browser_data_channel_test.cpp : <exception-handling>off : emscripten ; webcpp.run browser_peer_connection_no_exceptions : browser_peer_connection_test.cpp : <exception-handling>off : emscripten ;
From the root of a webcpp checkout, with the three libraries of Building found, each of these builds and runs the tests, natively, under AddressSanitizer and UndefinedBehaviorSanitizer, and on emscripten:
b2 libs/trystero/test
b2 libs/trystero/test address-sanitizer=norecover undefined-sanitizer=norecover
b2 toolset=emscripten libs/trystero/test
The 3 examples are linked and never run, since each needs the
public relays or a browser. b2 compares timestamps, so a result that counts
is built from scratch, with b2 -a.
The oracle and the interop lanes
Two lanes of trystero’s own, declared in test/oracle/Jamfile, check it
against Trystero itself, pinned at 0.26.0 by test/oracle/package.json and
package-lock.json, with Node:
webcpp.original node ; webcpp.cases protocol : oracle.mjs : ../fixtures/cases : ../fixtures/expected ; webcpp.lane oracle : cases-protocol ;
oracle, which names no target, runs Trystero’s own functions over the
inputs of test/fixtures/cases/protocol.json and checks that they still
write test/fixtures/expected/expected.json byte for byte, the values the
protocol and native_sealer tests reproduce; update-expected is its only
writer.
webcpp.drive interop_browser : interop/peer.cpp
: <linkflags>-sENVIRONMENT=web <linkflags>-sALLOW_MEMORY_GROWTH <webcpp-drive-timeout>2400
: interop/run.mjs : --browser : emscripten ;
webcpp.drive interop_native : interop/native_peer.cpp
: <library>/webcpp/trystero//native <threading>multi <exception-handling>on <webcpp-drive-timeout>2400
<define>_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE
<define>_GLIBCXX_ASSERTIONS
: interop/run.mjs : --native : native ;
webcpp.lane interop : interop_browser interop_native : native emscripten ;
interop, natively and on emscripten, meets each C++ peer, the native one
as a process and the browser’s in headless Chrome, with Trystero JS peers in
Chrome, over an in-memory Nostr relay on 127.0.0.1 that verifies every
event’s id and signature as a public relay does. Its scenarios: the C++
peer offering; Trystero JS offering, with a password; a Trystero JS peer that
also sends malformed candidates, or text messages on its data channel; a
first relay to which no WebSocket can be made; a C++ peer given a TURN
server without credentials; a watcher without the key that answers every
offer with garbage in the other peer’s name, that resends a sealed answer
and offer it recorded, and that sends the C++ peer its own sealed offer
back, which the C++ peer must refuse; a flood of announcements and of
unsealed offers from fresh peer ids; a relay text longer than 1 MiB; a C++
peer and a Trystero JS page each in two rooms of one app, sharing one
connection and one peer id per app; a wrong password;
and a mesh of three. The native peer meets three more: a room
moved over the joined one, which must leave it as destroying it does; a
relay that ends the connection just as the peer writes, which twelve peers in
turn must survive; and the driver’s own check that a peer killed by a signal
is named at once.
The shared-connection scenarios run in both lanes:
-
Two rooms exchange chat and 100 KB files both ways on one adopted, connected transport.
-
A third room joins later through presence on that transport.
-
Rooms the JS page joined first bind when the C++ app joins them later.
-
A peer can enter another room of a passwordless app by token, while a protected app with another password and self id shares no connection or presence with it, in either id order.
-
Either side leaves one room while the other keeps exchanging, making no connection.
-
Leaving every room closes the transport after the configured idle time.
-
An authenticated peer sending wrong room proofs in both id orders gives paced lower-id reports while the other room keeps exchanging over the same connection.
-
A 5,000-message flood in one room leaves pings in another completing within 2 s.
-
A held handshake frame recovers in both id orders after one timeout, without a false password report; the other room keeps exchanging.
The C++ peers accept room2Delay and room3Delay in milliseconds; the JS
page also accepts room2Delay. These delayed joins establish the first room
before a shared-binding scenario begins;
the proof-pacing scenario first authenticates the shared password domain,
then corrupts room proofs after the initial announcement burst.
The adoption scenarios allow crossed duplicates to retry, but fail with connection counts if either side constructs more than four. Transient recovery accounts for JS reuse during the higher id’s dropping window (What remains).
Every peer gathers its ICE candidates on the machine alone: the C++ peers
get stun:127.0.0.1:9 in place of Trystero’s public STUN servers, the
native peer binds its connections to 127.0.0.1, and the Trystero JS peers
have no ICE server. Chrome is the one CHROME names, else on macOS
Playwright’s chrome-headless-shell; the continuous integration names Chrome
for Testing’s chrome-headless-shell, at the version webcpp pins. Each driver
is bounded by 2,400 s, beyond its scenarios’ own limits, so that a scenario that
does not complete fails by name with what each peer saw.
b2 -a libs/trystero/test/oracle//oracle
b2 -a --dump-tests --out-xml=native.trystero.interop.xml libs/trystero/test/oracle//interop
b2 -a --dump-tests --out-xml=emscripten.trystero.interop.xml toolset=emscripten libs/trystero/test/oracle//interop
The container lanes
Two lanes of the continuous integration, gcc-14 and clang-linux-18, build
and run the unit tests with GCC 14 and with clang 18 against
libstdc++ in a Linux container, Ubuntu 24.04, with docker run
and the network none, which leaves the loopback alone: every test passes,
which shows that none reaches beyond the machine
(guarantee 6). libsecp256k1 is installed before
running the container. B2 builds the pinned OpenSSL, libdatachannel and
nested source dependencies inside the container with the lane’s compiler,
runtime and static link configuration. These builds need no network access.
The common OpenSSL provider
The native provider-integration lane builds and runs one application that
uses Asio TLS, Beast and Trystero’s native backend together. It compares the
compiled OpenSSL version with the loaded implementation and constructs TLS
and WebRTC state. The run is bounded to 45 seconds and uses the threaded
release profile. The superproject’s source dependency tests also check that
the final Unix executable has no dynamic OpenSSL or libdatachannel provider,
that each static archive occurs once and that dependent archives precede
OpenSSL in the link order.
The test matrix
The continuous integration runs trystero’s tests in lanes, one toolset each,
and its own lanes, the oracle once and the interop natively and on
emscripten, as native.trystero.interop and emscripten.trystero.interop, plus the
native provider-integration lane for the common TLS provider.
Each lane builds from scratch and records what b2 built and ran; webcpp’s
tools/report/report.py merges the files of every lane into a matrix, where
each cell says whether that program passed in that lane, or how it failed.
The matrix is published with the documentation, at
https://webcpporg.github.io/report/.
Reference
Every name trystero declares, in the namespace webcpp::trystero and those
nested in it, from the Doc Comments of its headers: each class, function,
type and enumerator with its declaration, what it does, what it asks of its
caller and how it fails, and the part of Trystero 0.26.0 it ports. The other
sections teach the library; this one is where a name is looked up.
Macros
| Name | Description |
|---|---|
Defined when trystero is built without exceptions: when webcpp has none (the compiler has none, or the developer defined |
webcpp::trystero
A callable that allocates through a memory resource: what trystero holds every handler in, where std::function, which takes no allocator since C++17, would allocate with operator new.
Namespaces
| Name | Description |
|---|---|
The seam between the room and a platform: what a target must provide for a room to run on it. The browser provides it over |
|
The browser target: the backend interface over the page’s own |
|
The native target: the backend interface over libdatachannel for WebRTC, Boost.Beast for the relays’ WebSockets, OpenSSL for TLS and AES-GCM, and Asio for timers. Everything it reports runs on the executor it is given; the application runs that executor’s context, blocking on it or polling it. |
|
Types
| Name | Description |
|---|---|
A named channel of messages of type |
|
This program in one app: one peer id, and one connection per remote peer, shared by the rooms it joins, as a Trystero JS page shares them. |
|
What an app is made with: the part of Trystero’s |
|
The category of |
|
A move-only callable of a signature, whose target is allocated through a memory resource; see the partial specialization |
|
A |
|
Who sent a message, and the metadata it carried, the second and third arguments of Trystero’s action handler, in the receiving room’s resource. |
|
A room joined over Trystero’s Nostr strategy, what Trystero’s |
|
What a room of an app is joined with: the part of Trystero’s |
|
Where an action’s message goes and what it carries beside its payload, the target and the metadata of Trystero’s |
Type Aliases
| Name | Description |
|---|---|
Binary data allocated with |
|
What an action carries when its type is not fixed: Trystero’s text, JSON or binary payload. |
|
A peer’s id in a room, Trystero’s |
|
Binary data in a memory resource, an action’s binary payload in the room’s resource. |
Enums
| Name | Description |
|---|---|
What an operation of the library completes with when it does not succeed. |
Functions
| Name | Description |
|---|---|
Trystero’s default STUN servers, its |
|
The category of every |
|
An error code of |
|
Returns: |
|
Returns: !(a == b). |
|
webcpp::trystero::backend
The seam between the room and a platform: what a target must provide for a room to run on it. The browser provides it over emscripten::val (webcpp/trystero/browser), a native target over libdatachannel, Boost.Beast and OpenSSL (webcpp/trystero/native), and the tests over memory (test/support).
Description
Tip: every handler a backend invokes runs on the room’s executor, one at a time, and may destroy the object that invoked it. A backend whose events arrive on other threads (libdatachannel) posts them there first.
Every handler runs after the call that caused it has returned, never inside it: a completion (done) of a peer_connection or a sealer, and a websocket’s close when no socket can be made, are posted even when their result is known at once. The room calls these operations from inside handlers whose state a completion run there would change under them.
Text crosses this interface as std::string_view, valid for the duration of the call or of the handler: a backend reads what it is given before returning, and a handler copies what it keeps. The one exception is a data channel message, handed over by value: the receiver keeps the frame, and taking ownership of it is what spares a copy.
Tip: every method that makes an object takes the room’s memory resource last, and the backend allocates the object, its handlers and what it hands to the room (a frame, a text) through it, on the room’s executor only.
Types
| Name | Description |
|---|---|
A WebRTC data channel, as the |
|
Makes everything above, and gives a room its randomness and its clocks: one per page in the browser, one per application natively, one per test. |
|
A STUN or TURN server, an |
|
A WebRTC peer connection, as the |
|
AES-GCM under one room key, in Trystero’s sealed text form (protocol/signal.hpp), what Trystero’s |
|
A one-shot timer, what the |
|
A WebSocket to a Nostr relay, as the |
Type Aliases
| Name | Description |
|---|---|
A data channel frame, in the memory resource of the room it is for. |
Enums
| Name | Description |
|---|---|
The state of a peer connection, as |
Using Declarations
| Name | Description |
|---|---|
The error code every completion of a backend reports, empty on success. |
webcpp::trystero::backend::bytes
A data channel frame, in the memory resource of the room it is for.
webcpp::trystero::backend::data_channel
A WebRTC data channel, as the RTCDataChannel a Trystero peer exchanges its frames over.
Types
| Name | Description |
|---|---|
What a data channel reports, each on the room’s executor. |
Member Functions
| Name | Description |
|---|---|
|
Destroys the channel, which closes it without reporting the close. |
|
The bytes queued to send, its |
|
Closes the channel, which the handler |
|
Whether the channel is open, its |
|
Sends a binary message; one sent while the channel is not open is dropped. |
|
Replaces the handlers; a handler may replace them during its own call. |
Static Data Members
| Name | Description |
|---|---|
Trystero’s backpressure threshold, its |
Derived Classes
| Name | Description |
|---|---|
A data channel over the page’s |
|
A data channel of libdatachannel, the native |
See Also
"Design", in the guide.
webcpp::trystero::backend::data_channel::handlers
What a data channel reports, each on the room’s executor.
Data Members
| Name | Description |
|---|---|
The buffered amount fell to |
|
The channel closed; called once. |
|
A binary message arrived, handed over in the room’s resource. |
|
The channel opened. |
webcpp::trystero::backend::data_channel::handlers::buffered_amount_low
The buffered amount fell to low_threshold or below after being above it.
webcpp::trystero::backend::data_channel::handlers::close
The channel closed; called once.
webcpp::trystero::backend::data_channel::handlers::message
A binary message arrived, handed over in the room’s resource.
webcpp::trystero::backend::data_channel::handlers::open
The channel opened.
webcpp::trystero::backend::data_channel::~data_channel
Destroys the channel, which closes it without reporting the close.
webcpp::trystero::backend::data_channel::buffered_amount
The bytes queued to send, its bufferedAmount.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
[[nodiscard]]
virtual
std::size_t
buffered_amount() const = 0;
Return Value
The number of bytes sent and not yet transmitted.
|
Note
|
The return value should not be discarded. |
webcpp::trystero::backend::data_channel::close
Closes the channel, which the handler close reports.
webcpp::trystero::backend::data_channel::is_open
Whether the channel is open, its readyState "open".
Return Value
Whether a message sent now would be sent.
|
Note
|
The return value should not be discarded. |
webcpp::trystero::backend::data_channel::send
Sends a binary message; one sent while the channel is not open is dropped.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
void
send(std::span<uint8_t const> frame) = 0;
Parameters
| Name | Description |
|---|---|
frame |
The message, read during the call. |
webcpp::trystero::backend::data_channel::set_handlers
Replaces the handlers; a handler may replace them during its own call.
Parameters
| Name | Description |
|---|---|
h |
The new handlers; an empty one is not called. |
webcpp::trystero::backend::data_channel::low_threshold
Trystero’s backpressure threshold, its bufferedAmountLowThreshold: senders wait while more than 64 KiB is buffered.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
inline constexpr static
std::size_t low_threshold = 0xffff;
webcpp::trystero::backend::factory
Makes everything above, and gives a room its randomness and its clocks: one per page in the browser, one per application natively, one per test.
Description
Each object it makes is allocated, with its handlers and what it hands to the room, in the memory resource it is given, the room’s: that resource must outlive the object and every operation the object leaves pending.
Member Functions
| Name | Description |
|---|---|
|
Destroys the factory, which must outlive every room that uses it. |
|
Opens a WebSocket to a relay. |
|
Makes a peer connection that gathers its candidates through |
|
Makes a sealer under a room’s key. |
|
Makes a timer. |
|
A monotonic clock in milliseconds, for rate limits and timeouts, as |
|
Fills |
|
Wall-clock seconds since the Unix epoch, for Nostr’s |
Derived Classes
See Also
"Allocation", in the guide.
webcpp::trystero::backend::factory::~factory
Destroys the factory, which must outlive every room that uses it.
webcpp::trystero::backend::factory::connect
Opens a WebSocket to a relay.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
std::shared_ptr<websocket>
connect(
std::string_view url,
websocket::handlers h,
std::pmr::memory_resource* memory) = 0;
Return Value
The WebSocket, which reports a close when it cannot connect.
Parameters
| Name | Description |
|---|---|
url |
The relay’s |
h |
What the WebSocket reports. |
memory |
The room’s resource, which the WebSocket, its handlers and its texts are allocated in. |
webcpp::trystero::backend::factory::make_peer_connection
Makes a peer connection that gathers its candidates through servers.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
std::shared_ptr<peer_connection>
make_peer_connection(
std::span<ice_server const> servers,
std::pmr::memory_resource* memory) = 0;
Return Value
The connection, or null when the platform refuses to make one: the room then reports the negotiation with that peer as failed, through room::on_error.
Parameters
| Name | Description |
|---|---|
servers |
The STUN and TURN servers, read during the call. |
memory |
The room’s resource, which the connection, its channels, its handlers and its texts are allocated in. |
webcpp::trystero::backend::factory::make_sealer
Makes a sealer under a room’s key.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
std::shared_ptr<sealer>
make_sealer(
std::array<uint8_t, 32> key,
std::pmr::memory_resource* memory) = 0;
Return Value
The sealer.
Parameters
| Name | Description |
|---|---|
key |
The raw AES-256 key, as |
memory |
The room’s resource, which the sealer, its texts and its completions are allocated in. |
webcpp::trystero::backend::factory::make_timer
Makes a timer.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
std::shared_ptr<timer>
make_timer(std::pmr::memory_resource* memory) = 0;
Return Value
The timer, not started.
Parameters
| Name | Description |
|---|---|
memory |
The room’s resource, which the timer and its waits are allocated in. |
webcpp::trystero::backend::factory::now_ms
A monotonic clock in milliseconds, for rate limits and timeouts, as performance.now().
Return Value
The current time, in milliseconds from an origin of the backend’s.
webcpp::trystero::backend::factory::random
Fills out with bytes from a cryptographically secure source: they become secret keys and nonces.
Parameters
| Name | Description |
|---|---|
out |
The bytes to fill. |
webcpp::trystero::backend::factory::unix_seconds
Wall-clock seconds since the Unix epoch, for Nostr’s created_at and since.
Return Value
The current time, in whole seconds.
webcpp::trystero::backend::ice_server
A STUN or TURN server, an RTCIceServer of the iceServers Trystero gives RTCPeerConnection.
Description
A room copies those of its configuration into its own resource.
Data Members
| Name | Description |
|---|---|
The TURN server’s credential; empty for a STUN server. |
|
The server’s URLs, as |
|
The TURN server’s user name; empty for a STUN server. |
See Also
"Rooms and actions", in the guide.
webcpp::trystero::backend::ice_server::credential
The TURN server’s credential; empty for a STUN server.
webcpp::trystero::backend::ice_server::urls
The server’s URLs, as "stun:stun.l.google.com:19302" or "turn:host:3478".
webcpp::trystero::backend::ice_server::username
The TURN server’s user name; empty for a STUN server.
webcpp::trystero::backend::peer_connection
A WebRTC peer connection, as the RTCPeerConnection of a Trystero peer, whose negotiation the signaling drives: it makes an offer or an answer only when asked to.
Types
| Name | Description |
|---|---|
What a peer connection reports, each on the room’s executor. |
Member Functions
| Name | Description |
|---|---|
|
Destroys the connection, which closes it without reporting the close. |
|
Adds a remote ICE candidate, like |
|
Closes the connection and its channels. |
|
Opens a data channel, like |
|
Whether a remote description was applied, which a candidate needs before it is added. |
|
Replaces the handlers; a handler may replace them during its own call. |
|
Creates the offer or the answer the signaling state calls for and applies it, like |
|
Applies the remote peer’s offer or answer, like |
Derived Classes
| Name | Description |
|---|---|
A peer connection over the page’s |
|
A peer connection of libdatachannel, the native |
See Also
"Design", in the guide.
webcpp::trystero::backend::peer_connection::handlers
What a peer connection reports, each on the room’s executor.
Data Members
| Name | Description |
|---|---|
The remote peer opened a data channel. |
|
A local ICE candidate, the JSON of an |
|
The connection’s state changed. |
webcpp::trystero::backend::peer_connection::handlers::data_channel
The remote peer opened a data channel.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
handler<void(std::shared_ptr<data_channel>)> data_channel;
webcpp::trystero::backend::peer_connection::handlers::ice_candidate
A local ICE candidate, the JSON of an RTCIceCandidateInit, valid for the call.
webcpp::trystero::backend::peer_connection::handlers::state
The connection’s state changed.
webcpp::trystero::backend::peer_connection::~peer_connection
Destroys the connection, which closes it without reporting the close.
webcpp::trystero::backend::peer_connection::add_ice_candidate
Adds a remote ICE candidate, like addIceCandidate().
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
void
add_ice_candidate(
std::string_view candidate_json,
handler<void(boost::system::error_code)> done) = 0;
Parameters
| Name | Description |
|---|---|
candidate_json |
The JSON of an |
done |
Called once it is added, or with |
webcpp::trystero::backend::peer_connection::close
Closes the connection and its channels.
webcpp::trystero::backend::peer_connection::create_data_channel
Opens a data channel, like createDataChannel().
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
std::shared_ptr<data_channel>
create_data_channel(std::string_view label) = 0;
Return Value
The channel, made in the connection’s resource, or null when the platform refuses to open one, as on a closed connection.
Parameters
| Name | Description |
|---|---|
label |
The channel’s label, read during the call. |
webcpp::trystero::backend::peer_connection::has_remote_description
Whether a remote description was applied, which a candidate needs before it is added.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
[[nodiscard]]
virtual
bool
has_remote_description() const = 0;
Return Value
Whether the connection has a remoteDescription.
|
Note
|
The return value should not be discarded. |
webcpp::trystero::backend::peer_connection::set_handlers
Replaces the handlers; a handler may replace them during its own call.
Parameters
| Name | Description |
|---|---|
h |
The new handlers; an empty one is not called. |
webcpp::trystero::backend::peer_connection::set_local_description
Creates the offer or the answer the signaling state calls for and applies it, like setLocalDescription().
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
void
set_local_description(handler<void(boost::system::error_code, std::string_view, std::string_view)> done) = 0;
Parameters
| Name | Description |
|---|---|
done |
Called with the description’s type ("offer" or "answer") and its SDP, views valid for the call, or with |
webcpp::trystero::backend::peer_connection::set_remote_description
Applies the remote peer’s offer or answer, like setRemoteDescription().
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
void
set_remote_description(
std::string_view type,
std::string_view sdp,
handler<void(boost::system::error_code)> done) = 0;
Parameters
| Name | Description |
|---|---|
type |
"offer" or "answer", read during the call. |
sdp |
The description’s SDP, read during the call. |
done |
Called once it is applied, or with |
webcpp::trystero::backend::sealer
AES-GCM under one room key, in Trystero’s sealed text form (protocol/signal.hpp), what Trystero’s encrypt and decrypt do with crypto.subtle.
Member Functions
Derived Classes
See Also
"The protocol", in the guide.
webcpp::trystero::backend::sealer::open
Opens a sealed text.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
void
open(
std::string_view sealed,
handler<void(boost::system::error_code, std::string_view)> done) = 0;
Parameters
| Name | Description |
|---|---|
sealed |
The sealed text, read during the call. |
done |
Called with the plaintext, a view valid for the call, or with an error and an empty view: |
webcpp::trystero::backend::sealer::seal
Seals a text under a fresh random IV.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
void
seal(
std::string_view plaintext,
handler<void(boost::system::error_code, std::string_view)> done) = 0;
Parameters
| Name | Description |
|---|---|
plaintext |
The text, read during the call. |
done |
Called with the sealed text, a view valid for the call, or with |
webcpp::trystero::backend::timer
A one-shot timer, what the setTimeout calls of Trystero become.
Description
Starting it again replaces the pending expiry, and destroying it cancels it.
Member Functions
Derived Classes
See Also
"Design", in the guide.
webcpp::trystero::backend::timer::cancel
Cancels the pending expiry, if any, whose handler is then never called.
webcpp::trystero::backend::timer::start
Calls fire on the room’s executor once delay has passed, in place of a pending expiry.
Synopsis
Declared in <webcpp/trystero/backend.hpp>
virtual
void
start(
std::chrono::milliseconds delay,
handler<void()> fire) = 0;
Parameters
| Name | Description |
|---|---|
delay |
How long to wait. |
fire |
What to call when the timer expires; never called once it is cancelled. |
webcpp::trystero::backend::websocket
A WebSocket to a Nostr relay, as the WebSocket Trystero’s relay manager opens.
Types
| Name | Description |
|---|---|
What a WebSocket reports, each on the room’s executor. |
Member Functions
| Name | Description |
|---|---|
|
Destroys the WebSocket, which closes it without reporting the close. |
|
Closes the connection, which the handler |
|
Sends a text message; one sent before the connection opens or after it closes is dropped. |
Derived Classes
| Name | Description |
|---|---|
A WebSocket over the page’s |
See Also
"Design", in the guide.
webcpp::trystero::backend::websocket::handlers
What a WebSocket reports, each on the room’s executor.
webcpp::trystero::backend::websocket::handlers::close
The connection closed, or failed to open: called once, also when it never opened.
webcpp::trystero::backend::websocket::handlers::message
A text message arrived; the view is valid for the duration of the call.
webcpp::trystero::backend::websocket::~websocket
Destroys the WebSocket, which closes it without reporting the close.
webcpp::trystero::backend::websocket::send
Sends a text message; one sent before the connection opens or after it closes is dropped.
Parameters
| Name | Description |
|---|---|
text |
The message, read during the call. |
webcpp::trystero::backend::connection_state
The state of a peer connection, as RTCPeerConnection reports it.
Members
| Name | Description |
|---|---|
Nothing has happened yet: "new". |
|
The connection is being established: "connecting", or ICE’s "checking". |
|
The connection works: "connected", or ICE’s "completed". |
|
The connection was lost, perhaps for a moment: "disconnected". |
|
The connection failed: "failed". |
|
The connection was closed: "closed". |
webcpp::trystero::backend::connection_state::fresh
Nothing has happened yet: "new".
webcpp::trystero::backend::connection_state::connecting
The connection is being established: "connecting", or ICE’s "checking".
webcpp::trystero::backend::connection_state::connected
The connection works: "connected", or ICE’s "completed".
webcpp::trystero::backend::connection_state::disconnected
The connection was lost, perhaps for a moment: "disconnected".
webcpp::trystero::backend::connection_state::failed
The connection failed: "failed".
webcpp::trystero::backend::connection_state::closed
The connection was closed: "closed".
webcpp::trystero::backend::error_code
The error code every completion of a backend reports, empty on success.
webcpp::trystero::browser
The browser target: the backend interface over the page’s own WebSocket, RTCPeerConnection, RTCDataChannel, setTimeout and crypto.subtle, through emscripten::val. It needs a secure context (https, or http on localhost), where crypto.subtle exists.
Description
Tip: its objects, their handlers and the frames and texts they hand to the room are in the room’s resource; what emscripten::val holds is in the JavaScript heap, and the page’s event_loop keeps its registrations in its own resource.
Types
| Name | Description |
|---|---|
The browser’s factory: one per page, beside the page’s |
|
A data channel over the page’s |
|
The bridge between the page’s JavaScript event loop and an Asio io_context, which never runs on its own in a browser: it is polled after every JavaScript event that enters C++, and when work is posted from outside one. |
|
An Asio executor over an |
|
A peer connection over the page’s |
|
AES-GCM through |
|
A timer over the page’s |
|
A WebSocket over the page’s |
Functions
| Name | Description |
|---|---|
The function JavaScript calls, |
|
The one function JavaScript calls, |
|
The function JavaScript calls, |
|
Whether two executors are of one loop. |
|
Whether two executors are of different loops. |
Using Declarations
| Name | Description |
|---|---|
A JavaScript value, embind’s |
webcpp::trystero::browser::backend
The browser’s factory: one per page, beside the page’s event_loop.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
class backend
: public trystero::backend::factory
Description
It makes peer connections, data channels and received frames in app_config::memory, and WebSockets, sealers and room timers in the room’s effective room_options::memory. Objects, handlers and registrations use the resource supplied to their factory method. What emscripten::val holds is in the JavaScript heap. It needs a secure context, where crypto.subtle exists.
Base Classes
| Name | Description |
|---|---|
Makes everything above, and gives a room its randomness and its clocks: one per page in the browser, one per application natively, one per test. |
Member Functions
| Name | Description |
|---|---|
|
Constructs the page’s backend. |
|
Opens a WebSocket to a relay, with the page’s |
|
Makes a peer connection, with the page’s |
|
Makes a sealer, over |
|
Makes a timer, over |
|
Milliseconds from |
|
Fills |
|
Seconds since the Unix epoch, from |
Static Data Members
| Name | Description |
|---|---|
The most |
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::backend::backend
Constructs the page’s backend.
Parameters
| Name | Description |
|---|---|
loop |
The page’s loop, which must outlive the backend and every object it makes. |
webcpp::trystero::browser::backend::connect
Opens a WebSocket to a relay, with the page’s WebSocket.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
std::shared_ptr<trystero::backend::websocket>
connect(
std::string_view url,
trystero::backend::websocket::handlers h,
std::pmr::memory_resource* memory) override;
Return Value
The WebSocket.
Parameters
| Name | Description |
|---|---|
url |
The relay’s URL, read during the call. |
h |
What the WebSocket reports. |
memory |
The room’s resource, which the WebSocket, its handlers and its texts are allocated in. |
webcpp::trystero::browser::backend::make_peer_connection
Makes a peer connection, with the page’s RTCPeerConnection.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
std::shared_ptr<trystero::backend::peer_connection>
make_peer_connection(
std::span<trystero::backend::ice_server const> servers,
std::pmr::memory_resource* memory) override;
Return Value
The connection, or null when the page holds max_peer_connections, or when the browser refuses to make one, as peer_connection::create says.
Parameters
| Name | Description |
|---|---|
servers |
The STUN and TURN servers, read during the call. |
memory |
The app’s resource, which the connection, its channels, its handlers and its texts are allocated in. |
webcpp::trystero::browser::backend::make_sealer
Makes a sealer, over crypto.subtle.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
std::shared_ptr<trystero::backend::sealer>
make_sealer(
std::array<uint8_t, 32> key,
std::pmr::memory_resource* memory) override;
Return Value
The sealer.
Parameters
| Name | Description |
|---|---|
key |
The raw AES-256 key. |
memory |
The room’s resource, which the sealer, its texts and its completions are allocated in. |
webcpp::trystero::browser::backend::make_timer
Makes a timer, over setTimeout.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
std::shared_ptr<trystero::backend::timer>
make_timer(std::pmr::memory_resource* memory) override;
Return Value
The timer, not started.
Parameters
| Name | Description |
|---|---|
memory |
The room’s resource, which the timer and its handler are allocated in. |
webcpp::trystero::browser::backend::now_ms
Milliseconds from performance.now().
Return Value
The current time, in milliseconds since the page’s time origin.
webcpp::trystero::browser::backend::random
Fills out with the page’s entropy, which Emscripten draws from crypto.getRandomValues.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
random(std::span<uint8_t> out) override;
Parameters
| Name | Description |
|---|---|
out |
The bytes to fill. |
webcpp::trystero::browser::backend::unix_seconds
Seconds since the Unix epoch, from Date.now().
Return Value
The current time, in whole seconds.
webcpp::trystero::browser::backend::max_peer_connections
The most RTCPeerConnection objects the page’s rooms hold, every backend of the page together, counted from their construction until the garbage collector frees them: half of the 500 Chromium allows a page, which it counts the same way, so that a closed connection still counts until a collection the page cannot ask for.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
inline constexpr static
std::size_t max_peer_connections = 256;
Description
Tip: a room makes at most 64 connections in any 57 s, so floods of four rooms reach this within a minute when no collection runs; the page then makes none until one does, and its rooms report each negotiation they cannot start, while the rest of Chromium’s count stays free for what the page makes without a room.
webcpp::trystero::browser::data_channel
A data channel over the page’s RTCDataChannel, its messages received as ArrayBuffer objects.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
class data_channel
: public backend::data_channel
Description
A message that is not an ArrayBuffer, as a text one a peer sends, or that is larger than any room frame (protocol::max_room_frame_bytes), is dropped before it is read: neither can be a frame, and reading a text as bytes would make a typed array of the length its text says.
Base Classes
| Name | Description |
|---|---|
A WebRTC data channel, as the |
Member Functions
| Name | Description |
|---|---|
|
Wraps a channel of the page, and takes over its events. |
|
Destroys the wrapper, which closes the channel without reporting the close. |
|
The bytes queued to send. |
|
Closes the channel, which the handler |
|
Whether the channel is open. |
|
Sends a binary message, copied into a new |
|
Replaces the handlers, kept in a shared object so that one replacing them during its own call outlives the call. |
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::data_channel::data_channel
Wraps a channel of the page, and takes over its events.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
data_channel(
event_loop& loop,
emscripten::val channel,
std::pmr::memory_resource* memory);
Parameters
| Name | Description |
|---|---|
loop |
The page’s loop, which must outlive the channel. |
channel |
The |
memory |
The app’s resource, which the channel’s handlers, registration and received frames are allocated in. |
webcpp::trystero::browser::data_channel::~data_channel
Destroys the wrapper, which closes the channel without reporting the close.
webcpp::trystero::browser::data_channel::buffered_amount
The bytes queued to send.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
[[nodiscard]]
virtual
std::size_t
buffered_amount() const override;
webcpp::trystero::browser::data_channel::close
Closes the channel, which the handler close reports.
webcpp::trystero::browser::data_channel::is_open
Whether the channel is open.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
[[nodiscard]]
virtual
bool
is_open() const override;
webcpp::trystero::browser::data_channel::send
Sends a binary message, copied into a new Uint8Array, when the channel is open; one the channel refuses, as one larger than its peer accepts, is dropped.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
send(std::span<uint8_t const> frame) override;
Parameters
| Name | Description |
|---|---|
frame |
The message, read during the call. |
webcpp::trystero::browser::data_channel::set_handlers
Replaces the handlers, kept in a shared object so that one replacing them during its own call outlives the call.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
set_handlers(handlers h) override;
Parameters
| Name | Description |
|---|---|
h |
The new handlers, kept in the channel’s resource. |
webcpp::trystero::browser::event_loop
The bridge between the page’s JavaScript event loop and an Asio io_context, which never runs on its own in a browser: it is polled after every JavaScript event that enters C++, and when work is posted from outside one.
Description
JavaScript reaches C++ through handler(id, slot): a function bound to this loop, a registration and a slot, passed to trystero_dispatch (browser/src.hpp). An object registers a callback with add and removes it with remove when it is destroyed, so a late event for a destroyed object finds nothing. A loop must outlive the page’s use of it; its destructor marks every handler it made as dead.
Tip: a loop serves every room of the page, so the entries of its registrations are in the resource it is constructed with; a registered callback stays in its own allocator’s resource, a room’s for a room’s object, until it is removed or, for a promise, until the promise settles.
Type Aliases
| Name | Description |
|---|---|
What a registration calls: with the slot the event came through, and the event. |
Member Functions
| Name | Description |
|---|---|
|
Constructors |
|
Destroys the loop, and marks every handler it made as dead: their events are dropped. |
|
Not copy-assignable: the page’s functions name this loop by its address. |
Registers a callback, which JavaScript reaches through |
|
How many calls into C++ are running: 0 between events, 1 inside a callback. |
|
Runs a registration’s callback for a JavaScript event, then everything that became ready. |
|
An executor of this loop, what a room in the browser is constructed with. |
|
A JavaScript function of one argument that delivers it to registration |
|
The io_context the loop polls. |
|
Runs every ready handler; what they post runs too. |
|
Brings the loop back after an exception escaped a call into C++ that entered at |
|
Removes a registration: an event for it from now on finds nothing. |
|
Schedules a |
|
A JavaScript string of UTF-8 text, decoded by a |
|
|
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::event_loop::callback
What a registration calls: with the slot the event came through, and the event.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
using callback = handler<void(int, emscripten::val)>;
webcpp::trystero::browser::event_loop::event_loop
Constructors
Synopses
Declared in <webcpp/trystero/browser/event_loop.hpp>
Not copyable: the page’s functions name this loop by its address.
event_loop(event_loop const&) = delete;
Constructs a loop over io, which it keeps from running out of work.
explicit
event_loop(
boost::asio::io_context& io,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Parameters
| Name | Description |
|---|---|
io |
The io_context the loop polls, which must outlive the loop. |
memory |
The resource the entries of the registrations are allocated in; it must outlive the loop. |
webcpp::trystero::browser::event_loop::event_loop
Not copyable: the page’s functions name this loop by its address.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
event_loop(event_loop const&) = delete;
webcpp::trystero::browser::event_loop::event_loop
Constructs a loop over io, which it keeps from running out of work.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
explicit
event_loop(
boost::asio::io_context& io,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Parameters
| Name | Description |
|---|---|
io |
The io_context the loop polls, which must outlive the loop. |
memory |
The resource the entries of the registrations are allocated in; it must outlive the loop. |
webcpp::trystero::browser::event_loop::~event_loop
Destroys the loop, and marks every handler it made as dead: their events are dropped.
webcpp::trystero::browser::event_loop::operator=
Not copy-assignable: the page’s functions name this loop by its address.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
event_loop&
operator=(event_loop const&) = delete;
webcpp::trystero::browser::event_loop::add
Registers a callback, which JavaScript reaches through handler.
Description
The callback stays in its own allocator’s resource (a room’s, for a room’s object); only the registration’s entry is the loop’s.
Parameters
| Name | Description |
|---|---|
f |
The callback. |
webcpp::trystero::browser::event_loop::depth
How many calls into C++ are running: 0 between events, 1 inside a callback.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
[[nodiscard]]
int
depth() const noexcept;
webcpp::trystero::browser::event_loop::dispatch
Runs a registration’s callback for a JavaScript event, then everything that became ready.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
void
dispatch(
int id,
int slot,
emscripten::val event);
Description
An event the page delivers while C++ is already running, inside a call C++ made into JavaScript (a WebSocket that fires close from inside close(), as node’s does), is posted rather than run, so that a handler never runs inside the call that caused it.
Parameters
| Name | Description |
|---|---|
id |
The registration’s id; an unknown one runs nothing. |
slot |
The slot the event came through. |
event |
The event. |
webcpp::trystero::browser::event_loop::get_executor
An executor of this loop, what a room in the browser is constructed with.
Return Value
The executor.
webcpp::trystero::browser::event_loop::handler
A JavaScript function of one argument that delivers it to registration id, slot slot.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
[[nodiscard]]
emscripten::val
handler(
int id,
int slot) const;
Description
An exception that escapes the call, which no operation of trystero raises, is reported to the page (reportError) and does not stop the loop: the loop recovers, as recover says, and the function returns.
Return Value
The function, which calls trystero_dispatch with this loop, id, slot and its argument (browser/src.hpp).
|
Note
|
The return value should not be discarded. |
Parameters
| Name | Description |
|---|---|
id |
The registration’s id. |
slot |
A number the callback receives, which tells its events apart. |
webcpp::trystero::browser::event_loop::io
The io_context the loop polls.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
boost::asio::io_context&
io() noexcept;
Return Value
The io_context it was constructed with.
webcpp::trystero::browser::event_loop::poll
Runs every ready handler; what they post runs too.
webcpp::trystero::browser::event_loop::recover
Brings the loop back after an exception escaped a call into C++ that entered at depth: the calls it unwound no longer count, and a poll runs the work that is ready.
Description
Tip: without WebAssembly exceptions no destructor runs while an exception unwinds, so the depth a call raised stays raised; the page’s function reads the depth before it enters C++ and gives it back here, so that a nested entry that escapes leaves the depth of the call still running below it, never less. The page’s function also puts WebAssembly’s stack pointer back where it was before the call, which no epilogue of the unwound frames did, but what those frames owned on the heap stays allocated: 312 bytes per escape, measured, for a frame that held a string of 300. With exceptions, trystero_dispatch catches a C++ exception itself, which frees it and runs every destructor, and recovers the same way.
Parameters
| Name | Description |
|---|---|
depth |
The depth when the call entered, as |
webcpp::trystero::browser::event_loop::remove
Removes a registration: an event for it from now on finds nothing.
Parameters
| Name | Description |
|---|---|
id |
The registration’s id. |
webcpp::trystero::browser::event_loop::schedule_poll
Schedules a poll() in a microtask unless one is pending or a poll is running, which will see the work.
webcpp::trystero::browser::event_loop::text
A JavaScript string of UTF-8 text, decoded by a TextDecoder straight from the wasm memory the view points into.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
[[nodiscard]]
emscripten::val
text(std::string_view utf8) const;
Description
No std::string is made to carry it across, as emscripten::val(std::string) would.
Parameters
| Name | Description |
|---|---|
utf8 |
The text, read during the call. |
webcpp::trystero::browser::event_loop::utf8
utf8 overloads
Synopses
Declared in <webcpp/trystero/browser/event_loop.hpp>
The UTF-8 bytes of a JavaScript string, encoded by a TextEncoder and copied straight into a string of memory.
std::pmr::string
utf8(
emscripten::val const& text,
std::pmr::memory_resource* memory) const;
The UTF-8 bytes of a JavaScript string, as the other overload makes them, when they are at most max_bytes; nothing is copied into WebAssembly otherwise.
std::optional<std::pmr::string>
utf8(
emscripten::val const& text,
std::pmr::memory_resource* memory,
std::size_t max_bytes) const;
Return Value
-
The text, or an empty one when the string cannot be encoded.
-
The text, or nothing when it is longer or cannot be encoded.
Parameters
| Name | Description |
|---|---|
text |
The JavaScript string. |
memory |
The resource the copy is allocated in. |
max_bytes |
The most bytes the text may hold. |
webcpp::trystero::browser::event_loop::utf8
The UTF-8 bytes of a JavaScript string, encoded by a TextEncoder and copied straight into a string of memory.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
std::pmr::string
utf8(
emscripten::val const& text,
std::pmr::memory_resource* memory) const;
Description
No std::string is made to carry them across, as emscripten::val::as<std::string>() would.
Return Value
The text, or an empty one when the string cannot be encoded.
Parameters
| Name | Description |
|---|---|
text |
The JavaScript string. |
memory |
The resource the copy is allocated in. |
webcpp::trystero::browser::event_loop::utf8
The UTF-8 bytes of a JavaScript string, as the other overload makes them, when they are at most max_bytes; nothing is copied into WebAssembly otherwise.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
std::optional<std::pmr::string>
utf8(
emscripten::val const& text,
std::pmr::memory_resource* memory,
std::size_t max_bytes) const;
Description
The string’s length is checked first, in UTF-16 units, each at least one byte, so that a longer string is never encoded; then the encoded bytes are.
Return Value
The text, or nothing when it is longer or cannot be encoded.
Parameters
| Name | Description |
|---|---|
text |
The JavaScript string. |
memory |
The resource the copy is allocated in. |
max_bytes |
The most bytes the text may hold. |
webcpp::trystero::browser::executor
An Asio executor over an event_loop: what it is given runs on the loop’s io_context.
Description
Giving it work while no JavaScript event is being handled schedules one poll() in a microtask, so posted work runs without a polling loop and without the application calling poll(). A room in the browser is constructed with it, through boost::asio::any_io_executor.
Member Functions
Static Member Functions
| Name | Description |
|---|---|
The blocking property, which Asio queries: |
Friends
| Name | Description |
|---|---|
Whether two executors are of different loops. |
|
Whether two executors are of one loop. |
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::executor::executor
Constructs an executor of loop.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
explicit
executor(event_loop& loop) noexcept;
Parameters
| Name | Description |
|---|---|
loop |
The loop, which must outlive the executor and every copy of it. |
webcpp::trystero::browser::executor::execute
Posts f to the loop’s io_context, and schedules a poll unless one is coming.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
template<class F>
void
execute(F&& f) const;
Template Parameters
| Name | Description |
|---|---|
F |
A nullary callable. |
Parameters
| Name | Description |
|---|---|
f |
The work, allocated as its associated allocator says. |
webcpp::trystero::browser::executor::query
The execution context, the property Asio queries.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
[[nodiscard]]
boost::asio::execution_context&
query(boost::asio::execution::context_t) const noexcept;
webcpp::trystero::browser::executor::require
The executor with blocking.never required, which it already is.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
[[nodiscard]]
executor
require(boost::asio::execution::blocking_t::never_t) const noexcept;
webcpp::trystero::browser::executor::query
The blocking property, which Asio queries: execute never runs the work inside the call.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
constexpr
static
boost::asio::execution::blocking_t::never_t
query(boost::asio::execution::blocking_t) noexcept;
Return Value
blocking.never.
webcpp::trystero::browser::peer_connection
A peer connection over the page’s RTCPeerConnection.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
class peer_connection
: public backend::peer_connection
, public std::enable_shared_from_this<peer_connection>
Description
The state it reports merges its connectionState and its iceConnectionState.
Base Classes
| Name | Description |
|---|---|
A WebRTC peer connection, as the |
|
|
Member Functions
| Name | Description |
|---|---|
|
Wraps a connection of the page, and takes over its events; |
|
Destroys the wrapper, which closes the connection without reporting the close. |
|
Adds a remote ICE candidate, with |
|
Closes the connection and its channels. |
|
Opens a data channel with |
|
Whether a remote description was applied. |
The browser’s own |
|
|
Replaces the handlers, kept in a shared object so that one replacing them during its own call outlives the call. |
|
Creates and applies the offer or the answer the signaling state calls for, with |
|
Applies the remote peer’s offer or answer, with |
Static Member Functions
| Name | Description |
|---|---|
Makes an |
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::peer_connection::peer_connection
Wraps a connection of the page, and takes over its events; create makes one.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
peer_connection(
event_loop& loop,
emscripten::val connection,
std::pmr::memory_resource* memory);
Parameters
| Name | Description |
|---|---|
loop |
The page’s loop, which must outlive the connection. |
connection |
The |
memory |
The app’s resource, which the connection’s handlers, registration, channels and texts are allocated in. |
webcpp::trystero::browser::peer_connection::~peer_connection
Destroys the wrapper, which closes the connection without reporting the close.
webcpp::trystero::browser::peer_connection::add_ice_candidate
Adds a remote ICE candidate, with addIceCandidate().
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
add_ice_candidate(
std::string_view candidate_json,
handler<void(boost::system::error_code)> done) override;
Description
The text comes from a peer: one JSON.parse refuses is dropped, as a candidate the browser refuses is.
Parameters
| Name | Description |
|---|---|
candidate_json |
The JSON of an |
done |
Called once the candidate is added, or with |
webcpp::trystero::browser::peer_connection::close
Closes the connection and its channels.
webcpp::trystero::browser::peer_connection::create_data_channel
Opens a data channel with createDataChannel().
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
std::shared_ptr<backend::data_channel>
create_data_channel(std::string_view label) override;
Return Value
The channel, made in the connection’s resource, or null when the browser refuses to open one, as on a closed connection.
Parameters
| Name | Description |
|---|---|
label |
The channel’s label, read during the call. |
webcpp::trystero::browser::peer_connection::has_remote_description
Whether a remote description was applied.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
bool
has_remote_description() const override;
Return Value
Whether its remoteDescription is not null.
webcpp::trystero::browser::peer_connection::native_handle
The browser’s own RTCPeerConnection, what media will be added to.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
emscripten::val const&
native_handle() const;
Return Value
The connection.
See Also
"Roadmap", in the guide.
webcpp::trystero::browser::peer_connection::set_handlers
Replaces the handlers, kept in a shared object so that one replacing them during its own call outlives the call.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
set_handlers(handlers h) override;
Parameters
| Name | Description |
|---|---|
h |
The new handlers, kept in the connection’s resource. |
webcpp::trystero::browser::peer_connection::set_local_description
Creates and applies the offer or the answer the signaling state calls for, with setLocalDescription().
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
set_local_description(handler<void(boost::system::error_code, std::string_view, std::string_view)> done) override;
Parameters
| Name | Description |
|---|---|
done |
Called once the promise settles, with the |
webcpp::trystero::browser::peer_connection::set_remote_description
Applies the remote peer’s offer or answer, with setRemoteDescription().
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
set_remote_description(
std::string_view type,
std::string_view sdp,
handler<void(boost::system::error_code)> done) override;
Parameters
| Name | Description |
|---|---|
type |
"offer" or "answer", read during the call. |
sdp |
The description’s SDP, read during the call. |
done |
Called once the promise settles, or with |
webcpp::trystero::browser::peer_connection::create
Makes an RTCPeerConnection with servers as its iceServers.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
static
std::shared_ptr<peer_connection>
create(
event_loop& loop,
std::span<backend::ice_server const> servers,
std::pmr::memory_resource* memory);
Return Value
The connection, or null when the browser refuses to make one: it throws for a server it cannot use, and when the page holds as many connections as it allows. The page’s census counts the connection made, until the garbage collector frees it.
Parameters
| Name | Description |
|---|---|
loop |
The page’s loop, which must outlive the connection. |
servers |
The STUN and TURN servers, read during the call. |
memory |
The app’s resource, which the connection, its handlers, registration, channels and texts are allocated in. |
webcpp::trystero::browser::sealer
AES-GCM through crypto.subtle, under a key imported once, as Trystero’s encrypt and decrypt do.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
class sealer
: public backend::sealer
, public std::enable_shared_from_this<sealer>
Description
The text is copied into JavaScript during the call; an operation that arrives while the key is still being imported waits with a copy of its text, in the room’s resource. Without crypto.subtle, outside a secure context, every operation fails.
Base Classes
| Name | Description |
|---|---|
AES-GCM under one room key, in Trystero’s sealed text form (protocol/signal.hpp), what Trystero’s |
|
|
Type Aliases
| Name | Description |
|---|---|
What a seal or an open completes with: an error, or the text, a view valid for the call. |
Member Functions
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::sealer::done_handler
What a seal or an open completes with: an error, or the text, a view valid for the call.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
using done_handler = handler<void(boost::system::error_code, std::string_view)>;
webcpp::trystero::browser::sealer::sealer
Constructs a sealer under a room’s key, which it imports on its first operation.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
sealer(
event_loop& loop,
std::array<uint8_t, 32> key,
std::pmr::memory_resource* memory);
Parameters
| Name | Description |
|---|---|
loop |
The page’s loop, which must outlive the sealer’s pending promises. |
key |
The raw AES-256 key, |
memory |
The room’s resource, which its texts, its waiting operations and its completions are allocated in. |
webcpp::trystero::browser::sealer::open
Opens a sealed text, with crypto.subtle.decrypt.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
open(
std::string_view sealed,
done_handler done) override;
Parameters
| Name | Description |
|---|---|
sealed |
The sealed text, read during the call. |
done |
Called with the plaintext, or with |
webcpp::trystero::browser::sealer::seal
Seals a text under a fresh random IV, with crypto.subtle.encrypt.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
seal(
std::string_view plaintext,
done_handler done) override;
Parameters
| Name | Description |
|---|---|
plaintext |
The text, read during the call. |
done |
Called with the sealed text, or with |
webcpp::trystero::browser::timer
A timer over the page’s setTimeout.
Base Classes
| Name | Description |
|---|---|
A one-shot timer, what the |
Member Functions
| Name | Description |
|---|---|
|
Constructs a timer, not started, registered with the loop. |
|
Destroys the timer, which clears a pending |
|
Clears the pending |
|
Calls |
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::timer::timer
Constructs a timer, not started, registered with the loop.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
timer(
event_loop& loop,
std::pmr::memory_resource* memory);
Parameters
| Name | Description |
|---|---|
loop |
The page’s loop, which must outlive the timer. |
memory |
The room’s resource, which the timer’s registration and handler are allocated in. |
webcpp::trystero::browser::timer::cancel
Clears the pending setTimeout, if any, whose handler is then never called.
webcpp::trystero::browser::timer::start
Calls fire once delay has passed, through setTimeout, in place of a pending expiry.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
start(
std::chrono::milliseconds delay,
handler<void()> fire) override;
Parameters
| Name | Description |
|---|---|
delay |
How long to wait. |
fire |
What to call when the timer expires; never called once it is cancelled. |
webcpp::trystero::browser::websocket
A WebSocket over the page’s WebSocket.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
class websocket
: public backend::websocket
Description
A text longer than protocol::max_relay_message_bytes is not copied into WebAssembly: the socket closes, and reports the close. A text that would take the socket’s bufferedAmount beyond protocol::max_queued_relay_bytes, as when a relay stops reading, drops the socket, which reports one close at once. A page has no idle timeout: its WebSocket cannot ping.
Base Classes
| Name | Description |
|---|---|
A WebSocket to a Nostr relay, as the |
Member Functions
| Name | Description |
|---|---|
|
Opens a WebSocket to |
|
Destroys the WebSocket, which closes it without reporting the close. |
|
Closes the socket, which the handler |
|
Sends a text message when the socket is open, its |
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::websocket::websocket
Opens a WebSocket to url.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
websocket(
event_loop& loop,
std::string_view url,
handlers h,
std::pmr::memory_resource* memory);
Description
A URL the page’s WebSocket refuses with an exception (one that does not parse, another scheme, a fragment, mixed content, a policy of the page’s) makes no socket: the handler close reports it after the call, as it reports a relay that cannot be reached.
Parameters
| Name | Description |
|---|---|
loop |
The page’s loop, which must outlive the WebSocket. |
url |
The relay’s URL, read during the call. |
h |
What the WebSocket reports. |
memory |
The room’s resource, which the WebSocket’s handlers, registration and received texts are allocated in. |
webcpp::trystero::browser::websocket::~websocket
Destroys the WebSocket, which closes it without reporting the close.
webcpp::trystero::browser::websocket::close
Closes the socket, which the handler close reports; one never made has reported it.
webcpp::trystero::browser::websocket::send
Sends a text message when the socket is open, its readyState 1; otherwise, or when the socket refuses it, drops it.
Synopsis
Declared in <webcpp/trystero/browser/backend.hpp>
virtual
void
send(std::string_view text) override;
Description
A text that would take the bytes the socket holds unsent beyond protocol::max_queued_relay_bytes drops the socket instead, which reports its close.
Parameters
| Name | Description |
|---|---|
text |
The message, read during the call. |
webcpp::trystero::browser::depth
The function JavaScript calls, trystero_depth, before it enters C++ through trystero_dispatch: the depth trystero_recover restores if the call escapes.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
int
depth(
emscripten::val const& token,
uintptr_t loop);
Return Value
The loop’s depth, or 0 for a destroyed loop.
Parameters
| Name | Description |
|---|---|
token |
The loop’s token, whose |
loop |
The loop’s address, as a number. |
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::dispatch
The one function JavaScript calls, trystero_dispatch, which browser/src.hpp exports.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
void
dispatch(
emscripten::val const& token,
uintptr_t loop,
int id,
int slot,
emscripten::val event);
Description
Events for a destroyed loop are dropped. With exceptions, a C++ exception that escapes the callback stops here: it is reported to the page, as event_loop::handler says, and the loop recovers.
Parameters
| Name | Description |
|---|---|
token |
The loop’s token, whose |
loop |
The loop’s address, as a number. |
id |
The registration’s id. |
slot |
The slot the event came through. |
event |
The event. |
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::recover
The function JavaScript calls, trystero_recover, when an exception escaped trystero_dispatch: the loop recovers, as event_loop::recover says.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
void
recover(
emscripten::val const& token,
uintptr_t loop,
int depth);
Description
Calls for a destroyed loop do nothing.
Parameters
| Name | Description |
|---|---|
token |
The loop’s token, whose |
loop |
The loop’s address, as a number. |
depth |
The loop’s depth when the call that escaped entered, as |
See Also
"The browser backend", in the guide.
webcpp::trystero::browser::operator==
Whether two executors are of one loop.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
|
Note
|
This function is a hidden friend of executor, declared only inside that class: it can be found only through argument-dependent lookup. |
Return Value
Whether they run work on the same loop.
Parameters
| Name | Description |
|---|---|
a |
An executor. |
b |
Another. |
webcpp::trystero::browser::operator!=
Whether two executors are of different loops.
Synopsis
Declared in <webcpp/trystero/browser/event_loop.hpp>
|
Note
|
This function is a hidden friend of executor, declared only inside that class: it can be found only through argument-dependent lookup. |
Return Value
Whether they run work on different loops.
Parameters
| Name | Description |
|---|---|
a |
An executor. |
b |
Another. |
webcpp::trystero::native
The native target: the backend interface over libdatachannel for WebRTC, Boost.Beast for the relays’ WebSockets, OpenSSL for TLS and AES-GCM, and Asio for timers. Everything it reports runs on the executor it is given; the application runs that executor’s context, blocking on it or polling it.
Types
| Name | Description |
|---|---|
The native factory: one per application, beside the executor it runs on. |
|
What a native backend makes its peer connections with, beside the app’s |
|
A data channel of libdatachannel, the native |
|
A peer connection of libdatachannel, the native |
|
AES-GCM under one room key, through OpenSSL, in Trystero’s sealed text form, what Trystero’s |
|
An Asio timer whose waits are allocated in the room’s resource. |
|
What a native WebSocket tolerates of a relay that stops reading or answering. |
Functions
| Name | Description |
|---|---|
Connects to a Nostr relay over Boost.Beast: |
webcpp::trystero::native::backend
The native factory: one per application, beside the executor it runs on.
Synopsis
Declared in <webcpp/trystero/native/backend.hpp>
class backend
: public trystero::backend::factory
Description
It makes peer connections, data channels and received frames in app_config::memory, and WebSockets, sealers and room timers in the room’s effective room_options::memory. Objects and their handlers use the resource supplied to their factory method. What libdatachannel and OpenSSL allocate stays theirs. Its peer connections need exceptions: without them (WEBCPP_TRYSTERO_NO_EXCEPTIONS) it does not exist.
Base Classes
| Name | Description |
|---|---|
Makes everything above, and gives a room its randomness and its clocks: one per page in the browser, one per application natively, one per test. |
Member Functions
| Name | Description |
|---|---|
|
A backend whose TLS verifies relays against the system’s certificate authorities. |
|
Opens a WebSocket to a relay over Boost.Beast, with |
|
Makes a peer connection of libdatachannel, bound to |
|
Makes a sealer over OpenSSL’s AES-256-GCM. |
|
Makes an Asio timer on the backend’s executor. |
|
Milliseconds from the standard library’s steady clock. |
|
Fills |
The TLS configuration of the relays’ connections, for an application with authorities of its own. |
|
|
Seconds since the Unix epoch, from the system’s wall clock. |
See Also
"The native backend", in the guide.
webcpp::trystero::native::backend::backend
A backend whose TLS verifies relays against the system’s certificate authorities.
Synopsis
Declared in <webcpp/trystero/native/backend.hpp>
explicit
backend(
boost::asio::any_io_executor ex,
backend_config config = {});
Exceptions
| Name | Thrown on |
|---|---|
|
A |
Parameters
| Name | Description |
|---|---|
ex |
The executor everything the backend reports runs on. |
config |
What its peer connections are made with; the default binds them to no address. |
webcpp::trystero::native::backend::connect
Opens a WebSocket to a relay over Boost.Beast, with connect_websocket.
Synopsis
Declared in <webcpp/trystero/native/backend.hpp>
virtual
std::shared_ptr<trystero::backend::websocket>
connect(
std::string_view url,
trystero::backend::websocket::handlers h,
std::pmr::memory_resource* memory) override;
Return Value
The WebSocket.
Parameters
| Name | Description |
|---|---|
url |
The relay’s |
h |
What the WebSocket reports. |
memory |
The room’s resource, which the WebSocket, its handlers and its queued texts are allocated in. |
webcpp::trystero::native::backend::make_peer_connection
Makes a peer connection of libdatachannel, bound to backend_config::bind_address.
Synopsis
Declared in <webcpp/trystero/native/backend.hpp>
virtual
std::shared_ptr<trystero::backend::peer_connection>
make_peer_connection(
std::span<trystero::backend::ice_server const> servers,
std::pmr::memory_resource* memory) override;
Return Value
The connection, or null when the backend made backend_config::max_peer_connections_per_minute in the last minute, or when libdatachannel refuses to make one, by throwing.
Parameters
| Name | Description |
|---|---|
servers |
The STUN and TURN servers, read during the call. |
memory |
The app’s resource, which the connection, its channels, its handlers and its texts are allocated in. |
webcpp::trystero::native::backend::make_sealer
Makes a sealer over OpenSSL’s AES-256-GCM.
Synopsis
Declared in <webcpp/trystero/native/backend.hpp>
virtual
std::shared_ptr<trystero::backend::sealer>
make_sealer(
std::array<uint8_t, 32> key,
std::pmr::memory_resource* memory) override;
Return Value
The sealer.
Parameters
| Name | Description |
|---|---|
key |
The raw AES-256 key. |
memory |
The room’s resource, which the sealer, its texts and its completions are allocated in. |
webcpp::trystero::native::backend::make_timer
Makes an Asio timer on the backend’s executor.
Synopsis
Declared in <webcpp/trystero/native/backend.hpp>
virtual
std::shared_ptr<trystero::backend::timer>
make_timer(std::pmr::memory_resource* memory) override;
Return Value
The timer, not started.
Parameters
| Name | Description |
|---|---|
memory |
The room’s resource, which the timer and its waits are allocated in. |
webcpp::trystero::native::backend::now_ms
Milliseconds from the standard library’s steady clock.
Return Value
The current time, in milliseconds since the clock’s epoch.
webcpp::trystero::native::backend::random
Fills out from OpenSSL’s random generator; the program aborts when it has no secure randomness.
Synopsis
Declared in <webcpp/trystero/native/backend.hpp>
virtual
void
random(std::span<uint8_t> out) override;
Parameters
| Name | Description |
|---|---|
out |
The bytes to fill. |
webcpp::trystero::native::backend::tls_context
The TLS configuration of the relays’ connections, for an application with authorities of its own.
Return Value
The context every wss:// connection of the backend is made with.
webcpp::trystero::native::backend::unix_seconds
Seconds since the Unix epoch, from the system’s wall clock.
Return Value
The current time, in whole seconds.
webcpp::trystero::native::backend_config
What a native backend makes its peer connections with, beside the app’s app_config::ice_servers.
Data Members
| Name | Description |
|---|---|
The local address every peer connection binds to, as |
|
The most peer connections the backend makes in any minute, for every app and room it serves together: beyond, it makes none, and a room reports each negotiation it cannot start. |
|
What the relays’ WebSockets tolerate of a relay that stops reading or answering. |
See Also
"The native backend", in the guide.
webcpp::trystero::native::backend_config::bind_address
The local address every peer connection binds to, as "127.0.0.1"; empty means every interface but the loopback.
Description
Bound to the loopback, the peers of one machine connect with no other network, which a test wants; a peer bound there reaches no other machine.
webcpp::trystero::native::backend_config::max_peer_connections_per_minute
The most peer connections the backend makes in any minute, for every app and room it serves together: beyond, it makes none, and a room reports each negotiation it cannot start.
Synopsis
Declared in <webcpp/trystero/native/backend.hpp>
std::size_t max_peer_connections_per_minute = 256;
Description
Tip: a room makes at most 64 in any 57 s however its peers behave, so the default lets four rooms do so at once; an application with more rooms raises it.
webcpp::trystero::native::backend_config::relay_limits
What the relays’ WebSockets tolerate of a relay that stops reading or answering.
webcpp::trystero::native::data_channel
A data channel of libdatachannel, the native RTCDataChannel.
Synopsis
Declared in <webcpp/trystero/native/data_channel.hpp>
class data_channel
: public backend::data_channel
, public std::enable_shared_from_this<data_channel>
Description
Its events arrive on libdatachannel’s threads; each is posted to the executor and handled there, through a weak reference, so a late event for a destroyed channel does nothing. It exists only with exceptions: libdatachannel reports errors by throwing.
A text message, as a peer can send, or a binary one larger than any room frame (protocol::max_room_frame_bytes), is dropped on libdatachannel’s thread: neither can be a frame. Binary frames received before the message handler is installed wait in the app’s resource, in order, up to 64 frames for 10 seconds. Exceeding either bound closes the channel. Installing a receiver posts their delivery, so handler registration never calls it inline.
Tip: a received message is copied once, out of libdatachannel’s std::vector<std::byte> into the backend’s bytes, in the room’s resource, on the executor: what runs on libdatachannel’s threads touches no resource of the room, and its posts carry Asio’s default allocator.
Base Classes
| Name | Description |
|---|---|
A WebRTC data channel, as the |
|
|
Member Functions
| Name | Description |
|---|---|
|
Constructs the wrapper; only |
|
Destroys the wrapper, which closes the channel without reporting the close. |
|
The bytes queued to send. |
|
Closes the channel, which the handler |
|
Whether the channel is open. |
|
Sends a binary message; one the channel refuses, closed in between, is dropped, and its close event reports the close. |
|
Replaces the handlers, kept in a shared object so that one replacing them during its own call outlives the call. |
Static Member Functions
| Name | Description |
|---|---|
Wraps a channel of libdatachannel, and takes over its events. |
webcpp::trystero::native::data_channel::data_channel
Constructs the wrapper; only create can, which then takes over the channel’s events.
Synopsis
Declared in <webcpp/trystero/native/data_channel.hpp>
data_channel(
private_tag,
boost::asio::any_io_executor ex,
std::shared_ptr<rtc::DataChannel> channel,
std::pmr::memory_resource* memory);
Parameters
| Name | Description |
|---|---|
ex |
The executor every handler of the channel runs on. |
channel |
libdatachannel’s channel. |
memory |
The resource its handlers and its frames are allocated in. |
webcpp::trystero::native::data_channel::~data_channel
Destroys the wrapper, which closes the channel without reporting the close.
webcpp::trystero::native::data_channel::buffered_amount
The bytes queued to send.
Synopsis
Declared in <webcpp/trystero/native/data_channel.hpp>
virtual
std::size_t
buffered_amount() const override;
Return Value
libdatachannel’s bufferedAmount().
webcpp::trystero::native::data_channel::close
Closes the channel, which the handler close reports; a refusal is ignored.
webcpp::trystero::native::data_channel::is_open
Whether the channel is open.
Synopsis
Declared in <webcpp/trystero/native/data_channel.hpp>
virtual
bool
is_open() const override;
Return Value
Whether this wrapper has not closed and libdatachannel’s channel is open.
webcpp::trystero::native::data_channel::send
Sends a binary message; one the channel refuses, closed in between, is dropped, and its close event reports the close.
Synopsis
Declared in <webcpp/trystero/native/data_channel.hpp>
virtual
void
send(std::span<uint8_t const> frame) override;
Parameters
| Name | Description |
|---|---|
frame |
The message, read during the call. |
webcpp::trystero::native::data_channel::set_handlers
Replaces the handlers, kept in a shared object so that one replacing them during its own call outlives the call.
Synopsis
Declared in <webcpp/trystero/native/data_channel.hpp>
virtual
void
set_handlers(handlers h) override;
Parameters
| Name | Description |
|---|---|
h |
The new handlers, kept in the channel’s resource. |
webcpp::trystero::native::data_channel::create
Wraps a channel of libdatachannel, and takes over its events.
Synopsis
Declared in <webcpp/trystero/native/data_channel.hpp>
static
std::shared_ptr<data_channel>
create(
boost::asio::any_io_executor ex,
std::shared_ptr<rtc::DataChannel> channel,
std::pmr::memory_resource* memory);
Return Value
The channel.
Parameters
| Name | Description |
|---|---|
ex |
The executor every handler of the channel runs on. |
channel |
libdatachannel’s channel. |
memory |
The resource the wrapper, its handlers and its frames are allocated in. |
webcpp::trystero::native::peer_connection
A peer connection of libdatachannel, the native RTCPeerConnection, with its negotiation driven by the signaling.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
class peer_connection
: public backend::peer_connection
, public std::enable_shared_from_this<peer_connection>
Description
Automatic negotiation is off, so an offer or an answer exists only when set_local_description asks for one, as in the browser. It exists only with exceptions: libdatachannel reports errors by throwing.
| libdatachannel | Here | | events, on its own threads | posted to the executor, through a weak reference | | exceptions on refused input | error::backend_failure | | Candidate | the JSON of an RTCIceCandidateInit, as the browser writes it |
Tip: the connection, its handlers and the texts it hands to the app are in the app’s resource, and so are the rtc::PeerConnection object and its control block, which create makes with std::allocate_shared; what that object allocates inside libdatachannel, the data channels it creates, and the std::string and rtc::Description its API takes and returns, are libdatachannel’s.
Base Classes
| Name | Description |
|---|---|
A WebRTC peer connection, as the |
|
|
Type Aliases
| Name | Description |
|---|---|
What |
Member Functions
| Name | Description |
|---|---|
|
Constructs the wrapper; only |
|
Destroys the wrapper, which closes the connection without reporting the close. |
|
Adds a remote ICE candidate; the empty candidate that ends the remote gathering adds none. |
|
Closes the connection and its channels. |
|
Opens a data channel. |
|
Whether a remote description was applied. |
libdatachannel’s own |
|
|
Replaces the handlers, kept in a shared object so that one replacing them during its own call outlives the call. |
|
Creates the offer or the answer the signaling state calls for and applies it. |
|
Applies the remote peer’s offer or answer. |
Static Member Functions
| Name | Description |
|---|---|
Makes a peer connection that gathers through |
See Also
"The native backend", in the guide.
webcpp::trystero::native::peer_connection::description_handler
What set_local_description completes with: an error, or the type and the SDP.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
using description_handler = handler<void(boost::system::error_code, std::string_view, std::string_view)>;
webcpp::trystero::native::peer_connection::peer_connection
Constructs the wrapper; only create can, which then takes over the connection’s events.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
peer_connection(
private_tag,
boost::asio::any_io_executor ex,
std::shared_ptr<rtc::PeerConnection> pc,
std::pmr::memory_resource* memory);
Parameters
| Name | Description |
|---|---|
ex |
The executor every handler of the connection runs on. |
pc |
libdatachannel’s connection. |
memory |
The resource its handlers and its texts are allocated in. |
webcpp::trystero::native::peer_connection::~peer_connection
Destroys the wrapper, which closes the connection without reporting the close.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
virtual
~peer_connection() override;
webcpp::trystero::native::peer_connection::add_ice_candidate
Adds a remote ICE candidate; the empty candidate that ends the remote gathering adds none.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
virtual
void
add_ice_candidate(
std::string_view candidate_json,
handler<void(boost::system::error_code)> done) override;
Parameters
| Name | Description |
|---|---|
candidate_json |
The JSON of an |
done |
Called on the executor once it is added, or with |
webcpp::trystero::native::peer_connection::close
Closes the connection and its channels.
webcpp::trystero::native::peer_connection::create_data_channel
Opens a data channel.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
virtual
std::shared_ptr<backend::data_channel>
create_data_channel(std::string_view label) override;
Return Value
The channel, made in the connection’s resource, or null when libdatachannel refuses to open one, as on a closed connection.
Parameters
| Name | Description |
|---|---|
label |
The channel’s label, read during the call. |
webcpp::trystero::native::peer_connection::has_remote_description
Whether a remote description was applied.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
virtual
bool
has_remote_description() const override;
Return Value
Whether libdatachannel’s connection has a remote description.
webcpp::trystero::native::peer_connection::native_handle
libdatachannel’s own rtc::PeerConnection, what media will be added to.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
std::shared_ptr<rtc::PeerConnection> const&
native_handle() const;
Description
The object and its control block are in the app’s resource: no copy of the pointer may outlive app_config::memory.
Return Value
The connection.
See Also
"Roadmap", in the guide.
webcpp::trystero::native::peer_connection::set_handlers
Replaces the handlers, kept in a shared object so that one replacing them during its own call outlives the call.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
virtual
void
set_handlers(handlers h) override;
Parameters
| Name | Description |
|---|---|
h |
The new handlers, kept in the connection’s resource. |
webcpp::trystero::native::peer_connection::set_local_description
Creates the offer or the answer the signaling state calls for and applies it.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
virtual
void
set_local_description(description_handler done) override;
Parameters
| Name | Description |
|---|---|
done |
Called on the executor with the description’s type and SDP, or with |
webcpp::trystero::native::peer_connection::set_remote_description
Applies the remote peer’s offer or answer.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
virtual
void
set_remote_description(
std::string_view type,
std::string_view sdp,
handler<void(boost::system::error_code)> done) override;
Parameters
| Name | Description |
|---|---|
type |
"offer" or "answer", read during the call. |
sdp |
The description’s SDP, read during the call. |
done |
Called on the executor once it is applied, or with |
webcpp::trystero::native::peer_connection::create
Makes a peer connection that gathers through servers, bound to bind_address when it is not empty.
Synopsis
Declared in <webcpp/trystero/native/peer_connection.hpp>
static
std::shared_ptr<peer_connection>
create(
boost::asio::any_io_executor ex,
std::span<backend::ice_server const> servers,
std::string_view bind_address,
std::pmr::memory_resource* memory);
Description
A connection that is not bound gathers a host candidate on each interface but the loopback, which ICE never offers (RFC 8445, 5.1.1.1); one bound to an address, as "127.0.0.1", gathers on that address alone, which lets two peers of one machine connect with no other network.
Exceptions
| Name | Thrown on |
|---|---|
|
What libdatachannel throws when it cannot make the connection. |
Return Value
The new connection.
Parameters
| Name | Description |
|---|---|
ex |
The executor every handler of the connection runs on. |
servers |
The STUN and TURN servers it gathers through. |
bind_address |
The local address it binds to, as |
memory |
The resource the connection, its handlers and its texts are allocated in. |
webcpp::trystero::native::sealer
AES-GCM under one room key, through OpenSSL, in Trystero’s sealed text form, what Trystero’s encrypt and decrypt do with crypto.subtle.
Description
It works synchronously and posts each completion to the executor, as the backend interface asks: a handler never runs inside the call that started it. Its texts and completions are in the room’s resource; OpenSSL’s cipher context is OpenSSL’s. It needs no exceptions.
Base Classes
| Name | Description |
|---|---|
AES-GCM under one room key, in Trystero’s sealed text form (protocol/signal.hpp), what Trystero’s |
Type Aliases
| Name | Description |
|---|---|
What a seal or an open completes with: an error, or the text, a view valid for the call. |
Member Functions
See Also
"The native backend", in the guide.
webcpp::trystero::native::sealer::done_handler
What a seal or an open completes with: an error, or the text, a view valid for the call.
Synopsis
Declared in <webcpp/trystero/native/sealer.hpp>
using done_handler = handler<void(boost::system::error_code, std::string_view)>;
webcpp::trystero::native::sealer::sealer
Constructs a sealer under a room’s key.
Synopsis
Declared in <webcpp/trystero/native/sealer.hpp>
sealer(
boost::asio::any_io_executor ex,
std::array<uint8_t, 32> key,
std::pmr::memory_resource* memory);
Parameters
| Name | Description |
|---|---|
ex |
The executor its completions are posted to. |
key |
The raw AES-256 key, |
memory |
The room’s resource, which its texts and its completions are allocated in; it must outlive the completions it posts. |
webcpp::trystero::native::sealer::open
Opens a sealed text.
Synopsis
Declared in <webcpp/trystero/native/sealer.hpp>
virtual
void
open(
std::string_view sealed,
done_handler done) override;
Parameters
| Name | Description |
|---|---|
sealed |
The sealed text, read during the call. |
done |
Called on the executor with the plaintext, or with |
webcpp::trystero::native::sealer::seal
Seals a text under a fresh IV from OpenSSL’s random generator.
Synopsis
Declared in <webcpp/trystero/native/sealer.hpp>
virtual
void
seal(
std::string_view plaintext,
done_handler done) override;
Parameters
| Name | Description |
|---|---|
plaintext |
The text, read during the call. |
done |
Called on the executor with the sealed text, or with |
webcpp::trystero::native::timer
An Asio timer whose waits are allocated in the room’s resource.
Synopsis
Declared in <webcpp/trystero/native/timer.hpp>
class timer
: public trystero::backend::timer
Description
Tip: Asio cannot take back an expiry it has already queued, which it then completes as a success; so each wait carries its number, and cancel, a later start and the destructor end it, by changing the number or freeing it, before its handler runs.
Base Classes
| Name | Description |
|---|---|
A one-shot timer, what the |
Member Functions
See Also
"The native backend", in the guide.
webcpp::trystero::native::timer::timer
Constructs a timer, not started.
Synopsis
Declared in <webcpp/trystero/native/timer.hpp>
timer(
boost::asio::any_io_executor const& ex,
std::pmr::memory_resource* memory);
Parameters
| Name | Description |
|---|---|
ex |
The executor it fires on. |
memory |
The room’s resource, which each wait is allocated in; it must outlive the timer’s pending waits. |
webcpp::trystero::native::timer::cancel
Cancels the pending expiry, if any, whose handler is then never called.
webcpp::trystero::native::timer::start
Calls fire on the executor once delay has passed, in place of a pending expiry.
Synopsis
Declared in <webcpp/trystero/native/timer.hpp>
virtual
void
start(
std::chrono::milliseconds delay,
handler<void()> fire) override;
Parameters
| Name | Description |
|---|---|
delay |
How long to wait. |
fire |
What to call when the timer expires; never called once the timer is cancelled, started again or destroyed, even when its expiry was already queued. |
webcpp::trystero::native::websocket_limits
What a native WebSocket tolerates of a relay that stops reading or answering.
Data Members
| Name | Description |
|---|---|
How long the connection may stay silent: half of it, a keep-alive ping is sent, and a relay that answers nothing within the whole of it is closed, which reports a close. |
|
The most bytes of text queued to write: a relay that stops reading lets them pile up, and a text beyond this drops the connection, which reports a close, so that the room reconnects with its backoff. |
See Also
"The native backend", in the guide.
webcpp::trystero::native::websocket_limits::idle_timeout
How long the connection may stay silent: half of it, a keep-alive ping is sent, and a relay that answers nothing within the whole of it is closed, which reports a close.
Synopsis
Declared in <webcpp/trystero/native/websocket.hpp>
std::chrono::seconds idle_timeout = std::chrono::seconds(30);
Description
It counts the bytes received, and a ping waits for the write in flight. Native only: a page’s WebSocket cannot ping, so the browser backend has no idle timeout.
webcpp::trystero::native::websocket_limits::max_queued_bytes
The most bytes of text queued to write: a relay that stops reading lets them pile up, and a text beyond this drops the connection, which reports a close, so that the room reconnects with its backoff.
Synopsis
Declared in <webcpp/trystero/native/websocket.hpp>
std::size_t max_queued_bytes = protocol::max_queued_relay_bytes;
webcpp::trystero::native::connect_websocket
Connects to a Nostr relay over Boost.Beast: ws:// in the clear, wss:// over TLS with tls.
Synopsis
Declared in <webcpp/trystero/native/websocket.hpp>
std::shared_ptr<backend::websocket>
connect_websocket(
boost::asio::any_io_executor const& ex,
boost::asio::ssl::context& tls,
std::string_view url,
backend::websocket::handlers h,
std::pmr::memory_resource* memory,
websocket_limits limits = {});
Description
Writes are queued, one in flight at a time; a received message is handed over as a view of the read buffer, and one longer than protocol::max_relay_message_bytes closes the connection. A relay that stops reading is dropped once the queue would pass limits.max_queued_bytes, and one that stops answering once limits.idle_timeout passes with its keep-alive ping unanswered; both report a close. A URL it cannot parse reports a close, as an unreachable relay does. It needs no exceptions.
Return Value
The WebSocket; destroying it drops the connection without reporting it.
Parameters
| Name | Description |
|---|---|
ex |
The executor every handler runs on. |
tls |
The TLS context of |
url |
The relay’s URL, read during the call. |
h |
What the WebSocket reports. |
memory |
The room’s resource, which the connection, its handlers and its queued texts are allocated in; Beast’s stream state and the coroutines’ frames are Asio’s. |
limits |
What it tolerates of a relay that stops reading or answering. |
See Also
"The native backend", in the guide.
webcpp::trystero::protocol
Namespaces
| Name | Description |
|---|---|
Reading and writing the protocol’s JSON texts, with every allocation in the resource the caller gives. A text the protocol writes is a fixed shape of a few strings and numbers, so it is appended field by field rather than built as a boost::json::value, which would copy every string into a node of its own before serializing it again. |
|
Types
| Name | Description |
|---|---|
A decoded chunk, as a view into the frame. |
|
A decoded accepted, rejected or cancelled frame, as a view into the frame. |
|
What |
|
A decoded room envelope, as views into the frame: every data channel message names the room it belongs to, so one connection can serve several rooms of the same app. |
|
A decoded inline frame or offer, as views into the frame. |
|
A sealed signal’s parts, as |
|
A message on a signaling topic, the JSON object Trystero’s signal handler reads, as views. |
Type Aliases
| Name | Description |
|---|---|
Bytes, in the memory resource a room or the caller gives. |
|
A decoded action frame: a proposal, a chunk or a transfer’s answer. |
Enums
| Name | Description |
|---|---|
The kind of an action frame, the low four bits of its second byte, in Trystero’s action wire, version 2. |
|
The format of an action’s payload, the high four bits of a frame’s second byte: how Trystero’s |
|
Why a frame cannot be decoded; Trystero drops the peer for any of these. |
Functions
| Name | Description |
|---|---|
Appends the frame accepting transfer |
|
Appends standard base64 with padding, what |
|
Appends the frame cancelling transfer |
|
Appends a chunk of transfer |
|
Appends the lowercase hexadecimal of a byte sequence, Trystero’s |
|
Appends an inline frame to |
|
Appends an offer frame announcing |
|
Appends the frame rejecting transfer |
|
The bytes of a base64 text, padded or not, as |
|
Standard base64 with padding, what |
|
Starts a room frame in an empty buffer: the header for |
|
Decodes one action frame with exactly the checks Trystero’s |
|
Decodes the envelope of a frame, with the checks of Trystero’s |
|
The relays an app uses when it names none, Trystero’s |
|
The frame accepting a transfer, in a buffer of its own. |
|
The frame cancelling a transfer, in a buffer of its own. |
|
A chunk frame, in a buffer of its own. |
|
An inline frame, in a buffer of its own. |
|
An offer frame, in a buffer of its own. |
|
The frame rejecting a transfer, in a buffer of its own. |
|
Whether a payload with this metadata travels inline rather than as an offer and chunks. |
|
The text form of a sealed signal, what Trystero’s |
|
The bytes of a hexadecimal text, in either case. |
|
The password handshake’s proof, Trystero’s |
|
A random id of |
|
The parts of a sealed signal’s text, as Trystero’s |
|
The topic a peer receives its offers, answers and candidates on, Trystero’s |
|
A presence frame, Trystero’s |
|
The message a parsed signaling payload holds, as views into |
|
A room frame, Trystero’s |
|
The token that names a room inside a data channel’s envelope: the hexadecimal SHA-256 of "Trystero:<app>:<room>". |
|
The topic every member of a room announces on, Trystero’s |
|
The raw AES-GCM key of a room, what Trystero’s |
|
The longest text a plaintext seals to, as |
|
Trystero’s |
|
Trystero’s |
|
The SHA-256 of a text. |
|
The SHA-256 of the concatenation of |
|
Trystero’s |
|
Trystero’s |
|
The lowercase hexadecimal of a byte sequence, Trystero’s |
|
The sum of the UTF-16 code units of a UTF-8 text, what JavaScript’s |
|
Whether a name can name an action, as Trystero’s |
|
The JSON text of a message, with its members in Trystero’s order and the empty ones left out. |
Variables
| Name | Description |
|---|---|
The version of the action wire, every frame’s first byte, Trystero’s |
|
The size of a chunk’s header, Trystero’s |
|
The most payload bytes one chunk carries, Trystero’s |
|
The size of an accepted, rejected or cancelled frame’s header, |
|
Trystero 0.26’s default Nostr relays, its |
|
The 62 characters Trystero draws peer ids, offer ids and nonces from, its |
|
The size of an inline frame’s header, Trystero’s |
|
The largest action frame, Trystero’s |
|
The longest action name, in UTF-8 bytes. |
|
The longest offer id a signal may carry, in bytes; Trystero’s are |
|
The longest peer id a room accepts in a signal, in bytes; a signal with a longer one is ignored. |
|
The most text a room holds unsent for one relay, in UTF-8 bytes: 4 MiB, a thousand signals and more. |
|
The longest reason a rejected frame carries, in UTF-8 bytes. |
|
The longest text a room reads from a relay, in UTF-8 bytes: 1 MiB, far beyond any message of the protocol (an event’s content is a signal, whose SDP is a few KiB) and of what public relays accept (their events are capped at 64 to 512 KiB). |
|
The largest room frame, Trystero’s |
|
The longest room token a room frame may carry, in bytes. |
|
The longest sealed candidate a signal may carry, in bytes. |
|
The longest sealed offer or answer a signal may carry, in bytes. |
|
The length of the nonce that makes repeated announcements distinct Nostr events. |
|
The size of an offer’s header, Trystero’s |
|
The length of the id of a Trystero offer, which its answer and candidates name. |
|
The length of a Trystero peer id, its |
|
The size of the IV Trystero’s |
webcpp::trystero::protocol::json
Reading and writing the protocol’s JSON texts, with every allocation in the resource the caller gives. A text the protocol writes is a fixed shape of a few strings and numbers, so it is appended field by field rather than built as a boost::json::value, which would copy every string into a node of its own before serializing it again.
Description
Tip: strings are escaped by Boost.JSON’s serializer, so the bytes match what boost::json::serialize and JavaScript’s JSON.stringify write; a Nostr event id is a hash over exactly these bytes.
Functions
| Name | Description |
|---|---|
Appends an integer in decimal, as JavaScript writes a safe integer. |
|
Appends |
|
Appends |
|
Appends |
|
Parses |
webcpp::trystero::protocol::json::append_integer
Appends an integer in decimal, as JavaScript writes a safe integer.
Synopsis
Declared in <webcpp/trystero/protocol/json.hpp>
void
append_integer(
std::pmr::string& out,
int64_t value);
Parameters
| Name | Description |
|---|---|
out |
The text to append to. |
value |
The integer. |
webcpp::trystero::protocol::json::append_key
Appends "key": with a leading comma unless it opens the object.
Synopsis
Declared in <webcpp/trystero/protocol/json.hpp>
void
append_key(
std::pmr::string& out,
std::string_view key);
Parameters
| Name | Description |
|---|---|
out |
The text of an object being written, which ends with its |
key |
The member’s name. |
webcpp::trystero::protocol::json::append_string
Appends text as a JSON string literal, quotes included, as JSON.stringify writes it.
Synopsis
Declared in <webcpp/trystero/protocol/json.hpp>
void
append_string(
std::pmr::string& out,
std::string_view text);
Description
Tip: the serializer keeps where it stopped on a stack of its own when the text outgrows its buffer; that stack is in out’s resource.
Parameters
| Name | Description |
|---|---|
out |
The text to append to. |
text |
The string, in UTF-8. |
webcpp::trystero::protocol::json::append_value
Appends value serialized, as boost::json::serialize writes it.
Synopsis
Declared in <webcpp/trystero/protocol/json.hpp>
void
append_value(
std::pmr::string& out,
boost::json::value const& value);
Parameters
| Name | Description |
|---|---|
out |
The text to append to; the serializer’s state is in its resource. |
value |
The value. |
webcpp::trystero::protocol::json::parse
Parses text into a value of storage, or sets ec.
Synopsis
Declared in <webcpp/trystero/protocol/json.hpp>
boost::json::value
parse(
std::string_view text,
boost::json::storage_ptr const& storage,
boost::system::error_code& ec);
Description
Tip: boost::json::parse keeps its temporaries in the default resource once they outgrow a buffer on the stack; this parser keeps them in storage too.
Return Value
The value, or a null value of storage when ec is set.
Parameters
| Name | Description |
|---|---|
text |
The JSON text. |
storage |
The storage the value and the parser’s temporaries are allocated in. |
ec |
Set when the text is not one JSON value. |
webcpp::trystero::protocol::nostr
Types
| Name | Description |
|---|---|
A relay’s |
|
A relay’s |
|
A relay’s |
|
A relay’s |
|
A relay’s |
|
A BIP-340 key pair over libsecp256k1, the one key a peer signs every event with, as Trystero’s Nostr strategy does with its |
Type Aliases
| Name | Description |
|---|---|
What a relay sends, as views into the parsed message; anything else is ignored. |
Functions
| Name | Description |
|---|---|
The client message ending a subscription: |
|
The id of a Trystero event, as NIP-01 defines it: the SHA-256 of the JSON array |
|
The client message publishing a signed event on a topic, |
|
The message a parsed relay text holds, as views into |
|
The client message subscribing to topics, Trystero’s |
|
The event kind a topic maps to, Trystero’s |
|
Whether a BIP-340 signature over an event id verifies under an x-only public key. |
Variables
| Name | Description |
|---|---|
The tag a Trystero event carries its topic in, its |
webcpp::trystero::protocol::nostr::relay_message
What a relay sends, as views into the parsed message; anything else is ignored.
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
using relay_message = std::variant<event, ok, eose, closed, notice>;
See Also
"The protocol", in the guide.
webcpp::trystero::protocol::nostr::closed
A relay’s ["CLOSED", <subscription>, <reason>]: the relay ended a subscription.
Data Members
| Name | Description |
|---|---|
Why. |
|
The subscription’s id. |
webcpp::trystero::protocol::nostr::eose
A relay’s ["EOSE", <subscription>]: the stored events of a subscription were all sent.
Data Members
| Name | Description |
|---|---|
The subscription’s id. |
webcpp::trystero::protocol::nostr::event
A relay’s ["EVENT", <subscription>, <event>]: an event of a subscription, as views.
Data Members
| Name | Description |
|---|---|
The event’s content. |
|
The event’s id. |
|
The subscription’s id. |
|
The value of the event’s first "x" tag; empty when there is none. |
webcpp::trystero::protocol::nostr::event::topic
The value of the event’s first "x" tag; empty when there is none.
webcpp::trystero::protocol::nostr::notice
A relay’s ["NOTICE", <text>]: a message for humans.
Data Members
| Name | Description |
|---|---|
The message. |
webcpp::trystero::protocol::nostr::ok
A relay’s ["OK", <id>, <accepted>, <reason>]: whether it took a published event, as views.
webcpp::trystero::protocol::nostr::ok::reason
Why, as "rate-limited: ..." or "blocked: ..."; may be empty.
webcpp::trystero::protocol::nostr::signer
A BIP-340 key pair over libsecp256k1, the one key a peer signs every event with, as Trystero’s Nostr strategy does with its pubkey.
Description
Tip: its signing context is made in the resource it is given (secp256k1_context_preallocated_create), where secp256k1_context_create would take it from malloc.
Member Functions
Static Member Functions
| Name | Description |
|---|---|
Makes a key from 32 random bytes. |
webcpp::trystero::protocol::nostr::signer::pubkey
The x-only public key, an event’s pubkey.
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
[[nodiscard]]
std::string_view
pubkey() const;
Return Value
64 lowercase hexadecimal digits, valid as long as the signer.
|
Note
|
The return value should not be discarded. |
webcpp::trystero::protocol::nostr::signer::sign
Signs a message with BIP-340 Schnorr.
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
[[nodiscard]]
std::array<uint8_t, 64>
sign(
std::span<uint8_t const, 32> message,
std::span<uint8_t const, 32> aux) const;
Parameters
| Name | Description |
|---|---|
message |
The 32 bytes to sign, an event id. |
aux |
32 random bytes of auxiliary randomness. |
webcpp::trystero::protocol::nostr::signer::from_secret
Makes a key from 32 random bytes.
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
static
std::optional<signer>
from_secret(
std::span<uint8_t const, 32> secret,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The signer, or nothing when the bytes are not a valid secret key, which is negligibly rare.
Parameters
| Name | Description |
|---|---|
secret |
The secret key. |
memory |
The resource the signing context and the public key’s text are allocated in; it must outlive the signer. |
webcpp::trystero::protocol::nostr::close_message
The client message ending a subscription: ["CLOSE", id].
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
std::pmr::string
close_message(
std::string_view subscription,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The message’s JSON text.
Parameters
| Name | Description |
|---|---|
subscription |
The subscription’s id. |
memory |
The resource the message is allocated in. |
webcpp::trystero::protocol::nostr::event_id
The id of a Trystero event, as NIP-01 defines it: the SHA-256 of the JSON array [0, pubkey, created_at, kind, [["x", topic]], content].
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
std::array<uint8_t, 32>
event_id(
std::string_view pubkey,
int64_t created_at,
int kind,
std::string_view topic,
std::string_view content,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The id.
Parameters
| Name | Description |
|---|---|
pubkey |
The signer’s x-only public key, in hexadecimal. |
created_at |
The event’s time, in seconds since the Unix epoch. |
kind |
The event’s kind, |
topic |
The topic, the value of its tag "x". |
content |
The content. |
memory |
The resource the escaped topic and content are allocated in, during the call. |
webcpp::trystero::protocol::nostr::event_message
The client message publishing a signed event on a topic, ["EVENT", {...}], what Trystero’s createEvent makes.
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
std::pmr::string
event_message(
signer const& key,
int64_t created_at,
std::string_view topic,
std::string_view content,
std::span<uint8_t const, 32> aux,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Description
It is written once into one buffer. The topic and the content are escaped once; the id is hashed over those same bytes.
Return Value
The message’s JSON text.
Parameters
| Name | Description |
|---|---|
key |
The signer. |
created_at |
The event’s time, in seconds since the Unix epoch. |
topic |
The topic, the value of the event’s tag "x". |
content |
The content, a signal’s JSON text. |
aux |
32 random bytes for the signature. |
memory |
The resource the message is allocated in. |
webcpp::trystero::protocol::nostr::read_relay_message
The message a parsed relay text holds, as views into document.
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
std::optional<relay_message>
read_relay_message(boost::json::value const& document);
Return Value
The message, or nothing when Trystero would ignore it: not an array, or not one of the messages of relay_message.
Parameters
| Name | Description |
|---|---|
document |
The parsed text, which the caller keeps alive while it reads the views. |
webcpp::trystero::protocol::nostr::subscribe_message
The client message subscribing to topics, Trystero’s subscribe: `["REQ", id, {"kinds": [...], "since": t, "#x": [...]}]`.
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
std::pmr::string
subscribe_message(
std::string_view subscription,
std::span<std::pmr::string const> topics,
int64_t since,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The message’s JSON text.
Parameters
| Name | Description |
|---|---|
subscription |
The subscription’s id. |
topics |
The topics, whose kinds the filter lists once each, in increasing order. |
since |
The earliest |
memory |
The resource the message is allocated in. |
webcpp::trystero::protocol::nostr::topic_kind
The event kind a topic maps to, Trystero’s topicToKind: 20000 to 29999, ephemeral, so relays forward the events and keep none.
Return Value
20000 plus str_to_num(topic, 10000).
Parameters
| Name | Description |
|---|---|
topic |
The topic’s name. |
webcpp::trystero::protocol::nostr::verify
Whether a BIP-340 signature over an event id verifies under an x-only public key.
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
bool
verify(
std::span<uint8_t const, 64> sig,
std::span<uint8_t const, 32> id,
std::span<uint8_t const, 32> pubkey);
Description
It uses libsecp256k1’s static context, which allocates nothing.
Return Value
Whether the signature is valid; false too when the key is not a valid point.
Parameters
| Name | Description |
|---|---|
sig |
The signature. |
id |
The event id. |
pubkey |
The x-only public key. |
webcpp::trystero::protocol::nostr::topic_tag
The tag a Trystero event carries its topic in, its tag: "x".
Synopsis
Declared in <webcpp/trystero/protocol/nostr.hpp>
inline constexpr std::string_view topic_tag = "x";
webcpp::trystero::protocol::decoded_action
A decoded action frame: a proposal, a chunk or a transfer’s answer.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
using decoded_action = std::variant<proposal, chunk, control_message>;
webcpp::trystero::protocol::chunk::offset
Where the data starts in the payload, as the float64 the frame carries; NaN when absent.
webcpp::trystero::protocol::control_message
A decoded accepted, rejected or cancelled frame, as a view into the frame.
webcpp::trystero::protocol::control_message::id
The transfer’s id.
webcpp::trystero::protocol::control_message::kind
frame_kind::accepted, frame_kind::rejected or frame_kind::cancelled.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
frame_kind kind = frame_kind::accepted;
webcpp::trystero::protocol::control_message::reason
Why the transfer was rejected; rejected frames only.
webcpp::trystero::protocol::decode_result
What decode_action makes of a frame.
webcpp::trystero::protocol::decode_result::error
Why the frame cannot be decoded; meaningful only without a frame.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
wire_error error = wire_error::invalid_frame;
webcpp::trystero::protocol::decode_result::frame
The decoded frame, or nothing when it cannot be decoded.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
std::optional<decoded_action> frame;
webcpp::trystero::protocol::envelope
A decoded room envelope, as views into the frame: every data channel message names the room it belongs to, so one connection can serve several rooms of the same app.
Description
| Frame | Bytes | | room | 1, token length (uint16 BE), token, action frame | | presence | 2, present (0 or 1), token length (uint16 BE), token |
Enums
| Name | Description |
|---|---|
The kind of a frame, its first byte. |
Data Members
See Also
"The protocol", in the guide.
webcpp::trystero::protocol::envelope::kind::presence
A presence frame, 2: the peer joins or leaves the room.
webcpp::trystero::protocol::proposal
A decoded inline frame or offer, as views into the frame.
Data Members
| Name | Description |
|---|---|
The payload’s format. |
|
The transfer’s id; offers only. |
|
|
|
The metadata, JSON text, or empty. |
|
The action’s name, up to the first NUL of the 32-byte field. |
|
The payload; inline frames only. |
|
The payload’s size; for an inline frame, the bytes that follow the metadata. |
See Also
"The protocol", in the guide.
webcpp::trystero::protocol::proposal::format
The payload’s format.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
payload_format format = payload_format::text;
webcpp::trystero::protocol::proposal::kind
frame_kind::inline_payload or frame_kind::offer.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
frame_kind kind = frame_kind::inline_payload;
webcpp::trystero::protocol::proposal::name
The action’s name, up to the first NUL of the 32-byte field.
webcpp::trystero::protocol::proposal::size
The payload’s size; for an inline frame, the bytes that follow the metadata.
webcpp::trystero::protocol::sealed_parts
A sealed signal’s parts, as parse_sealed reads them.
Data Members
| Name | Description |
|---|---|
The ciphertext, followed by its 16-byte tag. |
|
The IV, 16 bytes in what Trystero writes. |
webcpp::trystero::protocol::sealed_parts::ciphertext
The ciphertext, followed by its 16-byte tag.
webcpp::trystero::protocol::sealed_parts::iv
The IV, 16 bytes in what Trystero writes.
webcpp::trystero::protocol::signal_message
A message on a signaling topic, the JSON object Trystero’s signal handler reads, as views.
Description
With none of offer, answer and candidate it is an announcement: the sender is in the room. Offer, answer and candidate hold sealed text; a candidate’s plaintext is the JSON of an RTCIceCandidateInit.
Member Functions
| Name | Description |
|---|---|
Whether the message is an announcement. |
Data Members
| Name | Description |
|---|---|
The sealed SDP of an answer, |
|
A sealed ICE candidate, |
|
The sealed SDP of an offer, |
|
The offer the message belongs to, |
|
Whether the sender’s room is passive, |
|
The sender’s id, |
See Also
"The protocol", in the guide.
webcpp::trystero::protocol::signal_message::is_announcement
Whether the message is an announcement.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
[[nodiscard]]
bool
is_announcement() const;
Return Value
Whether it holds none of offer, answer and candidate.
|
Note
|
The return value should not be discarded. |
webcpp::trystero::protocol::signal_message::answer
The sealed SDP of an answer, answer.
webcpp::trystero::protocol::signal_message::candidate
A sealed ICE candidate, candidate.
webcpp::trystero::protocol::signal_message::offer
The sealed SDP of an offer, offer.
webcpp::trystero::protocol::signal_message::offer_id
The offer the message belongs to, offerId; empty in an announcement.
webcpp::trystero::protocol::signal_message::passive
Whether the sender’s room is passive, passive: a Trystero room joined with passive announces only once it has seen a peer that is not passive.
webcpp::trystero::protocol::signal_message::peer_id
The sender’s id, peerId: 1 to max_peer_id_bytes bytes.
webcpp::trystero::protocol::frame_kind
The kind of an action frame, the low four bits of its second byte, in Trystero’s action wire, version 2.
Description
A payload that fits one frame travels inline; a larger one is offered, accepted by the receiver, then sent in chunks. Integers and float64 values are big-endian, as DataView writes them.
| Frame | Bytes | | inline (kind 0) | 2, kind or format << 4, name (32 bytes, NUL padded), metadata length | | | (uint16), metadata, payload | | offer (kind 1) | the inline header, transfer id (uint32), payload size (float64), | | | metadata | | chunk (kind 2) | 2, 2, transfer id (uint32), offset (float64), bytes | | accepted (kind 3) | 2, 3, transfer id (uint32) | | rejected (kind 4) | 2, 4, transfer id (uint32), reason (UTF-8, at most 200 bytes) | | cancelled (kind 5) | 2, 5, transfer id (uint32) |
Members
| Name | Description |
|---|---|
A whole payload, Trystero’s |
|
The offer of a transfer, Trystero’s |
|
A piece of a transfer’s payload, Trystero’s |
|
The receiver accepts an offer, Trystero’s |
|
The receiver refuses an offer, Trystero’s |
|
The sender gives a transfer up, Trystero’s |
See Also
"The protocol", in the guide.
webcpp::trystero::protocol::frame_kind::inline_payload
A whole payload, Trystero’s inline.
webcpp::trystero::protocol::frame_kind::offer
The offer of a transfer, Trystero’s offer.
webcpp::trystero::protocol::frame_kind::chunk
A piece of a transfer’s payload, Trystero’s chunk.
webcpp::trystero::protocol::frame_kind::accepted
The receiver accepts an offer, Trystero’s accepted.
webcpp::trystero::protocol::frame_kind::rejected
The receiver refuses an offer, Trystero’s rejected.
webcpp::trystero::protocol::frame_kind::cancelled
The sender gives a transfer up, Trystero’s cancelled.
webcpp::trystero::protocol::payload_format
The format of an action’s payload, the high four bits of a frame’s second byte: how Trystero’s decodePayload reads it.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
enum class payload_format : uint8_t;
Members
See Also
"The protocol", in the guide.
webcpp::trystero::protocol::payload_format::text
UTF-8 text, a JavaScript string.
webcpp::trystero::protocol::payload_format::json
JSON text, any other JavaScript value.
webcpp::trystero::protocol::payload_format::binary
Raw bytes, an ArrayBuffer or a typed array.
webcpp::trystero::protocol::wire_error
Why a frame cannot be decoded; Trystero drops the peer for any of these.
Members
| Name | Description |
|---|---|
Too short, too long, or a kind or a format that does not exist. |
|
A version other than |
|
A chunk or a transfer’s answer of the wrong size. |
|
An inline frame or an offer shorter than its header. |
|
An empty name, metadata beyond the frame, or an offer of an impossible size or length. |
webcpp::trystero::protocol::wire_error::invalid_frame
Too short, too long, or a kind or a format that does not exist.
webcpp::trystero::protocol::wire_error::incompatible_version
A version other than action_wire_version.
webcpp::trystero::protocol::wire_error::invalid_control
A chunk or a transfer’s answer of the wrong size.
webcpp::trystero::protocol::wire_error::invalid_header
An inline frame or an offer shorter than its header.
webcpp::trystero::protocol::wire_error::invalid_proposal
An empty name, metadata beyond the frame, or an offer of an impossible size or length.
webcpp::trystero::protocol::append_accepted
Appends the frame accepting transfer id.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
void
append_accepted(
bytes& out,
uint32_t id);
Parameters
| Name | Description |
|---|---|
out |
The buffer to append to. |
id |
The transfer’s id. |
webcpp::trystero::protocol::append_base64
Appends standard base64 with padding, what btoa writes over binary text.
Synopsis
Declared in <webcpp/trystero/protocol/text.hpp>
void
append_base64(
std::pmr::string& out,
std::span<uint8_t const> in);
Parameters
| Name | Description |
|---|---|
out |
The text to append to. |
in |
The bytes. |
webcpp::trystero::protocol::append_cancelled
Appends the frame cancelling transfer id.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
void
append_cancelled(
bytes& out,
uint32_t id);
Parameters
| Name | Description |
|---|---|
out |
The buffer to append to. |
id |
The transfer’s id. |
webcpp::trystero::protocol::append_chunk
Appends a chunk of transfer id.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
void
append_chunk(
bytes& out,
uint32_t id,
std::size_t offset,
std::span<uint8_t const> data);
Parameters
| Name | Description |
|---|---|
out |
The buffer to append to. |
id |
The transfer’s id. |
offset |
Where |
data |
At most |
webcpp::trystero::protocol::append_hex
Appends the lowercase hexadecimal of a byte sequence, Trystero’s toHex.
Synopsis
Declared in <webcpp/trystero/protocol/text.hpp>
void
append_hex(
std::pmr::string& out,
std::span<uint8_t const> data);
Parameters
| Name | Description |
|---|---|
out |
The text to append to. |
data |
The bytes. |
webcpp::trystero::protocol::append_inline
Appends an inline frame to out, which may already hold a room envelope’s header.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
void
append_inline(
bytes& out,
std::string_view name,
payload_format format,
std::string_view metadata,
std::span<uint8_t const> payload);
Parameters
| Name | Description |
|---|---|
out |
The buffer to append to. |
name |
The action’s name. |
format |
The payload’s format. |
metadata |
JSON text, or empty. |
payload |
The payload. |
Preconditions
-
valid_action_name(name)andfits_inline(metadata.size(), payload.size()).
webcpp::trystero::protocol::append_offer
Appends an offer frame announcing size bytes under transfer id.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
void
append_offer(
bytes& out,
std::string_view name,
payload_format format,
std::string_view metadata,
uint32_t id,
std::size_t size);
Parameters
| Name | Description |
|---|---|
out |
The buffer to append to. |
name |
The action’s name. |
format |
The payload’s format. |
metadata |
JSON text, or empty. |
id |
The transfer’s id. |
size |
The payload’s size in bytes. |
Preconditions
-
valid_action_name(name), and the metadata fits one frame after the offer’s header.
webcpp::trystero::protocol::append_rejected
Appends the frame rejecting transfer id.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
void
append_rejected(
bytes& out,
uint32_t id,
std::string_view reason);
Parameters
| Name | Description |
|---|---|
out |
The buffer to append to. |
id |
The transfer’s id. |
reason |
Why, cut to |
webcpp::trystero::protocol::base64_decode
The bytes of a base64 text, padded or not, as atob reads it.
Synopsis
Declared in <webcpp/trystero/protocol/text.hpp>
std::optional<bytes>
base64_decode(
std::string_view in,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The bytes, or nothing when the text is not base64.
Parameters
| Name | Description |
|---|---|
in |
The text. |
memory |
The resource the bytes are allocated in. |
webcpp::trystero::protocol::base64_encode
Standard base64 with padding, what btoa writes over binary text.
Synopsis
Declared in <webcpp/trystero/protocol/text.hpp>
std::pmr::string
base64_encode(
std::span<uint8_t const> in,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The text.
Parameters
| Name | Description |
|---|---|
in |
The bytes. |
memory |
The resource the text is allocated in. |
webcpp::trystero::protocol::begin_room_frame
Starts a room frame in an empty buffer: the header for token, with room reserved for payload_size more bytes.
Synopsis
Declared in <webcpp/trystero/protocol/envelope.hpp>
bytes
begin_room_frame(
std::string_view token,
std::size_t payload_size,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Description
The action frame is then appended in place, so the frame is allocated and written once.
Return Value
The frame’s header.
Parameters
| Name | Description |
|---|---|
token |
The room’s token, |
payload_size |
The size of the action frame that will follow. |
memory |
The resource the frame is allocated in. |
webcpp::trystero::protocol::decode_action
Decodes one action frame with exactly the checks Trystero’s handleData makes.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
decode_result
decode_action(std::span<uint8_t const> bytes_in);
Return Value
The frame, as views into bytes_in, or why it cannot be decoded.
Parameters
| Name | Description |
|---|---|
bytes_in |
The action frame, out of its room envelope. |
webcpp::trystero::protocol::decode_envelope
Decodes the envelope of a frame, with the checks of Trystero’s unwrapFrame.
Synopsis
Declared in <webcpp/trystero/protocol/envelope.hpp>
std::optional<envelope>
decode_envelope(std::span<uint8_t const> frame);
Return Value
The envelope, or nothing when the frame is malformed; Trystero drops the peer then.
Parameters
| Name | Description |
|---|---|
frame |
A data channel message. |
webcpp::trystero::protocol::derive_relays
The relays an app uses when it names none, Trystero’s getRelays: the defaults shuffled by the app id, the first redundancy kept.
Synopsis
Declared in <webcpp/trystero/protocol/relays.hpp>
std::pmr::vector<std::string_view>
derive_relays(
std::string_view app_id,
std::span<std::string_view const> defaults,
std::size_t redundancy,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The relays, as views into defaults.
Parameters
| Name | Description |
|---|---|
app_id |
The application’s id, whose |
defaults |
The relays to choose from, |
redundancy |
How many to keep. |
memory |
The resource the list is allocated in. |
webcpp::trystero::protocol::encode_accepted
The frame accepting a transfer, in a buffer of its own.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
bytes
encode_accepted(
uint32_t id,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The frame.
Parameters
| Name | Description |
|---|---|
id |
The transfer’s id. |
memory |
The resource the frame is allocated in. |
webcpp::trystero::protocol::encode_cancelled
The frame cancelling a transfer, in a buffer of its own.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
bytes
encode_cancelled(
uint32_t id,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The frame.
Parameters
| Name | Description |
|---|---|
id |
The transfer’s id. |
memory |
The resource the frame is allocated in. |
webcpp::trystero::protocol::encode_chunk
A chunk frame, in a buffer of its own.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
bytes
encode_chunk(
uint32_t id,
std::size_t offset,
std::span<uint8_t const> data,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The frame, as append_chunk writes it.
Parameters
| Name | Description |
|---|---|
id |
The transfer’s id. |
offset |
Where |
data |
At most |
memory |
The resource the frame is allocated in. |
webcpp::trystero::protocol::encode_inline
An inline frame, in a buffer of its own.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
bytes
encode_inline(
std::string_view name,
payload_format format,
std::string_view metadata,
std::span<uint8_t const> payload,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The frame, as append_inline writes it.
Parameters
| Name | Description |
|---|---|
name |
The action’s name. |
format |
The payload’s format. |
metadata |
JSON text, or empty. |
payload |
The payload. |
memory |
The resource the frame is allocated in. |
webcpp::trystero::protocol::encode_offer
An offer frame, in a buffer of its own.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
bytes
encode_offer(
std::string_view name,
payload_format format,
std::string_view metadata,
uint32_t id,
std::size_t size,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The frame, as append_offer writes it.
Parameters
| Name | Description |
|---|---|
name |
The action’s name. |
format |
The payload’s format. |
metadata |
JSON text, or empty. |
id |
The transfer’s id. |
size |
The payload’s size in bytes. |
memory |
The resource the frame is allocated in. |
webcpp::trystero::protocol::encode_rejected
The frame rejecting a transfer, in a buffer of its own.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
bytes
encode_rejected(
uint32_t id,
std::string_view reason,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The frame.
Parameters
| Name | Description |
|---|---|
id |
The transfer’s id. |
reason |
Why, cut to |
memory |
The resource the frame is allocated in. |
webcpp::trystero::protocol::fits_inline
Whether a payload with this metadata travels inline rather than as an offer and chunks.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
bool
fits_inline(
std::size_t metadata_size,
std::size_t payload_size);
Return Value
Whether the inline frame fits max_action_frame_bytes.
Parameters
| Name | Description |
|---|---|
metadata_size |
The metadata’s size in bytes. |
payload_size |
The payload’s size in bytes. |
webcpp::trystero::protocol::format_sealed
The text form of a sealed signal, what Trystero’s encrypt returns: the IV’s bytes in decimal joined by commas, "$", the ciphertext in base64.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
std::pmr::string
format_sealed(
std::span<uint8_t const> iv,
std::span<uint8_t const> ciphertext,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The text.
Parameters
| Name | Description |
|---|---|
iv |
The IV. |
ciphertext |
The ciphertext with its tag, as |
memory |
The resource the text is allocated in. |
webcpp::trystero::protocol::from_hex
The bytes of a hexadecimal text, in either case.
Synopsis
Declared in <webcpp/trystero/protocol/text.hpp>
std::optional<bytes>
from_hex(
std::string_view text,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The bytes, or nothing when the text is not hexadecimal of even length.
Parameters
| Name | Description |
|---|---|
text |
The text. |
memory |
The resource the bytes are allocated in. |
webcpp::trystero::protocol::handshake_proof
The password handshake’s proof, Trystero’s hashChallenge: the hexadecimal SHA-256 of "<challenge>:<password>:<app>:<room>".
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
std::pmr::string
handshake_proof(
std::string_view challenge,
std::string_view password,
std::string_view app_id,
std::string_view room_id,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The proof, 64 hexadecimal digits.
Parameters
| Name | Description |
|---|---|
challenge |
The challenge the other peer sent. |
password |
The password used for this room, shared by its app. |
app_id |
The application’s id. |
room_id |
The room’s id. |
memory |
The resource the proof is allocated in. |
webcpp::trystero::protocol::make_id
A random id of n characters of id_alphabet, Trystero’s genId.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
template<class Generator>
std::pmr::string
make_id(
std::size_t n,
Generator& generator,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The id.
Template Parameters
| Name | Description |
|---|---|
Generator |
A uniform random bit generator. |
Parameters
| Name | Description |
|---|---|
n |
The id’s length. |
generator |
The source of randomness. |
memory |
The resource the id is allocated in. |
webcpp::trystero::protocol::parse_sealed
The parts of a sealed signal’s text, as Trystero’s decrypt splits it.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
std::optional<sealed_parts>
parse_sealed(
std::string_view text,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The parts, or nothing when the text has no "$", an IV byte that is not a decimal number up to 255, or a ciphertext that is not base64.
Parameters
| Name | Description |
|---|---|
text |
The text. |
memory |
The resource the parts are allocated in. |
webcpp::trystero::protocol::peer_topic
The topic a peer receives its offers, answers and candidates on, Trystero’s sha1 of the root topic’s text, "@" and the peer’s id.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
std::pmr::string
peer_topic(
std::string_view app_id,
std::string_view room_id,
std::string_view peer_id,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The topic’s name.
Parameters
| Name | Description |
|---|---|
app_id |
The application’s id. |
room_id |
The room’s id. |
peer_id |
The peer’s id. |
memory |
The resource the topic is allocated in. |
webcpp::trystero::protocol::presence_frame
A presence frame, Trystero’s wrapRoomPresenceFrame: whether this peer is in a room.
Synopsis
Declared in <webcpp/trystero/protocol/envelope.hpp>
bytes
presence_frame(
std::string_view token,
bool present,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The frame.
Parameters
| Name | Description |
|---|---|
token |
The room’s token, |
present |
Whether the peer joins (true) or leaves (false) the room. |
memory |
The resource the frame is allocated in. |
webcpp::trystero::protocol::read_signal
The message a parsed signaling payload holds, as views into document.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
std::optional<signal_message>
read_signal(boost::json::value const& document);
Return Value
The message, or nothing when Trystero would ignore it (not an object, no peer id, or an offer, answer or candidate that is not a non-empty string), or when a field is longer than its bound: the peer id than max_peer_id_bytes, the offer id than max_offer_id_bytes, an offer or answer than max_sealed_description_bytes, a candidate than max_sealed_candidate_bytes.
Parameters
| Name | Description |
|---|---|
document |
The parsed payload, which the caller keeps alive while it reads the views. |
webcpp::trystero::protocol::room_frame
A room frame, Trystero’s wrapRoomFrame: an action frame in the envelope of its room.
Synopsis
Declared in <webcpp/trystero/protocol/envelope.hpp>
bytes
room_frame(
std::string_view token,
std::span<uint8_t const> payload,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The frame.
Parameters
| Name | Description |
|---|---|
token |
The room’s token, |
payload |
The action frame. |
memory |
The resource the frame is allocated in. |
webcpp::trystero::protocol::room_token
The token that names a room inside a data channel’s envelope: the hexadecimal SHA-256 of "Trystero:<app>:<room>".
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
std::pmr::string
room_token(
std::string_view app_id,
std::string_view room_id,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The token, 64 hexadecimal digits.
Parameters
| Name | Description |
|---|---|
app_id |
The application’s id. |
room_id |
The room’s id. |
memory |
The resource the token is allocated in. |
webcpp::trystero::protocol::root_topic
The topic every member of a room announces on, Trystero’s sha1 of "Trystero@<app>@<room>".
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
std::pmr::string
root_topic(
std::string_view app_id,
std::string_view room_id,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The topic’s name.
Parameters
| Name | Description |
|---|---|
app_id |
The application’s id. |
room_id |
The room’s id. |
memory |
The resource the topic is allocated in. |
webcpp::trystero::protocol::seal_key
The raw AES-GCM key of a room, what Trystero’s genKey imports: the SHA-256 of "<password>:<app>:<room>".
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
std::array<uint8_t, 32>
seal_key(
std::string_view password,
std::string_view app_id,
std::string_view room_id);
Return Value
The AES-256 key.
Parameters
| Name | Description |
|---|---|
password |
The password used for this room, shared by its app; empty means none. |
app_id |
The application’s id. |
room_id |
The room’s id. |
webcpp::trystero::protocol::sealed_size
The longest text a plaintext seals to, as format_sealed writes it.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
constexpr
std::size_t
sealed_size(std::size_t plaintext);
Description
The IV’s bytes in decimal and commas, "$", and the ciphertext with its 16-byte tag in base64.
Return Value
The size of the longest sealed text, in bytes, whatever the IV.
Parameters
| Name | Description |
|---|---|
plaintext |
The plaintext’s size, in bytes. |
webcpp::trystero::protocol::sha1_topic
Trystero’s sha1 of a text, the name of a topic.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
std::pmr::string
sha1_topic(
std::string_view text,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The name, as sha1_topic_of writes it.
Parameters
| Name | Description |
|---|---|
text |
The text. |
memory |
The resource the name is allocated in. |
webcpp::trystero::protocol::sha1_topic_of
Trystero’s sha1 of the concatenation of parts, the name of a topic.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
std::pmr::string
sha1_topic_of(
std::initializer_list<std::string_view> parts,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Description
It is the SHA-1 digest with every byte written in base 36 without padding, concatenated.
Tip: the text is not fixed width; it is a topic name and is only ever compared, never decoded.
Return Value
The name.
Parameters
| Name | Description |
|---|---|
parts |
The texts, in order. |
memory |
The resource the name is allocated in. |
webcpp::trystero::protocol::sha256
The SHA-256 of a text.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
std::array<uint8_t, 32>
sha256(std::string_view text);
Return Value
The digest.
Parameters
| Name | Description |
|---|---|
text |
The text. |
webcpp::trystero::protocol::sha256_of
The SHA-256 of the concatenation of parts, fed to the hash one part at a time.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
std::array<uint8_t, 32>
sha256_of(std::initializer_list<std::string_view> parts);
Description
Tip: every digest the protocol takes is of a few ids joined by separators; hashing the parts in place builds none.
Return Value
The digest.
Parameters
| Name | Description |
|---|---|
parts |
The texts, in order. |
webcpp::trystero::protocol::shuffle
Trystero’s shuffle: Fisher-Yates driven by sin(seed++) * 10000, so every peer of an app picks the same order.
Synopsis
Declared in <webcpp/trystero/protocol/relays.hpp>
template<class Sequence>
Sequence
shuffle(
Sequence items,
double seed);
Description
Tip: std::sin and V8’s Math.sin agree on these inputs; the oracle’s fixtures check it.
Return Value
The items, shuffled.
Template Parameters
| Name | Description |
|---|---|
Sequence |
A sequence with |
Parameters
| Name | Description |
|---|---|
items |
The items, shuffled in the copy returned. |
seed |
The seed, |
webcpp::trystero::protocol::str_to_num
Trystero’s strToNum: the sum of the text’s UTF-16 code units, modulo limit.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
double
str_to_num(
std::string_view text,
double limit = 9007199254740991.0);
Return Value
The number, in [0, limit).]
Parameters
| Name | Description |
|---|---|
text |
The text, in UTF-8. |
limit |
The modulus; the default is JavaScript’s |
webcpp::trystero::protocol::to_hex
The lowercase hexadecimal of a byte sequence, Trystero’s toHex.
Synopsis
Declared in <webcpp/trystero/protocol/text.hpp>
std::pmr::string
to_hex(
std::span<uint8_t const> data,
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
Two digits per byte.
Parameters
| Name | Description |
|---|---|
data |
The bytes. |
memory |
The resource the text is allocated in. |
webcpp::trystero::protocol::utf16_code_unit_sum
The sum of the UTF-16 code units of a UTF-8 text, what JavaScript’s charCodeAt sees.
Synopsis
Declared in <webcpp/trystero/protocol/text.hpp>
double
utf16_code_unit_sum(std::string_view utf8);
Description
Tip: Trystero derives Nostr kinds and the relay shuffle seed from it, so non-ASCII ids must count as in JS.
Return Value
The sum of the code units of its UTF-16 form, a character above U+FFFF counting as its two surrogates.
Parameters
| Name | Description |
|---|---|
utf8 |
The text, in UTF-8. |
webcpp::trystero::protocol::valid_action_name
Whether a name can name an action, as Trystero’s makeAction checks it: 1 to 32 UTF-8 bytes and no NUL.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
bool
valid_action_name(std::string_view name);
Return Value
Whether it is valid.
Parameters
| Name | Description |
|---|---|
name |
The name. |
webcpp::trystero::protocol::write_signal
The JSON text of a message, with its members in Trystero’s order and the empty ones left out.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
std::pmr::string
write_signal(
signal_message const& m,
std::string_view nonce = {},
std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The text.
Parameters
| Name | Description |
|---|---|
m |
The message. |
nonce |
A random text, which makes repeated announcements distinct events; empty for none. |
memory |
The resource the text is allocated in. |
webcpp::trystero::protocol::action_wire_version
The version of the action wire, every frame’s first byte, Trystero’s version.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
inline constexpr uint8_t action_wire_version = 2;
webcpp::trystero::protocol::chunk_header_size
The size of a chunk’s header, Trystero’s chunkHeaderSize.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
inline constexpr std::size_t chunk_header_size = 14;
webcpp::trystero::protocol::chunk_size
The most payload bytes one chunk carries, Trystero’s chunkSize.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
inline constexpr std::size_t chunk_size = max_action_frame_bytes - chunk_header_size;
webcpp::trystero::protocol::control_header_size
The size of an accepted, rejected or cancelled frame’s header, controlHeaderSize.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
inline constexpr std::size_t control_header_size = 6;
webcpp::trystero::protocol::default_nostr_relays
Trystero 0.26’s default Nostr relays, its defaultRelayUrls, in its order: the order is part of the relay derivation.
Synopsis
Declared in <webcpp/trystero/protocol/relays.hpp>
inline constexpr std::array<std::string_view, 29> default_nostr_relays = {
"wss://0x-nostr-relay.fly.dev",
"wss://bendernostur.duckdns.org:8443",
"wss://cdn.satellite.earth",
"wss://nos.lol",
"wss://nostr.chaima.info",
"wss://nostr.christiansass.de",
"wss://nostr.rblb.it",
"wss://nostr.red5d.dev",
"wss://nostr.robosats.org",
"wss://nostr.stakey.net",
"wss://nostrcity-club.fly.dev",
"wss://relay-dev.gulugulu.moe",
"wss://relay.aarpia.com",
"wss://relay.agentry.com",
"wss://relay.bitmacro.cloud",
"wss://relay.bullishbounty.com",
"wss://relay.degmods.com",
"wss://relay.flashapp.me",
"wss://relay.grigic.org",
"wss://relay.hackshed.dev",
"wss://relay.kaleidoswap.com",
"wss://relay.layer.systems",
"wss://relay.nostr.blockhenge.com",
"wss://relay.nostr.dev.br",
"wss://relay.nostrmap.net",
"wss://relay.novospes.com",
"wss://relay.piazza.today",
"wss://relay.routstr.com",
"wss://testr.nymble.world",
};
webcpp::trystero::protocol::id_alphabet
The 62 characters Trystero draws peer ids, offer ids and nonces from, its charSet.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
inline constexpr std::string_view id_alphabet = "0123456789AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz";
webcpp::trystero::protocol::inline_header_size
The size of an inline frame’s header, Trystero’s inlineHeaderSize.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
inline constexpr std::size_t inline_header_size = 36;
webcpp::trystero::protocol::max_action_frame_bytes
The largest action frame, Trystero’s maxActionFrameBytes: 16 KiB.
Synopsis
Declared in <webcpp/trystero/protocol/envelope.hpp>
inline constexpr std::size_t max_action_frame_bytes = std::size_t{16} * 1024;
webcpp::trystero::protocol::max_action_name_bytes
The longest action name, in UTF-8 bytes.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
inline constexpr std::size_t max_action_name_bytes = 32;
webcpp::trystero::protocol::max_offer_id_bytes
The longest offer id a signal may carry, in bytes; Trystero’s are offer_id_size.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
inline constexpr std::size_t max_offer_id_bytes = 64;
webcpp::trystero::protocol::max_peer_id_bytes
The longest peer id a room accepts in a signal, in bytes; a signal with a longer one is ignored.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
inline constexpr std::size_t max_peer_id_bytes = 64;
Description
Every id Trystero makes is 20 characters (selfId, peer_id_size); the bound leaves room for an application’s own ids and keeps a flood of long ids from costing more than a short one.
webcpp::trystero::protocol::max_queued_relay_bytes
The most text a room holds unsent for one relay, in UTF-8 bytes: 4 MiB, a thousand signals and more.
Synopsis
Declared in <webcpp/trystero/protocol/relays.hpp>
inline constexpr std::size_t max_queued_relay_bytes = std::size_t{4} << 20U;
Description
A relay that stops reading lets the texts pile up; beyond this, both backends drop its connection, which reports a close, and the room reconnects with its backoff.
webcpp::trystero::protocol::max_reject_reason_bytes
The longest reason a rejected frame carries, in UTF-8 bytes.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
inline constexpr std::size_t max_reject_reason_bytes = 200;
webcpp::trystero::protocol::max_relay_message_bytes
The longest text a room reads from a relay, in UTF-8 bytes: 1 MiB, far beyond any message of the protocol (an event’s content is a signal, whose SDP is a few KiB) and of what public relays accept (their events are capped at 64 to 512 KiB).
Synopsis
Declared in <webcpp/trystero/protocol/relays.hpp>
inline constexpr std::size_t max_relay_message_bytes = std::size_t{1} << 20U;
Description
Both backends close a relay’s connection that sends a longer one, before copying it, and the room reconnects as it does to a relay that went away.
webcpp::trystero::protocol::max_room_frame_bytes
The largest room frame, Trystero’s maxRoomFrameBytes: an action frame in its envelope.
Synopsis
Declared in <webcpp/trystero/protocol/envelope.hpp>
inline constexpr std::size_t max_room_frame_bytes = max_action_frame_bytes + 3 + max_room_token_bytes;
webcpp::trystero::protocol::max_room_token_bytes
The longest room token a room frame may carry, in bytes.
Synopsis
Declared in <webcpp/trystero/protocol/envelope.hpp>
inline constexpr std::size_t max_room_token_bytes = 128;
webcpp::trystero::protocol::max_sealed_candidate_bytes
The longest sealed candidate a signal may carry, in bytes.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
inline constexpr std::size_t max_sealed_candidate_bytes = std::size_t{16} * 1024;
Description
Trystero drops a candidate whose plaintext is longer than 8192 bytes (maxRemoteCandidateSdpLength), and every candidate up to that size, sealed, fits.
webcpp::trystero::protocol::max_sealed_description_bytes
The longest sealed offer or answer a signal may carry, in bytes.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
inline constexpr std::size_t max_sealed_description_bytes = std::size_t{64} * 1024;
Description
A data channel’s SDP is a few KiB, with every candidate gathered in it a few more. Trystero bounds none; this one bounds what a negotiation copies and keeps, and refuses no SDP a browser writes for a data channel.
webcpp::trystero::protocol::nonce_size
The length of the nonce that makes repeated announcements distinct Nostr events.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
inline constexpr std::size_t nonce_size = 8;
webcpp::trystero::protocol::offer_header_size
The size of an offer’s header, Trystero’s offerHeaderSize.
Synopsis
Declared in <webcpp/trystero/protocol/action_wire.hpp>
inline constexpr std::size_t offer_header_size = 48;
webcpp::trystero::protocol::offer_id_size
The length of the id of a Trystero offer, which its answer and candidates name.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
inline constexpr std::size_t offer_id_size = 12;
webcpp::trystero::protocol::peer_id_size
The length of a Trystero peer id, its selfId.
Synopsis
Declared in <webcpp/trystero/protocol/topics.hpp>
inline constexpr std::size_t peer_id_size = 20;
webcpp::trystero::protocol::seal_iv_size
The size of the IV Trystero’s encrypt draws for AES-GCM: 16 bytes, not the usual 12.
Synopsis
Declared in <webcpp/trystero/protocol/signal.hpp>
inline constexpr std::size_t seal_iv_size = 16;
webcpp::trystero::bytes
Binary data allocated with std::allocator, an action’s binary payload when the user chose standard containers.
Description
A message received as bytes is a copy of the payload, allocated with std::allocator.
webcpp::trystero::payload
What an action carries when its type is not fixed: Trystero’s text, JSON or binary payload.
Synopsis
Declared in <webcpp/trystero/room.hpp>
using payload = std::variant<std::string, boost::json::value, bytes>;
Description
A message received as payload holds the alternative of its format: a std::string for text, a boost::json::value for JSON, in the room’s JSON storage, and bytes for binary data.
webcpp::trystero::peer_id
A peer’s id in a room, Trystero’s selfId and the ids getPeers lists: 20 characters of protocol::id_alphabet unless the configuration names one.
Description
A room hands its ids out in its own resource.
webcpp::trystero::pmr_bytes
Binary data in a memory resource, an action’s binary payload in the room’s resource.
Description
A message received as pmr_bytes uses the room’s effective room_options::memory, which must outlive the value. A received buffer in the same resource is moved into the value; a buffer in the app’s distinct resource is copied once into the room’s. An inline payload is taken out of its frame before being moved or copied.
See Also
"Allocation", in the guide.
webcpp::trystero::action
A named channel of messages of type T within a room, wire compatible with what Trystero’s makeAction returns.
Description
Messages arrive at the handler on_message set, or, without one, queue for async_receive, up to 1024 per action, the oldest dropped first. A message of another format than T’s is not delivered to the handler. A room makes it with room::make_action, and it keeps the room’s state alive.
Member Functions
| Name | Description |
|---|---|
Receives the next message, when no handler is set: the oldest queued, or the next to arrive. |
|
|
|
The action’s name. |
|
Sets what is called with every message, Trystero’s |
Friends
| Name | Description |
|---|---|
A room joined over Trystero’s Nostr strategy, what Trystero’s |
Template Parameters
| Name | Description |
|---|---|
T |
The type of its messages, one of those |
See Also
"Rooms and actions", in the guide.
webcpp::trystero::action::async_receive
Receives the next message, when no handler is set: the oldest queued, or the next to arrive.
Synopsis
Declared in <webcpp/trystero/room.hpp>
template<class CompletionToken>
auto
async_receive(CompletionToken&& token);
Description
Completes with (error_code, T, message_info); a message of another payload format than T’s completes with error::transfer_rejected and an empty value, and leaving the room with error::room_left. T must own its storage: a view would outlive the message.
Return Value
What the token’s initiation returns.
Template Parameters
| Name | Description |
|---|---|
CompletionToken |
An Asio completion token for |
Parameters
| Name | Description |
|---|---|
token |
The completion token. |
webcpp::trystero::action::async_send
async_send overloads
Synopses
Declared in <webcpp/trystero/room.hpp>
Sends a message to every active peer, without metadata.
template<class CompletionToken>
auto
async_send(
T value,
CompletionToken&& token);
Sends a message to options.targets, or every active peer, Trystero’s send.
template<class CompletionToken>
auto
async_send(
T value,
send_options options,
CompletionToken&& token);
Return Value
What the token’s initiation returns.
Template Parameters
| Name | Description |
|---|---|
CompletionToken |
An Asio completion token for |
Parameters
| Name | Description |
|---|---|
value |
The message, taken at the call as the other overload takes it. |
token |
The completion token. |
options |
The targets and the metadata. |
webcpp::trystero::action::async_send
Sends a message to every active peer, without metadata.
Synopsis
Declared in <webcpp/trystero/room.hpp>
template<class CompletionToken>
auto
async_send(
T value,
CompletionToken&& token);
Return Value
What the token’s initiation returns.
Template Parameters
| Name | Description |
|---|---|
CompletionToken |
An Asio completion token for |
Parameters
| Name | Description |
|---|---|
value |
The message, taken at the call as the other overload takes it. |
token |
The completion token. |
webcpp::trystero::action::async_send
Sends a message to options.targets, or every active peer, Trystero’s send.
Synopsis
Declared in <webcpp/trystero/room.hpp>
template<class CompletionToken>
auto
async_send(
T value,
send_options options,
CompletionToken&& token);
Description
A payload that fits one frame is sent inline; a larger one is offered, accepted by each receiver, and sent in chunks. Completes with (error_code) once each target’s local data channel has taken the inline frame, or the last chunk of a transfer the receiver accepted, as Trystero’s send resolves: nothing confirms that the receiver has the message. It completes with the first failure: error::invalid_action_name, error::metadata_too_large, error::no_such_peer, error::transfer_rejected, error::transfer_timed_out, error::channel_stalled, error::peer_disconnected or error::room_left.
Return Value
What the token’s initiation returns.
Template Parameters
| Name | Description |
|---|---|
CompletionToken |
An Asio completion token for |
Parameters
| Name | Description |
|---|---|
value |
The message, taken at the call: moved when |
options |
The targets and the metadata. |
token |
The completion token. |
webcpp::trystero::action::on_message
Sets what is called with every message, Trystero’s onMessage.
Synopsis
Declared in <webcpp/trystero/room.hpp>
template<class F>
requires std::is_invocable_v<F&, T, const message_info&>
void
on_message(F&& f);
Description
Messages that queued while the action had no handler are delivered to it first.
Template Parameters
| Name | Description |
|---|---|
F |
A callable with a |
Parameters
| Name | Description |
|---|---|
f |
The handler, kept in the room’s resource; it replaces the one set before. |
webcpp::trystero::app
This program in one app: one peer id, and one connection per remote peer, shared by the rooms it joins, as a Trystero JS page shares them.
Description
Every room uses the app’s immutable password; different passwords require separate apps with distinct peer ids.
Tip: the app and every room of it share one state; destroying the app while rooms remain changes nothing for them. Every allocation of the app goes through app_config::memory, which must outlive the app, its rooms and every operation they leave pending.
Type Aliases
| Name | Description |
|---|---|
The executor every handler and completion of the app and its rooms runs on. |
Member Functions
| Name | Description |
|---|---|
|
Constructors |
|
Releases the state; its rooms go on, and their connections with them. |
Assignment operators |
|
The application’s id, Trystero’s |
|
The executor of the app and of its rooms. |
|
Joins a room of the app, Trystero’s |
|
Sets what is called with each part of the configuration the app set aside, with an empty peer id and |
|
The Nostr relays every room of the app uses, derived from the app id unless the configuration named them. |
|
This peer’s id in every room of the app, Trystero’s |
Static Data Members
| Name | Description |
|---|---|
The most rooms an app holds at once: a Trystero JS peer drops a connection that advertises more, its limit of 64 room tokens per connection. |
See Also
"Rooms and actions", in the guide.
webcpp::trystero::app::executor_type
The executor every handler and completion of the app and its rooms runs on.
webcpp::trystero::app::app
Constructors
Synopses
Declared in <webcpp/trystero/app.hpp>
Not copyable: an app is one peer id and its connections.
Takes the state of the app moved from, which may then only be destroyed or assigned.
Makes an app: draws its id unless the configuration names one, and keeps the configuration.
app(
executor_type ex,
backend::factory& factory,
app_config config);
Parameters
| Name | Description |
|---|---|
other |
The app moved from. |
ex |
The executor of the app and of its rooms. |
factory |
The backend, which must outlive the app and its rooms. |
config |
The configuration, copied into |
Preconditions
-
config.memoryis not null.
webcpp::trystero::app::app
Takes the state of the app moved from, which may then only be destroyed or assigned.
Parameters
| Name | Description |
|---|---|
other |
The app moved from. |
webcpp::trystero::app::app
Makes an app: draws its id unless the configuration names one, and keeps the configuration.
Synopsis
Declared in <webcpp/trystero/app.hpp>
app(
executor_type ex,
backend::factory& factory,
app_config config);
Description
What no peer could use (an id longer than any peer accepts, a negative idle time, an ICE server WebRTC refuses) is set aside, and reported to on_error.
Parameters
| Name | Description |
|---|---|
ex |
The executor of the app and of its rooms. |
factory |
The backend, which must outlive the app and its rooms. |
config |
The configuration, copied into |
Preconditions
-
config.memoryis not null.
webcpp::trystero::app::operator=
Assignment operators
Synopses
Declared in <webcpp/trystero/app.hpp>
Not copy-assignable: an app is one peer id and its connections.
Releases the state it held, then takes the state of the app moved from, which may then only be destroyed or assigned; the rooms of the state released go on.
Return Value
This app.
Parameters
| Name | Description |
|---|---|
other |
The app moved from. |
webcpp::trystero::app::operator=
Releases the state it held, then takes the state of the app moved from, which may then only be destroyed or assigned; the rooms of the state released go on.
Return Value
This app.
Parameters
| Name | Description |
|---|---|
other |
The app moved from. |
webcpp::trystero::app::get_executor
The executor of the app and of its rooms.
Synopsis
Declared in <webcpp/trystero/app.hpp>
[[nodiscard]]
executor_type const&
get_executor() const;
Return Value
The executor every handler and completion of the app and its rooms runs on.
|
Note
|
The return value should not be discarded. |
webcpp::trystero::app::join
Joins a room of the app, Trystero’s joinRoom for an app already configured: connects to the relays and announces this peer on the room’s topic.
Synopsis
Declared in <webcpp/trystero/app.hpp>
boost::system::result<room>
join(
std::string_view room_id,
room_options options = {});
Return Value
The room, or error::already_joined while a room of that id is joined in the app, or error::too_many_rooms when max_rooms rooms count.
Parameters
| Name | Description |
|---|---|
room_id |
The room’s id, Trystero’s |
options |
The handshake timeout, receive bound and resource of the room. |
webcpp::trystero::app::on_error
Sets what is called with each part of the configuration the app set aside, with an empty peer id and error::invalid_configuration.
Synopsis
Declared in <webcpp/trystero/app.hpp>
template<class F>
requires std::is_invocable_v<F&, std::string_view, boost::system::error_code,
std::string_view>
void
on_error(F&& f);
Description
The reports are delivered once the caller has returned, each once, to the handler set when they are delivered: one replaced before then gets none.
Template Parameters
| Name | Description |
|---|---|
F |
A callable with the peer’s id, the |
Parameters
| Name | Description |
|---|---|
f |
The handler, kept in the app’s resource; it replaces the one set before. |
webcpp::trystero::app::relay_urls
The Nostr relays every room of the app uses, derived from the app id unless the configuration named them.
Synopsis
Declared in <webcpp/trystero/app.hpp>
[[nodiscard]]
std::span<std::pmr::string const>
relay_urls() const;
Return Value
The relays’ URLs, valid as long as the app.
|
Note
|
The return value should not be discarded. |
webcpp::trystero::app::max_rooms
The most rooms an app holds at once: a Trystero JS peer drops a connection that advertises more, its limit of 64 room tokens per connection.
webcpp::trystero::app_config
What an app is made with: the part of Trystero’s joinRoom configuration that every room of the app shares, its peer id among it, and the one connection per remote peer its rooms share.
Description
The app copies every part of it into memory. It is move-only, as its log is: pass it moved, or build it in place with designated initializers.
Data Members
| Name | Description |
|---|---|
The application’s id, Trystero’s |
|
The STUN and TURN servers; empty means Trystero’s defaults, |
|
How long a connection with no room bound to it after a departure stays open, Trystero’s |
|
Where the app’s and its rooms’ diagnostics go: called with a category ("app", "nostr", "signal", "webrtc", "room" or "action") and a sentence; empty means none, and then no text is composed. |
|
The resource every allocation of the app goes through: its state, its connections, the peer connections and data channels its rooms’ negotiations make, the frames received on them, the sentences of its log; not null. |
|
The password shared by every room of this app, Trystero’s |
|
How many of the default relays the derivation keeps, Trystero’s |
|
The Nostr relays to use, Trystero’s |
|
This program’s id in every room of the app, Trystero’s |
webcpp::trystero::app_config::app_id
The application’s id, Trystero’s appId: rooms of different apps never meet.
webcpp::trystero::app_config::ice_servers
The STUN and TURN servers; empty means Trystero’s defaults, default_ice_servers.
Synopsis
Declared in <webcpp/trystero/options.hpp>
std::pmr::vector<backend::ice_server> ice_servers = = {};
Description
A server WebRTC would refuse (a URL that is not stun:, stuns:, turn: or turns: host[:port], or a TURN server without a user name and a credential) is dropped when the app is made, and reported with error::invalid_configuration; the app goes on with the others, on every backend.
webcpp::trystero::app_config::idle_timeout
How long a connection with no room bound to it after a departure stays open, Trystero’s sharedPeerIdleMs; 0 closes it at once.
Synopsis
Declared in <webcpp/trystero/options.hpp>
std::chrono::milliseconds idle_timeout = {123'333};
Description
A negative value is replaced with 0, and reported with error::invalid_configuration.
webcpp::trystero::app_config::log
Where the app’s and its rooms’ diagnostics go: called with a category ("app", "nostr", "signal", "webrtc", "room" or "action") and a sentence; empty means none, and then no text is composed.
Description
The app moves the sink into memory, and composes each sentence there.
webcpp::trystero::app_config::memory
The resource every allocation of the app goes through: its state, its connections, the peer connections and data channels its rooms’ negotiations make, the frames received on them, the sentences of its log; not null.
Synopsis
Declared in <webcpp/trystero/options.hpp>
std::pmr::memory_resource* memory = std::pmr::get_default_resource();
Description
What libdatachannel, OpenSSL and the browser’s JavaScript allocate stays theirs. The app uses the resource from its executor only, so it need not be thread-safe when that executor runs on one thread. It must outlive the app, its rooms, retained values allocated in it and every operation they leave pending: on Asio, until the io_context their executor belongs to has run out of their work or has been destroyed, so a resource is declared before its io_context; in the browser, until the app’s and its rooms’ pending promises have settled. The app and its rooms allocate and free per event, so a resource that reuses what is freed (a pool) suits a long-lived app, and a monotonic one only a short-lived one.
See Also
"Allocation", in the guide.
webcpp::trystero::app_config::password
The password shared by every room of this app, Trystero’s password.
Description
The app copies it into its resource and keeps it unchanged. Different passwords require separate apps with distinct peer ids, so a connection authenticated in one domain cannot be reused in another. Empty means a passwordless domain. Signals are still sealed with the app and room ids, and the room handshake then has no password challenge.
webcpp::trystero::app_config::relay_redundancy
How many of the default relays the derivation keeps, Trystero’s relayConfig.redundancy; ignored when relay_urls names the relays.
webcpp::trystero::app_config::relay_urls
The Nostr relays to use, Trystero’s relayConfig.urls; empty means Trystero’s defaults, derived from the app id.
Synopsis
Declared in <webcpp/trystero/options.hpp>
std::pmr::vector<std::pmr::string> relay_urls = = {};
webcpp::trystero::app_config::self_id
This program’s id in every room of the app, Trystero’s selfId; empty means 20 random characters, drawn once for the app.
Description
Other peers ignore an id longer than protocol::max_peer_id_bytes (64): the app replaces one with 20 random characters, and reports it with error::invalid_configuration.
webcpp::trystero::error_category_impl
The category of error, named "webcpp.trystero", whose messages are fixed texts.
Synopsis
Declared in <webcpp/trystero/error.hpp>
class error_category_impl final
: public boost::system::error_category
Description
A program reaches its one instance through error_category.
Base Classes
| Name | Description |
|---|---|
|
Non-Member Functions
| Name | Description |
|---|---|
webcpp::trystero::error_category_impl::message
message overloads
Synopses
Declared in <webcpp/trystero/error.hpp>
The message of a value of error, as a std::string.
virtual
std::string
message(int ev) const override;
The message of a value of error without a std::string.
virtual
char const*
message(
int ev,
char*,
std::size_t) const noexcept override;
Return Value
-
The message, or "unknown webcpp.trystero error" for a value
errordoes not name. -
The message, a static text.
Parameters
| Name | Description |
|---|---|
ev |
The value, an |
webcpp::trystero::error_category_impl::message
The message of a value of error, as a std::string.
Synopsis
Declared in <webcpp/trystero/error.hpp>
virtual
std::string
message(int ev) const override;
Return Value
The message, or "unknown webcpp.trystero error" for a value error does not name.
Parameters
| Name | Description |
|---|---|
ev |
The value, an |
webcpp::trystero::error_category_impl::message
The message of a value of error without a std::string.
Synopsis
Declared in <webcpp/trystero/error.hpp>
virtual
char const*
message(
int ev,
char*,
std::size_t) const noexcept override;
Description
A room logs through this form, which allocates nothing: the messages are static texts, and the buffer is not used.
Return Value
The message, a static text.
Parameters
| Name | Description |
|---|---|
ev |
The value, an |
webcpp::trystero::error_category_impl::name
The category’s name.
Synopsis
Declared in <webcpp/trystero/error.hpp>
virtual
char const*
name() const noexcept override;
Return Value
"webcpp.trystero".
webcpp::trystero::handler
A move-only callable of a signature, whose target is allocated through a memory resource; see the partial specialization handler<R(Args...)>.
Specializations
| Name | Description |
|---|---|
A move-only callable of the signature |
Template Parameters
| Name | Description |
|---|---|
Signature |
A function type, |
See Also
"Allocation", in the guide.
webcpp::trystero::handler<R(Args...)>
A move-only callable of the signature R(Args...), which holds its target inline when the target fits in three pointers and is nothrow-movable, and otherwise in storage of its allocator.
Synopsis
Declared in <webcpp/trystero/handler.hpp>
template<
class R,
class... Args>
class handler<R(Args...)>;
Description
It replaces std::function wherever trystero keeps a handler, including the backend, app, rooms and log sink, since std::function takes no allocator since C++17. Each handler uses its owner’s std::pmr::polymorphic_allocator<>, over app_config::memory or the effective room_options::memory; one constructed from a callable alone uses the default resource. The resource must outlive the handler.
Tip: a move assignment carries the source’s allocator with its target, as moving the target there does; the allocator-extended move, which a std::pmr container uses, moves the target into another resource when the two differ.
Type Aliases
| Name | Description |
|---|---|
What the handler allocates its target with when the target does not fit inline. |
Member Functions
Template Parameters
| Name | Description |
|---|---|
R |
The result type. |
Args |
The parameter types. |
See Also
"Allocation", in the guide.
webcpp::trystero::handler<R(Args...)>::allocator_type
What the handler allocates its target with when the target does not fit inline.
Synopsis
Declared in <webcpp/trystero/handler.hpp>
using allocator_type = std::pmr::polymorphic_allocator<>;
webcpp::trystero::handler<R(Args...)>::handler
Constructors
Synopses
Declared in <webcpp/trystero/handler.hpp>
Constructs an empty handler, which must not be called.
handler() noexcept = default;
Not copyable: a handler owns its target, which may not be copyable.
Constructs a handler of other’s target and allocator.
Constructs an empty handler, which must not be called, from nullptr.
handler(std::nullptr_t) noexcept;
Constructs a handler of f, allocated with the default resource when it does not fit inline.
template<class F>
requires (!std::is_same_v<std::remove_cvref_t<F>, handler> &&
std::is_invocable_r_v<R, std::decay_t<F>&, Args...> &&
std::is_move_constructible_v<std::decay_t<F>>)
handler(F&& f);
Constructs a handler of other’s target, moved into allocator’s resource when it differs from other’s: the allocator-extended move a std::pmr container uses.
handler(
std::allocator_arg_t,
allocator_type allocator,
handler&& other);
Constructs a handler of f, allocated with allocator when it does not fit inline.
template<class F>
requires (!std::is_same_v<std::remove_cvref_t<F>, handler> &&
std::is_invocable_r_v<R, std::decay_t<F>&, Args...> &&
std::is_move_constructible_v<std::decay_t<F>>)
handler(
std::allocator_arg_t,
allocator_type allocator,
F&& f);
Template Parameters
| Name | Description |
|---|---|
F |
The callable’s type, invocable with |
Parameters
| Name | Description |
|---|---|
other |
The handler whose target is taken; it is left empty. |
f |
The callable, moved or copied into the handler. |
allocator |
What the handler keeps, and allocates the target with when it moves it. |
webcpp::trystero::handler<R(Args...)>::handler
Constructs an empty handler, which must not be called.
webcpp::trystero::handler<R(Args...)>::handler
Not copyable: a handler owns its target, which may not be copyable.
webcpp::trystero::handler<R(Args...)>::handler
Constructs a handler of other’s target and allocator.
Parameters
| Name | Description |
|---|---|
other |
The handler whose target is taken; it is left empty. |
webcpp::trystero::handler<R(Args...)>::handler
Constructs an empty handler, which must not be called, from nullptr.
webcpp::trystero::handler<R(Args...)>::handler
Constructs a handler of f, allocated with the default resource when it does not fit inline.
Synopsis
Declared in <webcpp/trystero/handler.hpp>
template<class F>
requires (!std::is_same_v<std::remove_cvref_t<F>, handler> &&
std::is_invocable_r_v<R, std::decay_t<F>&, Args...> &&
std::is_move_constructible_v<std::decay_t<F>>)
handler(F&& f);
Template Parameters
| Name | Description |
|---|---|
F |
The callable’s type, invocable with |
Parameters
| Name | Description |
|---|---|
f |
The callable, moved or copied into the handler. |
webcpp::trystero::handler<R(Args...)>::handler
Constructs a handler of other’s target, moved into allocator’s resource when it differs from other’s: the allocator-extended move a std::pmr container uses.
Synopsis
Declared in <webcpp/trystero/handler.hpp>
handler(
std::allocator_arg_t,
allocator_type allocator,
handler&& other);
Parameters
| Name | Description |
|---|---|
allocator |
What the handler keeps, and allocates the target with when it moves it. |
other |
The handler whose target is taken; it is left empty. |
webcpp::trystero::handler<R(Args...)>::handler
Constructs a handler of f, allocated with allocator when it does not fit inline.
Synopsis
Declared in <webcpp/trystero/handler.hpp>
template<class F>
requires (!std::is_same_v<std::remove_cvref_t<F>, handler> &&
std::is_invocable_r_v<R, std::decay_t<F>&, Args...> &&
std::is_move_constructible_v<std::decay_t<F>>)
handler(
std::allocator_arg_t,
allocator_type allocator,
F&& f);
Template Parameters
| Name | Description |
|---|---|
F |
The callable’s type, invocable with |
Parameters
| Name | Description |
|---|---|
allocator |
What the target is allocated with, and what the handler keeps; its resource must outlive the handler. |
f |
The callable, moved or copied into the handler. |
webcpp::trystero::handler<R(Args...)>::~handler
Destroys the target, and gives its storage back to the allocator.
webcpp::trystero::handler<R(Args...)>::operator=
Assignment operators
Synopses
Declared in <webcpp/trystero/handler.hpp>
Not copy-assignable: a handler owns its target, which may not be copyable.
Destroys the target, then takes other’s target and allocator.
Destroys the target, which leaves the handler empty.
Return Value
This handler.
Parameters
| Name | Description |
|---|---|
other |
The handler whose target is taken; it is left empty. |
webcpp::trystero::handler<R(Args...)>::operator=
Not copy-assignable: a handler owns its target, which may not be copyable.
webcpp::trystero::handler<R(Args...)>::operator=
Destroys the target, then takes other’s target and allocator.
Return Value
This handler.
Parameters
| Name | Description |
|---|---|
other |
The handler whose target is taken; it is left empty. |
webcpp::trystero::handler<R(Args...)>::operator=
Destroys the target, which leaves the handler empty.
Return Value
This handler.
webcpp::trystero::handler<R(Args...)>::get_allocator
The allocator the target is allocated with when it does not fit inline.
Synopsis
Declared in <webcpp/trystero/handler.hpp>
[[nodiscard]]
allocator_type
get_allocator() const noexcept;
webcpp::trystero::handler<R(Args...)>::operator()
Calls the target.
Return Value
What the target returns, converted to R.
Parameters
| Name | Description |
|---|---|
args |
The arguments, forwarded to the target. |
Preconditions
-
The handler is not empty.
webcpp::trystero::handler<R(Args...)>::operator bool
Whether the handler has a target.
Return Value
Whether it may be called.
webcpp::trystero::json_resource
A boost::container::pmr::memory_resource that allocates from a std::pmr::memory_resource.
Synopsis
Declared in <webcpp/trystero/json_resource.hpp>
class json_resource final
: public boost::container::pmr::memory_resource
Description
A room makes one over room_options::memory, or the app’s resource when null, as the JSON storage every JSON value of the room is allocated in: what it parses from a relay, a signal or the handshake, and the boost::json::value an action’s handler receives. The storage is counted, so a value a handler keeps keeps it alive; Boost.JSON allocates that count with operator new, once per room.
Base Classes
| Name | Description |
|---|---|
|
Member Functions
| Name | Description |
|---|---|
|
Constructs a resource that allocates from |
The resource this one allocates from. |
Protected Member Functions
| Name | Description |
|---|---|
|
Allocates from the upstream resource. |
|
Gives storage back to the upstream resource. |
|
Whether |
Non-Member Functions
| Name | Description |
|---|---|
Returns: !(a == b). |
|
Returns: |
See Also
"Allocation", in the guide.
webcpp::trystero::json_resource::json_resource
Constructs a resource that allocates from upstream.
Synopsis
Declared in <webcpp/trystero/json_resource.hpp>
explicit
json_resource(std::pmr::memory_resource* upstream) noexcept;
Parameters
| Name | Description |
|---|---|
upstream |
The resource every allocation goes to; it must outlive this resource and every value allocated in it. |
webcpp::trystero::json_resource::upstream
The resource this one allocates from.
Synopsis
Declared in <webcpp/trystero/json_resource.hpp>
[[nodiscard]]
std::pmr::memory_resource*
upstream() const noexcept;
webcpp::trystero::json_resource::do_allocate
Allocates from the upstream resource.
Synopsis
Declared in <webcpp/trystero/json_resource.hpp>
virtual
void*
do_allocate(
std::size_t bytes,
std::size_t alignment) override;
Return Value
The storage, from the upstream resource.
Parameters
| Name | Description |
|---|---|
bytes |
The size. |
alignment |
The alignment. |
webcpp::trystero::json_resource::do_deallocate
Gives storage back to the upstream resource.
Synopsis
Declared in <webcpp/trystero/json_resource.hpp>
virtual
void
do_deallocate(
void* pointer,
std::size_t bytes,
std::size_t alignment) override;
Parameters
| Name | Description |
|---|---|
pointer |
The storage, which |
bytes |
The size it was allocated with. |
alignment |
The alignment it was allocated with. |
webcpp::trystero::json_resource::do_is_equal
Whether other is this resource: it is equal to itself alone.
Synopsis
Declared in <webcpp/trystero/json_resource.hpp>
[[nodiscard]]
virtual
bool
do_is_equal(boost::container::pmr::memory_resource const& other) const noexcept override;
Description
Tip: another adapter over the same upstream would do too, but telling one apart from another resource needs RTTI, which a user may build without; an unequal resource costs Boost.JSON a copy where it could have moved.
Parameters
| Name | Description |
|---|---|
other |
The resource to compare with. |
webcpp::trystero::message_info
Who sent a message, and the metadata it carried, the second and third arguments of Trystero’s action handler, in the receiving room’s resource.
webcpp::trystero::message_info::from
The sender’s id.
webcpp::trystero::message_info::metadata
The metadata the sender passed, as JSON text, or empty when it passed none.
webcpp::trystero::room
A room joined over Trystero’s Nostr strategy, what Trystero’s joinRoom returns.
Description
app::join makes it, joined; async_leave leaves with Trystero’s farewell, and destroying it leaves at once. Its peers meet over the relays, connect over WebRTC through the connections its app shares between its rooms, and exchange the messages of the actions it makes, wire compatible with Trystero’s JavaScript.
Every room uses its app’s immutable password. Initial sealed signaling authenticates that domain before a connection is adopted. The unchanged JS room handshake verifies only at the lower id; different passwords require separate apps with distinct self ids.
Tip: every handler the room calls, and every completion, runs on its app’s executor. The views a handler receives are valid for the duration of the call. Every allocation of the room goes through its effective room_options::memory, the app’s resource when null; shared connections and received frames use app_config::memory. A completion handler is allocated as its own associated allocator says. Destroying the room leaves operations pending on its executor, so both resources must outlive those operations and every retained value allocated in them.
Type Aliases
| Name | Description |
|---|---|
The executor every handler and completion of the room runs on. |
Member Functions
| Name | Description |
|---|---|
|
Constructors |
|
Leaves at once, without the farewell’s grace period: every peer is told, the room lets go of the app’s connections, which the app’s other rooms may go on using, and every pending operation completes with |
Assignment operators |
|
Says goodbye to every peer and closes everything, Trystero’s |
|
Measures the round trip to a peer, Trystero’s |
|
The executor of the room’s app. |
|
Makes an action of this room, Trystero’s |
|
Sets what is called with each problem with one peer that the room handled itself, as a wrong password or a timed-out handshake, Trystero’s |
|
Sets what is called with each peer that joins, once its handshake completes, Trystero’s |
|
Sets what is called with each joined peer that leaves, Trystero’s |
|
The peers whose handshake completed, Trystero’s |
|
The Nostr relays this room uses, its app’s: derived from the app id unless the app’s configuration named them. |
|
This peer’s id in the room, Trystero’s |
Friends
| Name | Description |
|---|---|
This program in one app: one peer id, and one connection per remote peer, shared by the rooms it joins, as a Trystero JS page shares them. |
See Also
"Rooms and actions", in the guide.
webcpp::trystero::room::executor_type
The executor every handler and completion of the room runs on.
webcpp::trystero::room::room
Takes the membership of the room moved from, which may then only be destroyed or assigned.
webcpp::trystero::room::~room
Leaves at once, without the farewell’s grace period: every peer is told, the room lets go of the app’s connections, which the app’s other rooms may go on using, and every pending operation completes with error::room_left.
webcpp::trystero::room::operator=
Assignment operators
Synopses
Declared in <webcpp/trystero/room.hpp>
Not copy-assignable: a room is one membership.
Leaves the membership this room held at once, as the destructor does, then takes the membership of the room moved from, which may then only be destroyed or assigned.
Return Value
This room.
Parameters
| Name | Description |
|---|---|
other |
The room moved from. |
webcpp::trystero::room::operator=
Leaves the membership this room held at once, as the destructor does, then takes the membership of the room moved from, which may then only be destroyed or assigned.
Description
Every peer of the membership left is told, the room lets go of the app’s connections, which the app’s other rooms may go on using, and every operation pending on it completes with error::room_left, even while an action of it lives.
Return Value
This room.
Parameters
| Name | Description |
|---|---|
other |
The room moved from. |
webcpp::trystero::room::async_leave
Says goodbye to every peer and closes everything, Trystero’s leave.
Synopsis
Declared in <webcpp/trystero/room.hpp>
template<class CompletionToken>
auto
async_leave(CompletionToken&& token);
Description
Each peer is sent @_leave, and 99 ms later, as Trystero waits, the room lets go of the app’s connections, which the app’s other rooms may go on using, and every pending operation completes with error::room_left. Completes with (error_code), empty, also when the room was already left.
Return Value
What the token’s initiation returns.
Template Parameters
| Name | Description |
|---|---|
CompletionToken |
An Asio completion token for |
Parameters
| Name | Description |
|---|---|
token |
The completion token. |
webcpp::trystero::room::async_ping
Measures the round trip to a peer, Trystero’s ping.
Synopsis
Declared in <webcpp/trystero/room.hpp>
template<class CompletionToken>
auto
async_ping(
std::string_view peer,
CompletionToken&& token);
Description
Completes with (error_code, std::chrono::milliseconds): the time from sending @_ping to receiving its @_pong, or error::no_such_peer when the peer is not active, error::peer_disconnected when it leaves first, and error::room_left.
Return Value
What the token’s initiation returns.
Template Parameters
| Name | Description |
|---|---|
CompletionToken |
An Asio completion token for |
Parameters
| Name | Description |
|---|---|
peer |
The peer’s id, copied at the call, so a deferred operation does not depend on the caller’s string. |
token |
The completion token. |
webcpp::trystero::room::get_executor
The executor of the room’s app.
Synopsis
Declared in <webcpp/trystero/room.hpp>
[[nodiscard]]
executor_type const&
get_executor() const;
Return Value
The executor every handler and completion of the room runs on.
|
Note
|
The return value should not be discarded. |
webcpp::trystero::room::make_action
Makes an action of this room, Trystero’s makeAction: a named channel of messages.
Synopsis
Declared in <webcpp/trystero/room.hpp>
template<class T>
action<T>
make_action(std::string_view name);
Description
Frames of the action that arrived before it was made, which the room keeps for a while (64 per peer, for 120 s), are delivered to it.
Return Value
The action.
Template Parameters
Parameters
| Name | Description |
|---|---|
name |
The action’s name, 1 to 32 UTF-8 bytes without NUL; with another, every send fails with |
webcpp::trystero::room::on_error
Sets what is called with each problem with one peer that the room handled itself, as a wrong password or a timed-out handshake, Trystero’s onPeerError.
Synopsis
Declared in <webcpp/trystero/room.hpp>
template<class F>
requires std::is_invocable_v<F&, std::string_view, boost::system::error_code,
std::string_view>
void
on_error(F&& f);
Description
Signals that do not open with the room key (error::wrong_password) are reported once per negotiation, and paced: the first of a quiet 10 s at once, with its peer, and the others of those 10 s in one report, with an empty peer id and a description that starts with their number, since anyone who reads the relays can send them from any number of fresh ids. A part of the configuration the app set aside is reported to app::on_error, not here.
Template Parameters
| Name | Description |
|---|---|
F |
A callable with the peer’s id, the |
Parameters
| Name | Description |
|---|---|
f |
The handler, kept in the room’s resource; it replaces the one set before. |
webcpp::trystero::room::on_peer_join
Sets what is called with each peer that joins, once its handshake completes, Trystero’s onPeerJoin.
Synopsis
Declared in <webcpp/trystero/room.hpp>
template<class F>
requires std::is_invocable_v<F&, std::string_view>
void
on_peer_join(F&& f);
Template Parameters
| Name | Description |
|---|---|
F |
A callable with a |
Parameters
| Name | Description |
|---|---|
f |
The handler, kept in the room’s resource; it replaces the one set before. |
webcpp::trystero::room::on_peer_leave
Sets what is called with each joined peer that leaves, Trystero’s onPeerLeave.
Synopsis
Declared in <webcpp/trystero/room.hpp>
template<class F>
requires std::is_invocable_v<F&, std::string_view>
void
on_peer_leave(F&& f);
Template Parameters
| Name | Description |
|---|---|
F |
A callable with a |
Parameters
| Name | Description |
|---|---|
f |
The handler, kept in the room’s resource; it replaces the one set before. |
webcpp::trystero::room::peers
The peers whose handshake completed, Trystero’s getPeers.
Synopsis
Declared in <webcpp/trystero/room.hpp>
[[nodiscard]]
std::pmr::vector<peer_id>
peers() const;
webcpp::trystero::room::relay_urls
The Nostr relays this room uses, its app’s: derived from the app id unless the app’s configuration named them.
Synopsis
Declared in <webcpp/trystero/room.hpp>
[[nodiscard]]
std::span<std::pmr::string const>
relay_urls() const;
Return Value
The relays’ URLs, valid as long as the room.
|
Note
|
The return value should not be discarded. |
webcpp::trystero::room_options
What a room of an app is joined with: the part of Trystero’s joinRoom configuration that is the room’s own.
Data Members
| Name | Description |
|---|---|
How long a connected peer has to finish the handshake before it is dropped, Trystero’s |
|
The most bytes the room reserves at once for the transfers it receives, across peers, Trystero’s |
|
The resource every allocation of the room goes through: its state, bindings, negotiations, relays, actions, sent frames, transfers and JSON. |
webcpp::trystero::room_options::handshake_timeout
How long a connected peer has to finish the handshake before it is dropped, Trystero’s handshakeTimeoutMs.
Synopsis
Declared in <webcpp/trystero/options.hpp>
std::chrono::milliseconds handshake_timeout = {10'000};
webcpp::trystero::room_options::max_receive_bytes
The most bytes the room reserves at once for the transfers it receives, across peers, Trystero’s maxReceiveBytes.
Synopsis
Declared in <webcpp/trystero/options.hpp>
std::size_t max_receive_bytes = std::size_t{256} * 1024 * 1024;
webcpp::trystero::room_options::memory
The resource every allocation of the room goes through: its state, bindings, negotiations, relays, actions, sent frames, transfers and JSON.
Description
Null means the app’s resource.
It must outlive the room, every value it hands out and every operation it leaves pending: on Asio, until its io_context has run out of the room’s work or has been destroyed; in the browser, until its pending promises have settled. Received frames and shared connections use the app’s resource.
webcpp::trystero::send_options
Where an action’s message goes and what it carries beside its payload, the target and the metadata of Trystero’s send.
Description
Tip: it owns its strings because a completion token such as asio::deferred starts the operation later, after the caller’s views may be gone.
webcpp::trystero::send_options::metadata
The metadata, as JSON text, or empty for none.
webcpp::trystero::send_options::targets
The peers to send to; empty means every active peer.
webcpp::trystero::error
What an operation of the library completes with when it does not succeed.
Description
Each value is one of the errors Trystero rejects a promise with or reports to onPeerError, as a code of error_category rather than a message: an operation completes with it, and room::on_error reports it with the peer it concerns.
Members
| Name | Description |
|---|---|
The room was left while the operation was pending, Trystero’s "room left". |
|
The target is not an active peer of the room, Trystero’s "no active peer with id". |
|
The peer went away before the operation finished, Trystero’s "peer disconnected". |
|
The action’s name is not 1 to 32 UTF-8 bytes without NUL, as Trystero requires. |
|
The action’s metadata does not fit one frame, Trystero’s "action metadata is too large". |
|
The receiver refused a transfer, or a received payload is not of the awaited type. |
|
The receiver neither accepted nor refused a transfer in time. |
|
The data channel stopped draining while a transfer waited for it. |
|
A peer’s signal or handshake used another password, or the peer sent a handshake message the protocol does not expect, Trystero’s "incorrect password for overlapping room". |
|
A connected peer did not finish the handshake in time, Trystero’s "handshake timed out". |
|
Reserved: nothing reports it today. |
|
The browser or the native stack refused an operation. |
|
The room’s configuration names something no backend can use, as an ICE server whose URL WebRTC refuses; the room goes on without it, and |
|
The app already holds a room of that id; a room is joined once per app. |
|
The app already holds as many rooms as a peer accepts, 64 of them. |
See Also
"Rooms and actions", in the guide.
webcpp::trystero::error::room_left
The room was left while the operation was pending, Trystero’s "room left".
webcpp::trystero::error::no_such_peer
The target is not an active peer of the room, Trystero’s "no active peer with id".
webcpp::trystero::error::peer_disconnected
The peer went away before the operation finished, Trystero’s "peer disconnected".
webcpp::trystero::error::invalid_action_name
The action’s name is not 1 to 32 UTF-8 bytes without NUL, as Trystero requires.
webcpp::trystero::error::metadata_too_large
The action’s metadata does not fit one frame, Trystero’s "action metadata is too large".
webcpp::trystero::error::transfer_rejected
The receiver refused a transfer, or a received payload is not of the awaited type.
webcpp::trystero::error::transfer_timed_out
The receiver neither accepted nor refused a transfer in time.
webcpp::trystero::error::channel_stalled
The data channel stopped draining while a transfer waited for it.
webcpp::trystero::error::wrong_password
A peer’s signal or handshake used another password, or the peer sent a handshake message the protocol does not expect, Trystero’s "incorrect password for overlapping room".
webcpp::trystero::error::handshake_timed_out
A connected peer did not finish the handshake in time, Trystero’s "handshake timed out".
webcpp::trystero::error::handshake_failed
Reserved: nothing reports it today.
Description
A handshake message of the wrong shape reports wrong_password, as Trystero’s handshake does.
webcpp::trystero::error::invalid_configuration
The room’s configuration names something no backend can use, as an ICE server whose URL WebRTC refuses; the room goes on without it, and room::on_error reports it with no peer.
webcpp::trystero::error::already_joined
The app already holds a room of that id; a room is joined once per app.
webcpp::trystero::error::too_many_rooms
The app already holds as many rooms as a peer accepts, 64 of them.
webcpp::trystero::default_ice_servers
Trystero’s default STUN servers, its defaultIceServers: Google’s and Cloudflare’s.
Synopsis
Declared in <webcpp/trystero/options.hpp>
std::pmr::vector<backend::ice_server>
default_ice_servers(std::pmr::memory_resource* memory = std::pmr::get_default_resource());
Return Value
The servers, an app’s when its configuration names none.
Parameters
| Name | Description |
|---|---|
memory |
The resource the list and its strings are allocated in. |
webcpp::trystero::error_category
The category of every error code.
Synopsis
Declared in <webcpp/trystero/error.hpp>
boost::system::error_category const&
error_category() noexcept;
Return Value
The one instance of error_category_impl.
webcpp::trystero::make_error_code
An error code of error_category, which boost::system::error_code finds by argument-dependent lookup.
Synopsis
Declared in <webcpp/trystero/error.hpp>
boost::system::error_code
make_error_code(error e) noexcept;
Return Value
The code of e.
Parameters
| Name | Description |
|---|---|
e |
The error. |
webcpp::trystero::operator==
Returns: &a == &b || a.is_equal(b).
Synopses
Declared in <webcpp/trystero/error.hpp>
constexpr
bool
operator==(
error_category_impl const& lhs,
error_category_impl const& rhs) noexcept;
Returns: &a == &b || a.is_equal(b).
[[nodiscard]]
bool
operator==(
json_resource const& a,
json_resource const& b) noexcept;
webcpp::trystero::operator==
Synopsis
Declared in <webcpp/trystero/error.hpp>
constexpr
bool
operator==(
error_category_impl const& lhs,
error_category_impl const& rhs) noexcept;
|
Note
|
This function is a hidden friend of error_category_impl, declared inside its base class error_category: it can be found only through argument-dependent lookup. |
webcpp::trystero::operator==
Returns: &a == &b || a.is_equal(b).
Synopsis
Declared in <webcpp/trystero/json_resource.hpp>
[[nodiscard]]
bool
operator==(
json_resource const& a,
json_resource const& b) noexcept;
|
Note
|
This function is a hidden friend of json_resource, declared inside its base class memory_resource: it can be found only through argument-dependent lookup. |
Return Value
|
Note
|
The return value should not be discarded. |
webcpp::trystero::operator!=
Returns: !(a == b).
Synopses
Declared in <webcpp/trystero/error.hpp>
constexpr
bool
operator!=(
error_category_impl const& lhs,
error_category_impl const& rhs) noexcept;
Returns: !(a == b).
[[nodiscard]]
bool
operator!=(
json_resource const& a,
json_resource const& b) noexcept;
webcpp::trystero::operator!=
Synopsis
Declared in <webcpp/trystero/error.hpp>
constexpr
bool
operator!=(
error_category_impl const& lhs,
error_category_impl const& rhs) noexcept;
|
Note
|
This function is a hidden friend of error_category_impl, declared inside its base class error_category: it can be found only through argument-dependent lookup. |
webcpp::trystero::operator!=
Returns: !(a == b).
Synopsis
Declared in <webcpp/trystero/json_resource.hpp>
[[nodiscard]]
bool
operator!=(
json_resource const& a,
json_resource const& b) noexcept;
|
Note
|
This function is a hidden friend of json_resource, declared inside its base class memory_resource: it can be found only through argument-dependent lookup. |
Return Value
|
Note
|
The return value should not be discarded. |
webcpp::trystero::operator<
Synopsis
Declared in <webcpp/trystero/error.hpp>
constexpr
bool
operator<(
error_category_impl const& lhs,
error_category_impl const& rhs) noexcept;
|
Note
|
This function is a hidden friend of error_category_impl, declared inside its base class error_category: it can be found only through argument-dependent lookup. |
WEBCPP_TRYSTERO_NO_EXCEPTIONS
Defined when trystero is built without exceptions: when webcpp has none (the compiler has none, or the developer defined WEBCPP_NO_EXCEPTIONS for every webcpp library), or when the developer defines it alone, to disable trystero’s in a build that has them.
Description
trystero raises no exception of its own: every operation that can fail completes with an error code. The native backend’s WebRTC, over libdatachannel, which reports errors by throwing, needs exceptions: while this is defined, native::data_channel, native::peer_connection and native::backend do not exist, and the rest of trystero is unchanged.
See Also
"The native backend", in the guide.
Appendix A: Differences from Trystero
trystero ports the part of Trystero 0.26.0 that rooms of messages need: the Nostr strategy, the signaling, the password handshake, the action wire and the room’s API. Where it keeps a feature, it keeps Trystero’s wire and its defaults; where it bounds what a peer or a relay can make a room hold, it says so in Bounded signaling, since Trystero JS bounds none of it. These features of Trystero are not ported, each for its reason:
| Not ported | Why |
|---|---|
Audio and video: |
The largest part left, on the roadmap
(Roadmap): in the browser, the page’s |
Request actions, |
Not ported yet: an application answers a message with another, to its sender’s id. |
|
Not ported yet: a room accepts every transfer for an action with a
receiver within |
|
Not ported yet: the password handshake, which runs on the same internal
action, |
Passive rooms, |
Not ported yet: a room always announces itself. |
The BitTorrent, MQTT, Firebase, Supabase, IPFS and WebSocket relay strategies |
Each is a strategy of its own to port; Nostr’s public relays need no account and no server of the application’s own. |
|
Not ported yet: a room reconnects with its backoff, and reports what its relays do through its log. |
|
Candidates are always trickled, as Trystero’s Nostr strategy does by
default; |
Where a ported feature behaves otherwise
| Behaviour | Trystero JS | Here | Why |
|---|---|---|---|
Scope of sharing and of the self id |
the page: one |
an |
no static state; several peers in one process; an id across apps is a program’s choice ( |
Passwords across rooms |
independently configured per room, including mixed passwords on one shared connection |
one immutable |
the initial sealed negotiation authenticates one password domain before any of its rooms reuse the connection; the JS wire remains unchanged |
A peer kicked from a room, or that kicked us, over a live connection |
bound again by its next signal or presence |
bound again at a rebinding time, 11 s doubling to 5 min; the lower id refuses it meanwhile and keeps its frames |
a peer sending invalid room proofs would otherwise loop reports and announcements without end; one refusing side cannot alternate with another |
Reuse over a live connection |
binds on any signal of that peer id |
binds only a peer that advertised the room’s token itself, and by reuse only on their first meeting over that connection |
a forged announcement must not bind an innocent peer |
Announcements after a peer leaves |
restarted after every leave |
restarted only when no live connection to that peer remains |
over a live connection, rebinding goes through the connection |
Presence after binding |
sent when a room registers |
also sent again when a room binds by reuse or at its rebinding time |
it makes the other side, Trystero JS included, bind at once |
An answer to an earlier challenge |
checked against the current challenge only |
accepted when it is correct for any of the last 8 challenges sent since the last completed handshake |
a late answer of an ended binding is honest and proves the password; failing it reports a false error |
Frames after the higher id’s own kick |
kept for the next binding |
dropped until the higher id sends presence |
they belong to the ended binding; flushed into the next one, they would answer or be answered in its place |
Joining a room already joined |
the same room |
|
a |
Rooms per app |
unbounded; a peer advertising a 65th is dropped by the other side |
|
the 65th would cost every connection of the app |
A connection whose last room was kicked |
idle 123,333 ms |
closed at once |
a bound: a failed peer holds no connection, as today |
Relay sockets and the Nostr key |
shared by the rooms of the page, topics batched per socket |
per room, as today |
not part of this decision; invisible to peers; shared through the app in a later change |
Relay and ICE configuration |
the first room’s relays; each negotiating room’s RTC configuration |
the app’s, for every room |
one configuration for what the rooms share |
|
test-only ( |
a configuration member, Trystero’s default |
tests and programs that close sooner |
Frames waiting for a bound room’s handler |
64, 10 s, then the connection drops |
no such state |
a binding’s delivery is set when it is made |
Renegotiation and media over a shared connection |
routed through a control room, streams owned per room; a connection with any media is destroyed when its last room goes |
not ported |
media is on the roadmap (Roadmap) |
Passive rooms |
presence only for active rooms |
not ported |
as today |
Crossed duplicate connections |
first registered wins at each side; crossed, both lose and meet again |
the same |
matching Trystero’s rule is what keeps a C++ and a Trystero JS side agreeing |
on_peer_join does not replay the joined peers. Trystero calls a newly
assigned onPeerJoin with each peer already joined. A room reports a join
from its executor, after the call to join has returned, so a
handler set right after join sees every join; peers() lists the
joined peers for a handler set later.
peers() lists ids. Trystero’s getPeers maps each id to its
RTCPeerConnection. A connection is the backend’s, a page’s object in the
browser and libdatachannel’s natively, and the room does not hand it out.
A send to a peer that has not joined fails. Trystero warns on the console
and resolves; async_send completes with error::no_such_peer, since an
operation that fails says so with its error code.
No example of trystero runs under the build, since each needs the public relays or a browser, so none has a twin in Trystero’s language, and the interop lane, not a twin, compares trystero with Trystero (The oracle and the interop lanes).
Appendix B: Roadmap
What trystero does not port yet, and what it does not build itself yet (Differences from Trystero gives each feature left out with its reason):
-
Media. Audio and video tracks and streams, and Trystero’s
@_stream,@_trackand@_signalactions with renegotiation. In the browser they are the page’sMediaStreamTrackobjects. Natively they would be libdatachannel’s RTP tracks, whose encoding, decoding and capture are the application’s. What that needs was measured: libdatachannel keeps only weak references to tracks, answers with every codec offered unless the answer is pruned, and echoes the offerer’smsidinto the answer. -
Trystero’s other features. Request actions (
kind: 'request'), decisions on incoming transfers and their progress, the application’s own handshake, passive rooms, and the other strategies, among them MQTT and BitTorrent trackers. -
libsecp256k1 from webcpp’s own build. libsecp256k1 is still found where b2’s command line or the superproject’s
.local/says. Native OpenSSL and libdatachannel already build from pinnedthird_party/sources (Building).
Appendix C: Acknowledgements
trystero ports the wire protocol of Trystero
0.26.0, Copyright (c) 2021 Dan Motzenbecker, so that its rooms are
Trystero’s rooms. It was written with Trystero’s source open: its protocol
constants, its message layouts, its default relays in their order, with the
shuffle that derives an app’s relays from its id, and its default STUN
servers come from it. Trystero is distributed under the MIT License, whose
text and copyright notice are in LICENSE-TRYSTERO.txt, at the root of the
library, exactly as Trystero 0.26.0 ships them. That adds no licence to
trystero’s own code, which is under the Boost Software License alone.
The BIP-340 signatures of the Nostr events are libsecp256k1's, by the Bitcoin Core contributors, under the MIT License. The native backend’s WebRTC is libdatachannel's, by Paul-Louis Ageneau, under the Mozilla Public License 2.0.
Appendix D: Changelog
What changed in trystero in each version of webcpp, newest first, one line per change. "Unreleased" is what the next version brings. The release notes of every version are on webcpp’s index page.
Unreleased
webcpp 0.1.0
-
Fixed native multiroom handoffs losing early presence frames, and negotiations stalled by a channel closing before room binding.
-
Changed the native backend to build pinned libdatachannel sources and their dependencies through B2, sharing source-built OpenSSL with Asio and Beast.
-
Changed native builds to require
threading=multi; the backend uses workers. -
Fixed GCC compilation of native WebSocket coroutine callbacks.
-
Changed room construction to
app::join, sharing connections between rooms. -
Breaking change: Removed
room_config; constructappand usejoinwithapp_configandroom_options. -
Breaking change: Changed passwords to
app_config; different passwords require separate apps and self ids. -
Added a changelog, the page’s last appendix, with each version’s changes.
-
Changed
config.hppto include<webcpp/config.hpp>, webcpp’s configuration, which the build adds. -
Added
WEBCPP_NO_EXCEPTIONS, which disables trystero’s exceptions with every webcpp library’s.