{"id":15061155,"url":"https://github.com/imandra-ai/batrpc","last_synced_at":"2026-02-03T22:32:45.406Z","repository":{"id":221424386,"uuid":"753807142","full_name":"imandra-ai/batrpc","owner":"imandra-ai","description":"RPC framework on top of protobuf.","archived":false,"fork":false,"pushed_at":"2024-09-04T17:58:30.000Z","size":7231,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":8,"default_branch":"main","last_synced_at":"2025-07-09T17:04:50.263Z","etag":null,"topics":["ocaml","protobuf","rpc","streaming"],"latest_commit_sha":null,"homepage":"http://docs.imandra.ai/batrpc/","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/imandra-ai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-06T20:35:22.000Z","updated_at":"2024-08-02T13:49:10.000Z","dependencies_parsed_at":"2024-02-21T20:28:19.392Z","dependency_job_id":"4fef9135-ddb7-48de-819b-fc1226eea734","html_url":"https://github.com/imandra-ai/batrpc","commit_stats":null,"previous_names":["imandra-ai/batrpc"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/imandra-ai/batrpc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imandra-ai%2Fbatrpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imandra-ai%2Fbatrpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imandra-ai%2Fbatrpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imandra-ai%2Fbatrpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/imandra-ai","download_url":"https://codeload.github.com/imandra-ai/batrpc/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/imandra-ai%2Fbatrpc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29060578,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-03T22:28:58.191Z","status":"ssl_error","status_checked_at":"2026-02-03T22:28:56.515Z","response_time":96,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ocaml","protobuf","rpc","streaming"],"created_at":"2024-09-24T23:10:06.827Z","updated_at":"2026-02-03T22:32:45.391Z","avatar_url":"https://github.com/imandra-ai.png","language":"OCaml","readme":"# BatRPC\n\n[![Build and Test](https://github.com/imandra-ai/batrpc/actions/workflows/main.yml/badge.svg)](https://github.com/imandra-ai/batrpc/actions/workflows/main.yml)\n\nThis is a RPC framework for OCaml, based on protobuf as a wire format.\n\n## Overview\n\nThe goal of BatRPC is to provide an efficient and flexible RPC system for\nour needs at Imandra. It is designed for long-lived connections between\ntwo processes that can both act as a server and a client.\n\n[Protobuf](https://protobuf.dev) is used as an\n[IDL](https://en.wikipedia.org/wiki/Interface_description_language) to\ndescribe the types used for communication, as well as the actual\nRPC endpoints.\nProtobuf also generates (de)serialization code for these types, and\nbundles for the services.\n\nMultiple services can be provided on a single connection, provided\nthey have distinct names.\n\n## Features\n\n- auto-generation of types, services, and (de)serialization using [ocaml-protoc](https://github.com/mransan/ocaml-protoc/)\n- basic per-message compression for large messages, using `deflate`. Stream-level\n    compression is not supported by BatRPC, but could be implemented transparently:\n    a `Client.t` or `Server.For_client.t` takes a pair of input/output byte streams which could be\n    compressed or encrypted. The types from [iostream](https://github.com/c-cube/ocaml-iostream/) are\n    used to abstract over the byte streams.\n- messages carry headers, ie pairs of strings, pretty much like HTTP headers.\n- middlewares on the server side. A middleware can take an incoming request\n    and its future reply, and insert metadata in headers, perform logging, tracing, etc.\n- baked-in concurrency using [moonpool](https://github.com/c-cube/moonpool/) as a\n    thread pool and future library.\n- kinds of requests:\n    * [x] simple request/response\n    * [x] client-side streaming (the client sends a stream of values)\n    * [x] server-side streaming (the server returns a stream of values)\n    * [ ] bidirectional streaming\n\n## Example\n\n\u003cdetails\u003e\n\u003csummary\u003eA basic example, fully worked out\u003c/summary\u003e\n\nThe code is in `examples/trivial`.\n\nGiven this file (see `examples/trivial/trivial.proto`):\n\n```proto\nmessage Pair {\n  string x = 1;\n  string y = 2;\n}\n\nmessage BigString {\n  string msg = 1;\n}\n\nmessage Count {\n  int32 count = 1;\n}\n\nmessage SingleInt {\n  int32 i = 0;\n}\n\nservice Swapper {\n  rpc swap(Pair) returns (Pair);\n  rpc count_chars(BigString) returns (Count);\n}\n```\n\nand the dune rules\n\n```scheme\n(rule\n (targets trivial.ml trivial.mli)\n (deps trivial.proto)\n (mode promote)\n (action\n  (run ocaml-protoc --binary --pp --yojson --services --make --ml_out ./ %{deps})))\n```\n\nWe get files `trivial.ml` and `trivial.mli`. The signature generated from this is, roughly:\n\n```trivial.mli\ntype pair = {\n  x : string;\n  y : string;\n  artificial_delay_s : float option;\n}\n\ntype big_string = {\n  msg : string;\n}\n\ntype count = {\n  count : int32;\n}\n\ntype single_int = {\n  i : int32;\n}\n\nval pp_pair : Format.formatter -\u003e pair -\u003e unit \n(* … *)\n\n\nval encode_pb_pair : pair -\u003e Pbrt.Encoder.t -\u003e unit\n(* … *)\n\nval decode_pb_pair : Pbrt.Decoder.t -\u003e pair\n(* … *)\n\n\n(** Swapper service *)\nmodule Swapper : sig\n  open Pbrt_services\n  open Pbrt_services.Value_mode\n  \n  module Client : sig\n    \n    val swap : (pair, unary, pair, unary) Client.rpc\n    \n    val count_chars : (big_string, unary, count, unary) Client.rpc\n  end\n  \n  module Server : sig\n    (** Produce a server implementation from handlers *)\n    val make : \n      swap:((pair, unary, pair, unary) Server.rpc -\u003e 'handler) -\u003e\n      count_chars:((big_string, unary, count, unary) Server.rpc -\u003e 'handler) -\u003e\n      unit -\u003e 'handler Pbrt_services.Server.t\n  end\nend\n```\n\nWe can then use the `batrpc` library and this generated code, together, to\nimplement RPC clients and servers.\nHere \"client\" and \"server\" really means \"network client\" and \"network server\"\n(ie clients are the ones opening connections to servers); from the RPC\npoint of view, once the connection is established, both ends act both are\nclient and server in the sense that they can provide services, and emit\nrequests to services.\n\n### Client side\n\nLet's write a TCP client.\n\n```ocaml\nlet (let@) = (@@)\nlet port = 12345\n\nmodule RPC = Batrpc\nmodule Client = RPC.Basic_client\nmodule Fut = Moonpool.Fut\n\nlet () =\n  let addr = Unix.ADDR_INET (Unix.inet_addr_loopback, port) in\n  let timer = RPC.Simple_timer.create () in\n\n  Printf.printf \"connecting...\\n%!\";\n  let client : Client.t =\n    RPC.Tcp_client.connect ~timer addr |\u003e RPC.Error.unwrap\n  in\n  let@ () = Fun.protect ~finally:(fun () -\u003e Client.close_and_join client) in\n\n  let pair = Trivial.make_pair ~x:\"hello\" ~y:\"world\" () in\n  Format.printf \"pair: %a@.\" Trivial.pp_pair pair;\n\n  let fut_pair_swapped : Trivial.pair Moonpool.Fut.t =\n    Client.call client ~timeout_s:2. Trivial.Swapper.Client.swap pair\n  in\n\n  (* the request is in-flight, we can do other things here … *)\n\n  (* now wait for the result *)\n  let pair_swapped = Fut.wait_block_exn fut_pair_swapped in\n  Format.printf \"swapped pair: %a@.\" Trivial.pp_pair pair_swapped;\n  ()\n```\n\n\n### Server side\n\n```ocaml\nlet ( let@ ) = ( @@ )\nlet port = 12345\n\nmodule RPC = Batrpc\nmodule Fut = Moonpool.Fut\n\n(* this is where we implement the actual logic for the services *)\n\nlet trivial_service =\n  Trivial.Swapper.Server.make\n    ~swap:(fun rpc -\u003e\n      RPC.mk_handler rpc @@ fun (p : Trivial.pair) -\u003e\n      let@ _sp = Trace.with_span ~__FILE__ ~__LINE__ \"test.swap\" in\n      Fut.return @@ Trivial.make_pair ~x:p.y ~y:p.x ())\n    ~count_chars:(fun rpc -\u003e\n      RPC.mk_handler rpc @@ fun (msg : Trivial.big_string) -\u003e\n      let n = String.length msg.msg in\n      Fut.return @@ Trivial.make_count ~count:(Int32.of_int n) ())\n    ()\n\n(* we could host multiple services, here we only have one *)\nlet services = [ trivial_service ]\n\nlet () =\n  let active = RPC.Simple_switch.create () in\n  let timer = RPC.Simple_timer.create () in\n\n  (* we need a thread pool to run the tasks *)\n  let@ runner = Moonpool.Ws_pool.with_ ~num_threads:8 () in\n\n  let addr = Unix.ADDR_INET (Unix.inet_addr_loopback, port) in\n  let server : RPC.Tcp_server.t =\n    RPC.Tcp_server.create ~active ~runner ~timer ~services addr\n    |\u003e RPC.Error.unwrap\n  in\n\n  (* background thread to accept connection *)\n  Format.eprintf \"listening on port %d@.\" port;\n  RPC.Tcp_server.run server\n```\n\n\n\u003c/details\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimandra-ai%2Fbatrpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fimandra-ai%2Fbatrpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fimandra-ai%2Fbatrpc/lists"}