Developers
FFI

FFI Bindings

The SDK exposes FFI bindings for Go and C/C++. The source lives in sdk/ffi (opens in a new tab):

ffi
├── cpp      # C/C++ bindings (manual C FFI)
├── go       # Go bindings (via uniffi-bindgen-go)
└── shared   # Shared Rust implementation

Core logic lives in shared/ and is imported into language-specific wrappers. The shared layer handles thread safety and ensures client operations run 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)

// Initialize 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