No description
Find a file
toki c9d21b9888 feat: add DisconnectInfo and fix stale heartbeat wait-timer race
Introduce DisconnectInfo with reason/error so consumers can distinguish
heartbeat timeouts from remote close, write errors, and parse failures.
Wire reasons through TCP/WS read and write paths.

Fix the heartbeat wait-timer to re-check waitingHBResponse when firing:
in paired idle (both peers heartbeat in the same cycle) the response
could arrive while the wait timer was being installed, causing a stale
disconnect. The timer now re-arms the interval instead of disconnecting
when the response was already observed.

Add regression tests: TestHeartbeatSurvivesPairedIdle, and assert
DisconnectInfo.Reason == heartbeat_timeout in the legitimate timeout
tests for both TCP and WebSocket.
2026-05-17 17:37:43 +09:00
.claude chore: update claude settings and gitignore 2026-04-30 22:33:53 +09:00
.vscode Update launch.json 2026-04-05 20:55:10 +09:00
agent-ops refactor: rename toki_socket to proto_socket across all languages 2026-05-02 07:19:12 +09:00
agent-task update typescript_kotlin crosstest and add agent-task logs 2026-04-27 18:33:01 +09:00
dart refactor: rename toki_socket to proto_socket across all languages 2026-05-02 07:19:12 +09:00
examples/go-module-consumer feat: add go-module-consumer example and update README 2026-05-02 07:47:52 +09:00
go feat: add DisconnectInfo and fix stale heartbeat wait-timer race 2026-05-17 17:37:43 +09:00
kotlin refactor: rename toki_socket to proto_socket across all languages 2026-05-02 07:19:12 +09:00
proto update: crosstest coverage and proto restructure changes 2026-04-25 07:00:53 +09:00
python refactor: rename toki_socket to proto_socket across all languages 2026-05-02 07:19:12 +09:00
tools update: crosstest coverage and proto restructure changes 2026-04-25 07:00:53 +09:00
typescript refactor: rename toki_socket to proto_socket across all languages 2026-05-02 07:19:12 +09:00
.codex Update protocol and add WebSocket protobuf client/server implementations 2026-04-05 20:44:37 +09:00
.cursorrules refactor: sync agent-ops skills and update domain rules 2026-04-19 22:03:08 +09:00
.gitignore chore: update claude settings and gitignore 2026-04-30 22:33:53 +09:00
AGENTS.md refactor: sync agent-ops skills and update domain rules 2026-04-19 22:03:08 +09:00
CLAUDE.md refactor: sync agent-ops skills and update domain rules 2026-04-19 22:03:08 +09:00
GEMINI.md refactor: sync agent-ops skills and update domain rules 2026-04-19 22:03:08 +09:00
PORTING_GUIDE.md refactor: rename toki_socket to proto_socket across all languages 2026-05-02 07:19:12 +09:00
PROTOCOL.md refactor: rename toki_socket to proto_socket across all languages 2026-05-02 07:19:12 +09:00
README.md feat: add go-module-consumer example and update README 2026-05-02 07:47:52 +09:00
VERSIONING.md refactor: rename toki_socket to proto_socket across all languages 2026-05-02 07:19:12 +09:00

Proto Socket

Binary socket protocol library for bidirectional, heterogeneous communication across languages and platforms.

Built on Protocol Buffers with TCP length-prefixed framing, WebSocket binary frames, type-based message routing, request-response correlation, and built-in heartbeat.


Design Principles

  • Keep the core transport layer thin and stable
  • Provide only the minimum common foundation for cross-language communication
  • Standardize framing, serialization, routing, request-response correlation, and heartbeat
  • Do not embed application semantics into the protocol core
  • Domain-specific concerns such as auth, session, agent workflow, chat features, and game logic belong in upper-layer implementations

Protocol

See PROTOCOL.md for the full wire format specification.

[4-byte big-endian length] [PacketBase protobuf bytes]

For WebSocket/WSS transports, each binary frame contains one PacketBase protobuf payload without the TCP length header.

Protocol compatibility is tracked separately from language package versions. See VERSIONING.md. Even while packages are not published, the checked-in implementations must preserve the documented protocol contract.


Implementations

Language Status Path Use case
Dart Available dart/ Flutter, Dart server
C# Planned csharp/ Unity, .NET
Kotlin Available kotlin/ Android, JVM
Swift Planned swift/ iOS, macOS
Go Available go/ Server, tooling, scripting
TypeScript Available typescript/ Browser, Node.js
Python Available python/ Server, tooling, scripting

