FFI Bindings
The SDK exposes FFI bindings for Go and C/C++. The source lives in sdk/ffi:
ffi
├── cpp # C/C++ bindings (manual C FFI)
├── go # Go bindings (via uniffi-bindgen-go)
└── shared # Shared Rust implementationCore logic lives in shared/ and is imported into language-specific wrappers. The shared layer handles thread safety and runs client operations on blocking threads on the Rust side of the FFI boundary.
What’s exposed
Mixnet (Go and C/C++): ephemeral and persistent client creation, sending messages, anonymous replies via SURBs, listening for incoming messages.
TcpProxy (Go only): client and server creation and lifecycle.
The TcpProxy module is deprecated. For new projects, use the Stream module instead.
Client Pool and Stream have no standalone FFI bindings yet. The TcpProxy bindings use the Client Pool internally.
Quick example (Go)
// Initialise an ephemeral client
bindings.InitEphemeral()
// Get our Nym address
addr, _ := bindings.GetSelfAddress()
// Send a message through the Mixnet
bindings.SendMessage(addr, "hello from Go")
// Listen for incoming messages
msg, _ := bindings.ListenForIncoming()
fmt.Println("Received:", msg.Message)
// Reply anonymously via SURBs
bindings.Reply(msg.Sender, "reply from Go")Quick example (C++)
The C++ bindings use callbacks for return values and a ReceivedMessage struct for incoming data:
extern "C" {
struct ReceivedMessage {
const uint8_t* message;
size_t size;
const char* sender_tag;
};
void init_logging();
char init_ephemeral();
char get_self_address(void (*callback)(const char*));
char send_message(const char*, const char*);
char listen_for_incoming(void (*callback)(ReceivedMessage));
char reply(const char*, const char*);
}
// Get address via callback
char addr[134];
void on_address(const char* s) { strcpy(addr, s); }
// Receive message via callback
char sender_tag[22];
void on_message(ReceivedMessage msg) {
std::cout << "Received: " << msg.message << std::endl;
strcpy(sender_tag, msg.sender_tag);
}
int main() {
init_ephemeral();
get_self_address(on_address);
send_message(addr, "hello from C++");
listen_for_incoming(on_message);
reply(sender_tag, "reply from C++");
}Building
Each language has a build.sh script that compiles the Rust shared library and generates bindings. See the README in each directory for prerequisites.
Examples and source
- Go mixnet example : init, send, receive, SURB reply
- Go TcpProxy example : proxy client and server with TCP echo
- C++ example : same flow using Boost threads
sdk/ffisource: full source and build scripts