Gotalk
Async peer communication protocol & library
Install / Use
/learn @rsms/GotalkREADME
gotalk
Gotalk exists to make it easy for programs to talk with one another over the internet, like a web app coordinating with a web server, or a bunch of programs dividing work amongst each other.
Gotalk...
- is an efficient, easily debuggable multiplexing data transfer protocol
- is transport agnostic: works on any byte stream
- offers a high-level, easy-to-get-started API for WebSockets
- enables arbitrary number of requests & responses over a single persistent connection
- includes a small built-in JavaScript library
- provides a small and focused [Go API][godoc]
[Go API Documentation on godoc.org →][godoc]
Usage
Gotalk is a simple go module - import it into your program and go build:
import "github.com/rsms/gotalk"
To use a specific version, run go get github.com/rsms/gotalk@v1.0.1 (substituting the version number for the version you desire.)
Examples can be found in the examples directory.
Build them with go build:
$ cd examples/websocket-chat
$ go build
$ ./websocket-chat
Listening on http://localhost:1235/
Here's a minimal but complete example program: (examples/websocket-minimal)
package main
import (
"net/http"
"github.com/rsms/gotalk"
)
func main() {
gotalk.Handle("echo", func(in string) (string, error) {
return in, nil
})
http.Handle("/gotalk/", gotalk.WebSocketHandler())
http.Handle("/", http.FileServer(http.Dir(".")))
print("Listening on http://localhost:1234/\n")
panic(http.ListenAndServe("localhost:1234", nil))
}
Developing Gotalk & contributing
See CONTRIBUTING.md
Other implementations
Introduction

Gotalk takes the natural approach of bidirectional and concurrent communication — any peer have the ability to expose "operations" as well as asking other peers to perform operations. The traditional restrictions of who can request and who can respond usually associated with a client-server model is nowhere to be found in Gotalk.
Gotalk in a nutshell
Bidirectional — There's no discrimination on capabilities depending on who connected or who accepted. Both "servers" and "clients" can expose operations as well as send requests to the other side.
Concurrent — Requests, results, and notifications all share a single connection without blocking each other by means of pipelining. There's no serialization on request-result or even for a single large message, as the Gotalk protocol is frame-based and multiplexes messages over a single connection. This means you can perform several requests at once without having to think about queueing or blocking.

