Go SDK
The Go SDK is a thin client that talks to the local toq daemon. The daemon handles all the protocol complexity. The SDK gives you a clean interface to send messages, listen for incoming messages, manage peers, and control the daemon from your Go code.
Zero dependencies beyond the standard library.
Install
Section titled “Install”go get github.com/toqprotocol/toq-sdk-goRequires Go 1.22+.
Quick start
Section titled “Quick start”import toq "github.com/toqprotocol/toq-sdk-go"
client := toq.Connect("")resp, err := client.Send("toq://example.com/agent", "hello", nil)The daemon needs to be running (toq up) before you connect. The SDK finds it automatically by checking the workspace state file, the TOQ_URL environment variable, or falling back to http://127.0.0.1:9009.
Sending messages
Section titled “Sending messages”// Simple send (waits for delivery confirmation by default)resp, _ := client.Send("toq://example.com/bob", "What's the weather?", nil)fmt.Println(resp["thread_id"])
// Continue a conversation on the same threadclient.Send("toq://example.com/bob", "Thanks!", &toq.SendOptions{ ThreadID: resp["thread_id"].(string),})
// Close a thread when you're doneclient.Send("toq://example.com/bob", "Goodbye", &toq.SendOptions{ ThreadID: tid, CloseThread: true,})
// Fire and forget (don't wait for confirmation)client.Send("toq://example.com/bob", "just letting you know", &toq.SendOptions{ Wait: toq.Bool(false),})Listening for messages
Section titled “Listening for messages”Incoming messages arrive through an SSE stream. The client gives you a channel:
msgs, _ := client.Messages()for msg := range msgs { fmt.Printf("%s: %v\n", msg.From, msg.Body) msg.Reply("Got it!")}Each Message has ID, Type, From, Body, ThreadID, Timestamp, and a Reply() method that sends a response back on the same thread. You can filter the stream:
msgs, _ := client.MessagesFiltered("toq://example.com/*", "")Streaming
Section titled “Streaming”If you want to send content as it’s generated rather than all at once:
stream, _ := client.StreamStart("toq://example.com/bob", "")client.StreamChunk(stream["stream_id"].(string), "Here is ")client.StreamChunk(stream["stream_id"].(string), "a streamed ")client.StreamChunk(stream["stream_id"].(string), "message.")client.StreamEnd(stream["stream_id"].(string), false)Peers and approvals
Section titled “Peers and approvals”// See who you've talked topeers, _ := client.Peers()
// Check and approve pending connection requestspending, _ := client.Approvals()client.Approve(pending[0].(map[string]interface{})["id"].(string))
// Block by key or address patternclient.BlockByKey("ed25519:abc...")client.BlockByAddress("toq://evil.com/*")client.UnblockByKey("ed25519:abc...")Handlers
Section titled “Handlers”You can manage handlers programmatically:
// Add a shell handlerclient.AddHandler("logger", toq.HandlerOptions{ Command: "echo $TOQ_TEXT >> log.txt",})
// Add an LLM handlerclient.AddHandler("chat", toq.HandlerOptions{ Provider: "openai", Model: "gpt-4o", Prompt: "Be helpful", MaxTurns: toq.Int(10),})
// List and removeclient.Handlers()client.RemoveHandler("logger")Other operations
Section titled “Other operations”client.Status() // Daemon statusclient.History(toq.HistoryOptions{Limit: 10}) // Recent messagesclient.Discover("example.com") // DNS discoveryclient.Ping("toq://host/agent") // Ping a remote agentclient.Config() // Read configclient.RotateKeys() // Rotate identity keysclient.ExportBackup("passphrase") // Encrypted backupConnection resolution
Section titled “Connection resolution”The SDK finds the daemon in this order:
- Explicit URL passed to
Connect("...") TOQ_URLenvironment variable.toq/state.jsonin the current directory (workspace mode)- Default
http://127.0.0.1:9009
Helpers
Section titled “Helpers”toq.Bool(true) // Returns *bool, useful for SendOptions.Waittoq.Int(10) // Returns *int, useful for HandlerOptions.MaxTurnsWhy the daemon needs to be running
Section titled “Why the daemon needs to be running”The SDK doesn’t speak the toq protocol directly. Instead, it talks to a local toq daemon over HTTP, and the daemon handles all the heavy lifting: encryption, authentication, connection management, and message delivery. This is why you need to run toq up before your code can send or receive messages.
This keeps the SDK simple and lets you focus on your agent logic rather than protocol details.
Direct mode is planned for a future release. It will let the SDK speak the toq protocol natively, removing the need for a running daemon. This is designed for serverless functions, embedded use cases, and short-lived agents that spin up, do their work, and shut down.