Mixnet Client
The SDK Client (opens in a new tab) lets you send and receive messages over the Nym mixnet.
The client is message-based: it sends one-way messages to another client's address. Replying can be achieved in two ways:
- Reveal the sender's address to the recipient (as part of the payload)
- Use a SURB (single use reply block) that lets the recipient reply without compromising the identity of either party
Environment Setup
Create a new project with Vite:
npm create vite@latestChoose React + TypeScript, then:
cd <YOUR_APP>
npm i
npm run devInstallation
npm install @nymproject/sdk-full-fatFull Example
This example creates a Mixnet client, connects to a gateway, and provides a UI for sending and receiving messages through the mixnet.
For this example we use the full-fat version of the ESM SDK. If you use the unbundled ESM variant, make sure your bundler configuration copies the WASM and web worker files to the output bundle.
import React, { useEffect, useState } from "react";
import {
createNymMixnetClient,
NymMixnetClient,
Payload,
} from "@nymproject/sdk-full-fat";
import Box from "@mui/material/Box";
import CircularProgress from "@mui/material/CircularProgress";
import Paper from "@mui/material/Paper";
import Typography from "@mui/material/Typography";
import Stack from "@mui/material/Stack";
import TextField from "@mui/material/TextField";
import Button from "@mui/material/Button";
const nymApiUrl = "https://validator.nymtech.net/api";
export const Traffic = () => {
const [nym, setNym] = useState<NymMixnetClient>();
const [selfAddress, setSelfAddress] = useState<string>();
const [recipient, setRecipient] = useState<string>();
const [payload, setPayload] = useState<Payload>();
const [receivedMessage, setReceivedMessage] = useState<string>();
const [buttonEnabled, setButtonEnabled] = useState<boolean>(false);
const init = async () => {
const client = await createNymMixnetClient();
setNym(client);
// start the client and connect to a gateway
await client?.client.start({
clientId: crypto.randomUUID(),
nymApiUrl,
forceTls: true, // force WSS
});
// check when is connected and set the self address
client?.events.subscribeToConnected((e) => {
const { address } = e.args;
setSelfAddress(address);
});
// show whether the client is ready or not
client?.events.subscribeToLoaded((e) => {
console.log("Client ready: ", e.args);
});
// show message payload content when received
client?.events.subscribeToTextMessageReceivedEvent((e) => {
console.log(e.args.payload);
setReceivedMessage(e.args.payload);
});
};
const stop = async () => {
await nym?.client.stop();
};
const send = () =>
payload && recipient && nym?.client.send({ payload, recipient });
useEffect(() => {
init();
return () => {
stop();
};
}, []);
useEffect(() => {
if (recipient && payload) {
setButtonEnabled(true);
} else {
setButtonEnabled(false);
}
}, [recipient, payload]);
if (!nym || !selfAddress) {
return (
<Box sx={{ display: "flex" }}>
<CircularProgress />
</Box>
);
}
return (
<Box padding={3}>
<Paper style={{ marginTop: "1rem", padding: "2rem" }}>
<Stack spacing={3}>
<Typography variant="body1">My self address is:</Typography>
<Typography variant="body1">{selfAddress || "loading"}</Typography>
<Typography variant="h5">Communication through the Mixnet</Typography>
<TextField
type="text"
placeholder="Recipient Address"
onChange={(e) => setRecipient(e.target.value)}
size="small"
/>
<TextField
type="text"
placeholder="Message to send"
multiline
rows={4}
onChange={(e) =>
setPayload({ message: e.target.value, mimeType: "text/plain" })
}
size="small"
/>
<Button
variant="outlined"
onClick={() => send()}
disabled={!buttonEnabled}
sx={{ width: "fit-content" }}
>
Send
</Button>
</Stack>
{receivedMessage && (
<Stack spacing={3} style={{ marginTop: "1rem" }}>
<Typography variant="h5">Message Received!</Typography>
<Typography fontFamily="monospace">{receivedMessage}</Typography>
</Stack>
)}
</Paper>
</Box>
);
};If you encounter a Gateway client error that persists even after a hard refresh, open your browser console, navigate to the "Application" tab, and delete the databases listed under "IndexedDB".