Simple — Gotalk has a simple and opinionated API with very few components. You expose an operation via "handle" and send requests via "request".
Debuggable — The Gotalk protocol's wire format is ASCII-based for easy on-the-wire inspection of data. For example, here's a protocol message representing an operation request: r0001005hello00000005world. The Gotalk protocol can thus be operated over any reliable byte transport.
Practical — Gotalk includes a JavaScript implementation for Web Sockets alongside the full-featured Go implementation, making it easy to build real-time web applications. The Gotalk source code also includes a number of easily-readable examples.
By example
There are a few examples in the examples directory demonstrating Gotalk. But let's explore a simple program right now — here's a little something written in Go which demonstrates the use of an operation named "greet":
func server() {
gotalk.Handle("greet", func(in GreetIn) (GreetOut, error) {
return GreetOut{"Hello " + in.Name}, nil
})
if err := gotalk.Serve("tcp", "localhost:1234"); err != nil {
log.Fatalln(err)
}
}
func client() {
s, err := gotalk.Connect("tcp", "localhost:1234")
if err != nil {
log.Fatalln(err)
}
greeting := &GreetOut{}
if err := s.Request("greet", GreetIn{"Rasmus"}, greeting); err != nil {
log.Fatalln(err)
}
log.Printf("greeting: %+v\n", greeting)
s.Close()
}
Let's look at the above example in more detail, broken apart to see what's going on.
We begin by importing the gotalk library together with log which we use for printing to the console:
package main
import (
"log"
"github.com/rsms/gotalk"
)
We define two types: Expected input (request parameters) and output (result) for our "greet" operation:
type GreetIn struct {
Name string `json:"name"`
}
type GreetOut struct {
Greeting string `json:"greeting"`
}
Registers a process-global request handler for an operation called "greet" accepting parameters of type GreetIn, returning results of type GreetOut:
func server() {
gotalk.Handle("greet", func(in GreetIn) (GreetOut, error) {
return GreetOut{"Hello " + in.Name}, nil
})
Finally at the bottom of our server function we call gotalk.Serve, which starts a local TCP server on port 1234:
if err := gotalk.Serve("tcp", "localhost:1234"); err != nil {
log.Fatalln(err)
}
}
In out client function we start by connecting to the server:
func client() {
s, err := gotalk.Connect("tcp", "localhost:1234")
if err != nil {
log.Fatalln(err)
}
Finally we send a request for "greet" and print the result:
greeting := GreetOut{}
if err := s.Request("greet", GreetIn{"Rasmus"}, &greeting); err != nil {
log.Fatalln(err)
}
log.Printf("greeting: %+v\n", greeting)
s.Close()
}
Output:
greeting: {Greeting:Hello Rasmus}
Gotalk in the web browser
Gotalk is implemented not only in the full-fledged Go package, but also in a JavaScript library. This allows writing web apps talking Gotalk via Web Sockets possible.
// server.go:
package main
import (
"net/http"
"github.com/rsms/gotalk"
)
func main() {
gotalk.Handle("echo", func(in string) (string, error) {
return in, nil
})
http.Handle("/gotalk/", gotalk.WebSocketHandler())
http.Handle("/", http.FileServer(http.Dir(".")))
err := http.ListenAndServe("localhost:1234", nil)
if err != nil {
panic(err)
}
}
In our html document, we begin by registering any operations we can handle:
<!-- index.html -->
<body>
<script type="text/javascript" src="/gotalk/gotalk.js"></script>
<script>
gotalk.handle('greet', function (params, result) {
result({ greeting: 'Hello ' + params.name });
});
</script>
Notice how we load a JavaScript from "/gotalk/gotalk.js" — a gotalk web socket server embeds a matching web browser JS library which it returns from {path where gotalk web socket is mounted}/gotalk.js. It uses Etag cache validation, so you shouldn't need to think about "cache busting" the URL.
We can't "listen & accept" connections in a web browser, but we can "connect" so we do just that:
<!-- index.html -->
<body>
<script type="text/javascript" src="/gotalk/gotalk.js"></script>
<script>
gotalk.handle('greet', function (params, result) {
result({ greeting: 'Hello ' + params.name });
});
var s = gotalk.connection().on('open', function () {
// do something useful
}).on('close', function (err) {
if (err.isGotalkProtocolError) return console.error(err);
});
</script>
This is enough for enabling the server to do things in the browser ...
But you probably want to have the browser send requests to the server, so let's send a "echo" request just as our connection opens:
var s = gotalk.connection().on('open', function () {
s.request("echo", "Hello world", function (err, result) {
if (err) return console.error('echo failed:', err);
console.log('echo result:', result);
});
});
We could rewrite our code like this to allow some UI component to send a request:
var s = gotalk.connection();
button.addEventListener('click', function () {
s.request("echo", "Hello world", function (err, result) {
if (err) return console.error('echo failed:', err);
console.log('echo result:', result);
});
});
The request will fail with an error "socket is closed" if the user clicks our button while the connection isn't open.
There are two ways to open a connection on a socket: Sock.prototype.open which simply opens a connection, and Sock.prototype.openKeepAlive which keeps the connection open, reconnecting as needed with exponential back-off and internet reachability knowledge. gotalk.connection() is a short-hand for creating a new Sock with gotalk.defaultHandlers and then calling openKeepAlive on it.
Protocol and wire format
The wire format is designed to be human-readable and flexible; it's byte-based and can be efficiently implemented in a number of environments ranging from HTTP and WebSocket in a web browser to raw TCP in Go or C. The protocol provides only a small set of operations on which more elaborate operations can be modeled by the user.
This document describes protocol version 1
Here's a complete description of the protocol:
conversation = ProtocolVersion Message*
message = SingleRequest | StreamRequest
| SingleResult | StreamResult
| ErrorResult | RetryResult
| Notification | ProtocolError
Proto
Related Skills
canvas
347.6kCanvas Skill Display HTML content on connected OpenClaw nodes (Mac app, iOS, Android). Overview The canvas tool lets you present web content on any connected node's canvas view. Great for: -
node-connect
347.6kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
xurl
347.6kA CLI tool for making authenticated requests to the X (Twitter) API. Use this skill when you need to post tweets, reply, quote, search, read posts, manage followers, send DMs, upload media, or interact with any X API v2 endpoint.
frontend-design
108.4kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