New language implementations should start from PORTING_GUIDE.md and the templates in agent-ops/skills/project/add-proto-socket-crosstest-language/templates/. Mark an implementation available only after its same-language tests and cross-language tests pass.


Quick Start (Dart)

import 'package:proto_socket/proto_socket.dart';

// 1. Define your message in message_common.proto, generate with protoc

// 2. Implement a client
class MyClient extends ProtobufClient {
  MyClient(Socket socket) : super(socket, 30, 10, {
    MyMessage.getDefault().info_.qualifiedMessageName: MyMessage.fromBuffer,
  });
}

// 3. Implement a server
class MyServer extends ProtobufServer {
  MyServer() : super('0.0.0.0', 9090, (socket) => MyClient(socket));

  @override
  void onClientConnected(ProtobufClient client) {
    client.addListener<MyMessage>((msg) => print('Received: ${msg}'));
  }
}

// 4. Start
final server = MyServer();
await server.start();

// 5. Connect and send
final socket = await Socket.connect('localhost', 9090);
final client = MyClient(socket);
await client.send(MyMessage()..text = 'hello');

Adding Message Types

Edit the canonical proto at proto/message_common.proto, then regenerate all checked-in bindings:

tools/generate_proto.sh
tools/check_proto_sync.sh

The Go and Kotlin proto copies are allowed to keep only language-specific options such as option go_package or Java package/class options. tools/check_proto_sync.sh fails with a diff when their message schema drifts from proto/message_common.proto.


Quick Start (Go)

package main

import (
	"context"
	"net"
	"time"

	"google.golang.org/protobuf/proto"

	protoSocket "git.toki-labs.com/toki/common-proto-socket/go"
	"git.toki-labs.com/toki/common-proto-socket/go/packets"
)

func parserMap() protoSocket.ParserMap {
	return protoSocket.ParserMap{
		protoSocket.TypeNameOf(&packets.TestData{}): func(b []byte) (proto.Message, error) {
			m := &packets.TestData{}
			return m, proto.Unmarshal(b, m)
		},
	}
}

func main() {
	ctx := context.Background()

	server := protoSocket.NewTcpServer("127.0.0.1", 9090, func(conn net.Conn) *protoSocket.TcpClient {
		return protoSocket.NewTcpClient(conn, 30, 10, parserMap())
	})
	server.OnClientConnected = func(client *protoSocket.TcpClient) {
		protoSocket.AddRequestListenerTyped[*packets.TestData, *packets.TestData](
			&client.Communicator,
			func(req *packets.TestData) (*packets.TestData, error) {
				return &packets.TestData{Index: req.GetIndex(), Message: "echo: " + req.GetMessage()}, nil
			},
		)
	}
	if err := server.Start(ctx); err != nil {
		panic(err)
	}
	defer server.Stop()

	client, err := protoSocket.DialTcp(ctx, "127.0.0.1", 9090, 30, 10, parserMap())
	if err != nil {
		panic(err)
	}
	defer client.Close()

	res, err := protoSocket.SendRequestTyped[*packets.TestData, *packets.TestData](
		&client.Communicator,
		&packets.TestData{Index: 1, Message: "hello"},
		2*time.Second,
	)
	if err != nil {
		panic(err)
	}
	println(res.GetMessage())
}

Go also provides:

  • TCP: NewTcpServer, DialTcp, NewTcpServerTLS, DialTcpTLS
  • WebSocket: NewWsServer, DialWs, NewWsServerTLS, DialWss
  • Shared helpers: Send, SendRequest, AddListenerTyped, AddRequestListenerTyped, Broadcast

If you want a copyable scaffold for using this module from another repository, see examples/go-module-consumer/.


Running Tests

Local commands are documented here for development and troubleshooting. Continuous verification is expected to run in an external tool, with Jenkins planned to execute the full Dart, Go, and cross-language test suite.

cd dart
dart pub get
dart test
cd go
go test ./...
cd kotlin
./gradlew test

Cross-language checks:

cd go
go run ./crosstest/go_dart.go
go run ./crosstest/go_kotlin.go
cd dart
dart run crosstest/dart_go.dart
cd kotlin
./gradlew run -PmainClass=com.tokilabs.proto_socket.crosstest.KotlinGoKt

When proto files change, also run:

tools/check_proto_sync.sh