Go client
The Go client package connects directly to the Zentalk relay network: anonymous registration and connection via zero-knowledge proofs, encrypted peer-to-peer messaging, username lookup, and group messaging. It speaks the relay wire protocol — for plain HTTP integrations, call the REST API with net/http instead.
import "github.com/ZentaChain/zentalk-node/pkg/client"The package is importable today from the zentalk-node module.
Concepts
Section titled “Concepts”| Concept | Description |
|---|---|
| Identity | A local ZK identity: a 32-byte secret, a public commitment, and an RSA key pair, stored as a JSON file. The secret never leaves the device. |
| Registration | The identity’s commitment is registered with a relay and assigned a Merkle tree index. |
| Anonymous connection | On connect, the client generates a Groth16 proof of tree membership. The relay learns that you are registered, not who you are, and assigns an anonymous address. |
| Proving key | A Groth16 proving key required to generate connection proofs, produced by the zkp-setup tool. |
| Ephemeral key | A per-connection RSA key pair used for encrypted messaging, deliberately unlinked from the identity. |
See Security overview for the broader security model.
Core types
Section titled “Core types”All constructors and methods below are defined in pkg/client.
Client
Section titled “Client”Client is the main entry point; it composes the identity, registration, ZKP, messaging, address, and key managers.
type ClientConfig struct { RelayAddr string // Relay server address, e.g. "localhost:8080" IdentityPath string // Where the identity JSON file is stored ProvingKey groth16.ProvingKey // Proving key for ZK proofs}
func NewClient(config *ClientConfig) *Client| Method | Description |
|---|---|
Initialize() error |
Loads the identity file, or creates a new identity if none exists |
Register(metadata map[string]string) error |
Registers the identity’s commitment with the relay (optional metadata such as "username") |
Connect() error |
Fetches the Merkle tree, generates a ZK handshake proof, and connects anonymously |
Disconnect() error |
Closes the relay connection |
IsConnected() bool / IsRegistered() bool |
Connection and registration state |
SendMessage(recipientAddr protocol.Address, recipientPubKey *rsa.PublicKey, message string) error |
Sends an encrypted message |
ReceiveMessage() (*ReceivedMessage, error) |
Blocks until one message is received and decrypted |
StartMessageListener(handler func(*ReceivedMessage)) error |
Delivers incoming messages to a handler in the background |
RegisterUsername(username string) error |
Registers a username for address lookup |
LookupAddress(username string) (protocol.Address, error) |
Resolves a username to a protocol address |
AdvertiseKey() error |
Publishes the ephemeral public key so others can send you encrypted messages |
LookupKey(addr protocol.Address) (*rsa.PublicKey, error) |
Fetches a peer’s advertised public key |
GetIdentity() *Identity / GetAnonymousAddress() protocol.Address / GetEphemeralPublicKey() *rsa.PublicKey |
Accessors |
InitializeGroups(dbPath, password string) error |
Opens the encrypted local group database and enables group support |
Received messages have this shape:
type ReceivedMessage struct { From protocol.Address To protocol.Address Content string Timestamp time.Time}Identity and IdentityManager
Section titled “Identity and IdentityManager”func NewIdentityManager(identityPath string) *IdentityManagerIdentityManager persists identities as JSON: GenerateIdentity(), SaveIdentity(), LoadIdentity(), HasIdentity(), GetOrCreateIdentity() (*Identity, bool, error) (the bool reports whether a new identity was created), and DeleteIdentity().
type Identity struct { Secret [32]byte // Private: never share Commitment zkp.Commitment // Public: registered with the relay TreeIndex uint64 // Assigned by the relay after registration IsRegistered bool PrivateKeyPEM []byte // RSA private key (PEM) PublicKeyPEM []byte // RSA public key (PEM)}Identity also exposes GetPrivateKey(), GetPublicKey(), and GetPublicKeyDER().
GroupClient
Section titled “GroupClient”Group messaging is layered on top of a connected Client and a local encrypted message database:
func NewGroupClient(client *Client, db *storage.MessageDB) *GroupClient| Method | Description |
|---|---|
CreateGroup(name, description string, groupType group.GroupType) (*group.Group, error) |
Create a group; group.GroupTypePrivate (invite-only) or group.GroupTypePublic (joinable via link) |
JoinGroup(joinCode string) (*group.Group, error) |
Join a public group by join code |
InviteMember(groupID string, memberAddr protocol.Address, memberUsername string, memberPubKey *rsa.PublicKey) error |
Invite a member |
SendGroupMessage(groupID string, message string) error |
Send a message to all members |
GetGroupMessages(groupID string, limit, offset int) ([]*group.GroupMessage, error) |
Read stored group messages |
GetMyGroups() ([]*group.Group, error) / GetGroupMembers(groupID string) ([]*group.Member, error) |
Listings |
UpdateGroupInfo(groupID, name, description string) error |
Update metadata |
PromoteMember / DemoteMember / KickMember |
Admin operations (each takes groupID string, memberAddr protocol.Address) |
LeaveGroup(groupID string) error |
Leave a group |
For the group cryptography design, see Group messaging.
TreeSyncClient and ZKPClient
Section titled “TreeSyncClient and ZKPClient”Lower-level building blocks, used internally by Client.Connect() and available directly:
func NewTreeSyncClient(relayAddr string) *TreeSyncClient// FetchTree() (*zkp.MerkleTree, error)// FetchTreeWithTimeout(timeout time.Duration) (*zkp.MerkleTree, error)// VerifyTreeIntegrity(tree *zkp.MerkleTree) error
func NewZKPClient(provingKey groth16.ProvingKey, tree *zkp.MerkleTree, identity *Identity, ephemeralPubKey *rsa.PublicKey) (*ZKPClient, error)// GenerateHandshakeProof() (*protocol.ZKHandshakeMessage, error)// GetEpoch() uint64, GetCommitment() zkp.Commitment, GetTreeIndex() uint64Usage example
Section titled “Usage example”The flow below mirrors the zentalk-client CLI: register once, then connect, look up a recipient, and send an encrypted message.
package main
import ( "log"
"github.com/ZentaChain/zentalk-node/pkg/client" "github.com/ZentaChain/zentalk-node/pkg/zkp")
func main() { // Load the Groth16 proving key (generated by the zkp-setup tool) pk, err := zkp.LoadProvingKey("./zkp-keys/proving_key.bin") if err != nil { log.Fatalf("load proving key: %v", err) }
cl := client.NewClient(&client.ClientConfig{ RelayAddr: "localhost:8080", IdentityPath: "./zkp-identity.json", ProvingKey: pk, })
// Load or create the local ZK identity if err := cl.Initialize(); err != nil { log.Fatalf("initialize: %v", err) }
// One-time registration of the identity's commitment with the relay if !cl.IsRegistered() { if err := cl.Register(map[string]string{"username": "alice"}); err != nil { log.Fatalf("register: %v", err) } }
// Connect anonymously (generates a ZK membership proof) if err := cl.Connect(); err != nil { log.Fatalf("connect: %v", err) } defer cl.Disconnect()
// Resolve the recipient and fetch their advertised public key addr, err := cl.LookupAddress("bob") if err != nil { log.Fatalf("lookup address: %v", err) } pubKey, err := cl.LookupKey(addr) if err != nil { log.Fatalf("lookup key: %v", err) // recipient must have run AdvertiseKey() }
// Send an encrypted message if err := cl.SendMessage(addr, pubKey, "hello over the relay"); err != nil { log.Fatalf("send: %v", err) }}To receive messages, the peer advertises its key and listens:
if err := cl.AdvertiseKey(); err != nil { log.Fatal(err)}
err := cl.StartMessageListener(func(msg *client.ReceivedMessage) { log.Printf("from %x: %s (%s)", msg.From[:8], msg.Content, msg.Timestamp)})if err != nil { log.Fatal(err)}select {} // keep listeningA runnable, self-contained example of the X3DH key-agreement primitives is also included in the repository at examples/x3dh_example.go (go run ./examples/x3dh_example.go).
The zentalk-client CLI
Section titled “The zentalk-client CLI”The CLI in cmd/zentalk-client runs the ZKP messaging flow end to end without requiring any code.
go build -o zentalk-client ./cmd/zentalk-clientFull flag and command reference: CLI reference.
End-to-end walkthrough
Section titled “End-to-end walkthrough”Proving keys must exist before connecting; generate them once with the setup tool:
go run cmd/zkp-setup/main.go -output ./zkp-keysThen, with a relay running on localhost:8080:
# Terminal 1 — Bob registers, advertises his key, and listens./zentalk-client -cmd register -username bob./zentalk-client -cmd register-username -username bob./zentalk-client -cmd advertise-key./zentalk-client -cmd listen
# Terminal 2 — Alice registers and sends Bob a message./zentalk-client -cmd register -username alice -identity ./alice-identity.json./zentalk-client -cmd send -identity ./alice-identity.json \ -username bob -message "hello bob"Related pages
Section titled “Related pages”- SDK overview — how this package fits alongside the REST API
- REST API — for HTTP integrations in Go via
net/http - Security overview — ZKP and E2EE model
- X3DH — the key agreement used for message encryption
