diff --git a/documentation/docs/components/demos/ens/EnsDemo.tsx b/documentation/docs/components/demos/ens/EnsDemo.tsx
index 9981e81e10..ef105d6eaa 100644
--- a/documentation/docs/components/demos/ens/EnsDemo.tsx
+++ b/documentation/docs/components/demos/ens/EnsDemo.tsx
@@ -195,7 +195,7 @@ export function EnsDemo() {
ENS lookup
- Confirm traffic exits through Nym before resolving.
+ Confirms traffic exits through Nym. The comparison makes one direct (clearnet) call to ipinfo.io, so you will see a single ipinfo.io row in the Network tab.
diff --git a/documentation/docs/components/demos/railgun/RailgunDemo.tsx b/documentation/docs/components/demos/railgun/RailgunDemo.tsx
index 5b8586f790..5eb89b732d 100644
--- a/documentation/docs/components/demos/railgun/RailgunDemo.tsx
+++ b/documentation/docs/components/demos/railgun/RailgunDemo.tsx
@@ -292,6 +292,7 @@ export function RailgunDemo() {
{balance}
+
Verify IP makes one direct (clearnet) call to ipinfo.io for the comparison, so you will see a single ipinfo.io row in the Network tab.
+ Mixnet. An overlay network that routes your traffic through several relays
+ and mixes it with other people's, hiding who is talking to whom. Nym operates one. See{' '}
+ mixnet mode.
+
+
+ Entry gateway. Your first hop into the mixnet. Your browser holds one
+ WebSocket to it; all tunnelled traffic rides that connection as opaque frames. See{' '}
+ Nym nodes.
+
+
+ IPR (IP Packet Router), the exit. The mixnet's exit point onto the normal
+ internet. The RPC node and gateway see the IPR's IP address, never yours. See{' '}
+ exit services.
+
+
+ SURB (single-use reply block). A prepaid, single-use return envelope. It
+ lets the exit send a reply back through the mixnet without learning your address. See{' '}
+ anonymous replies.
+
+
+ Cover traffic / Poisson timing. Decoy packets and randomised send timing.
+ Together they keep your real traffic statistically hard to pick out. See{' '}
+ cover traffic.
+
+
+ mixFetch. The{' '}
+ @nymproject/mix-fetch package's{' '}
+ fetch()-shaped function. It runs the mixnet client (smolmix) in a Web Worker and
+ sends your request through the mixnet instead of the browser's network stack.
+
+
+ );
+}
diff --git a/documentation/docs/components/demos/shared/NetworkTabCallout.tsx b/documentation/docs/components/demos/shared/NetworkTabCallout.tsx
new file mode 100644
index 0000000000..047d2df066
--- /dev/null
+++ b/documentation/docs/components/demos/shared/NetworkTabCallout.tsx
@@ -0,0 +1,19 @@
+// Shared "Watch the Network tab" callout, used on the playground and the demo
+// pages. Generic wording so it reads correctly wherever a single mixnet tunnel
+// carries the page's traffic.
+import React from 'react';
+import { Callout } from 'nextra/components';
+
+export function NetworkTabCallout() {
+ return (
+
+ Watch the Network tab. Open DevTools → Network before you connect. Once the
+ tunnel reports ready, every operation you run here adds no new request to that
+ tab: it is multiplexed inside the single WebSocket to the entry gateway. Only the clearnet
+ comparison buttons add rows. (Setup also fetches the network topology over HTTPS and refreshes
+ it periodically, so those nym-api calls and the gateway WebSocket are the only clearnet requests
+ you will see.) Your real traffic never leaves the browser as an identifiable, per-destination
+ request.
+
+ );
+}
diff --git a/documentation/docs/components/demos/shared/mixTunnel.tsx b/documentation/docs/components/demos/shared/mixTunnel.tsx
index 398302e138..3323039f37 100644
--- a/documentation/docs/components/demos/shared/mixTunnel.tsx
+++ b/documentation/docs/components/demos/shared/mixTunnel.tsx
@@ -95,30 +95,44 @@ export function MixTunnelSetup({
try {
const m = mods ?? (await loadMixFetch());
if (!mods) setMods(m);
- await m.setupMixTunnel({
- ...(useRandomIpr ? {} : { preferredIpr: iprAddress.trim() }),
- clientId,
- forceTls,
- disablePoissonTraffic: disablePoisson,
- disableCoverTraffic: disableCover,
- openReplySurbs: clampSurbs(openSurbs, 1),
- dataReplySurbs: clampSurbs(dataSurbs, 0),
- primaryDns: optStr(primaryDns),
- fallbackDns: optStr(fallbackDns),
- dnsTimeoutMs: optInt(dnsTimeout),
- connectTimeoutMs: optInt(connectTimeout),
- maxRedirects: optInt(maxRedirects),
- storagePassphrase: storagePassphrase || undefined,
- debug,
- });
+ // One WASM instance per browser tab, shared across demo pages by the
+ // bundler. If another page already brought the tunnel up, reuse it rather
+ // than calling setupMixTunnel again (which throws "already initialised").
+ const existing = await m.getTunnelState().catch(() => null);
+ if (existing && existing.state === 'ready') {
+ log('tunnel', 'Tunnel already up from another page; reusing it (its original options apply).', 'green');
+ } else {
+ await m.setupMixTunnel({
+ ...(useRandomIpr ? {} : { preferredIpr: iprAddress.trim() }),
+ clientId,
+ forceTls,
+ disablePoissonTraffic: disablePoisson,
+ disableCoverTraffic: disableCover,
+ openReplySurbs: clampSurbs(openSurbs, 1),
+ dataReplySurbs: clampSurbs(dataSurbs, 0),
+ primaryDns: optStr(primaryDns),
+ fallbackDns: optStr(fallbackDns),
+ dnsTimeoutMs: optInt(dnsTimeout),
+ connectTimeoutMs: optInt(connectTimeout),
+ maxRedirects: optInt(maxRedirects),
+ storagePassphrase: storagePassphrase || undefined,
+ debug,
+ });
+ log('tunnel', 'Tunnel ready', 'green');
+ }
setConnected(true);
setStatus({ text: 'Connected', colour: 'green' });
- log('tunnel', 'Tunnel ready', 'green');
onReady(m.mixFetch);
} catch (e) {
- setStatus({ text: 'Failed', colour: 'red' });
- log('tunnel', `Connection failed: ${e}`, 'red');
- log('tunnel', "Timeouts and IPR rate-limits are common. Try again, or tick 'Use random IPR' and reload.", 'orange');
+ const msg = String((e as any)?.message ?? e);
+ if (/already initialised/i.test(msg)) {
+ log('tunnel', 'Tunnel already initialised in this tab; reload the page if it does not connect.', 'orange');
+ setStatus({ text: 'Failed (already initialised, reload)', colour: 'red' });
+ } else {
+ setStatus({ text: 'Failed', colour: 'red' });
+ log('tunnel', `Connection failed: ${msg}`, 'red');
+ log('tunnel', "Timeouts and IPR rate-limits are common. Try again, or tick 'Use random IPR' and reload.", 'orange');
+ }
} finally {
setBusy(false);
}
diff --git a/documentation/docs/components/outputs/api-scraping-outputs/nodes-count.json b/documentation/docs/components/outputs/api-scraping-outputs/nodes-count.json
index 548c896a6d..a00a51b937 100644
--- a/documentation/docs/components/outputs/api-scraping-outputs/nodes-count.json
+++ b/documentation/docs/components/outputs/api-scraping-outputs/nodes-count.json
@@ -1,6 +1,6 @@
{
- "nodes": 679,
+ "nodes": 685,
"locations": 75,
"mixnodes": 240,
- "exit_gateways": 431
+ "exit_gateways": 437
}
diff --git a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md
index 738f38df28..d97399cb5d 100644
--- a/documentation/docs/components/outputs/api-scraping-outputs/time-now.md
+++ b/documentation/docs/components/outputs/api-scraping-outputs/time-now.md
@@ -1 +1 @@
-Tuesday, June 9th 2026, 15:17:20 UTC
+Tuesday, June 9th 2026, 16:06:04 UTC
diff --git a/documentation/docs/components/outputs/command-outputs/nym-api-help.md b/documentation/docs/components/outputs/command-outputs/nym-api-help.md
index e056b3547a..da2cedcfe5 100644
--- a/documentation/docs/components/outputs/command-outputs/nym-api-help.md
+++ b/documentation/docs/components/outputs/command-outputs/nym-api-help.md
@@ -9,8 +9,7 @@ Commands:
Options:
-c, --config-env-file Path pointing to an env file that configures the Nym API [env: NYMAPI_CONFIG_ENV_FILE_ARG=]
- --no-banner A no-op flag included for consistency with other binaries (and compatibility with nymvisor, oops) [env:
- NYMAPI_NO_BANNER_ARG=]
+ --no-banner A no-op flag included for consistency with other binaries (and compatibility with nymvisor, oops) [env: NYMAPI_NO_BANNER_ARG=]
-h, --help Print help
-V, --version Print version
```
diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-help.md b/documentation/docs/components/outputs/command-outputs/nym-node-help.md
index 45d2749641..7eaa0fdaa3 100644
--- a/documentation/docs/components/outputs/command-outputs/nym-node-help.md
+++ b/documentation/docs/components/outputs/command-outputs/nym-node-help.md
@@ -12,8 +12,7 @@ Commands:
help Print this message or the help of the given subcommand(s)
Options:
- -c, --config-env-file Path pointing to an env file that configures the nym-node and overrides any preconfigured values [env:
- NYMNODE_CONFIG_ENV_FILE_ARG=]
+ -c, --config-env-file Path pointing to an env file that configures the nym-node and overrides any preconfigured values [env: NYMNODE_CONFIG_ENV_FILE_ARG=]
--no-banner Flag used for disabling the printed banner in tty [env: NYMNODE_NO_BANNER=]
-h, --help Print help
-V, --version Print version
diff --git a/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md b/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md
index 0d4dce7ed1..2880ba22f7 100644
--- a/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md
+++ b/documentation/docs/components/outputs/command-outputs/nym-node-run-help.md
@@ -4,127 +4,95 @@ Start this nym-node
Usage: nym-node run [OPTIONS]
Options:
- --id
- Id of the nym-node to use [env: NYMNODE_ID=] [default: default-nym-node]
- --config-file
- Path to a configuration file of this node [env: NYMNODE_CONFIG=]
- --accept-operator-terms-and-conditions
- Explicitly specify whether you agree with the terms and conditions of a nym node operator as defined at
- [env: NYMNODE_ACCEPT_OPERATOR_TERMS=]
- --deny-init
- Forbid a new node from being initialised if configuration file for the provided specification doesn't already exist [env: NYMNODE_DENY_INIT=]
- --init-only
- If this is a brand new nym-node, specify whether it should only be initialised without actually running the subprocesses [env: NYMNODE_INIT_ONLY=]
- --local
- Flag specifying this node will be running in a local setting [env: NYMNODE_LOCAL=]
- --mode [...]
- Specifies the current mode(s) of this nym-node [env: NYMNODE_MODE=] [possible values: mixnode, entry-gateway, exit-gateway, exit-providers-only]
- --modes
- Specifies the current mode(s) of this nym-node as a single flag [env: NYMNODE_MODES=] [possible values: mixnode, entry-gateway, exit-gateway,
- exit-providers-only]
- -w, --write-changes
- If this node has been initialised before, specify whether to write any new changes to the config file [env: NYMNODE_WRITE_CONFIG_CHANGES=]
- --bonding-information-output
- Specify output file for bonding information of this nym-node, i.e. its encoded keys. NOTE: the required bonding information is still a subject to change and
- this argument should be treated only as a preview of future features [env: NYMNODE_BONDING_INFORMATION_OUTPUT=]
- -o, --output