{"id":19674236,"url":"https://github.com/mirage/capnp-rpc","last_synced_at":"2025-04-05T09:07:03.164Z","repository":{"id":21011762,"uuid":"91577512","full_name":"mirage/capnp-rpc","owner":"mirage","description":"Cap'n Proto RPC implementation","archived":false,"fork":false,"pushed_at":"2025-02-04T10:27:57.000Z","size":5135,"stargazers_count":105,"open_issues_count":9,"forks_count":21,"subscribers_count":11,"default_branch":"master","last_synced_at":"2025-03-29T08:07:06.387Z","etag":null,"topics":["capn-proto","capnproto","ocaml-library","rpc-framework"],"latest_commit_sha":null,"homepage":"","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mirage.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE.md","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":"2017-05-17T13:06:17.000Z","updated_at":"2025-02-04T10:21:56.000Z","dependencies_parsed_at":"2024-11-07T13:24:54.771Z","dependency_job_id":"d594af15-15d6-4fb3-ae7d-53fa0859bd00","html_url":"https://github.com/mirage/capnp-rpc","commit_stats":{"total_commits":381,"total_committers":14,"mean_commits":"27.214285714285715","dds":0.4934383202099738,"last_synced_commit":"a9b4e9bf64e4fc2ecfa7e45feba66410e56831b0"},"previous_names":[],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirage%2Fcapnp-rpc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirage%2Fcapnp-rpc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirage%2Fcapnp-rpc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mirage%2Fcapnp-rpc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mirage","download_url":"https://codeload.github.com/mirage/capnp-rpc/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247312077,"owners_count":20918344,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["capn-proto","capnproto","ocaml-library","rpc-framework"],"created_at":"2024-11-11T17:17:38.289Z","updated_at":"2025-04-05T09:07:03.131Z","avatar_url":"https://github.com/mirage.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OCaml Cap'n Proto RPC library\n\nCopyright 2017 Docker, Inc.\nCopyright 2024 Thomas Leonard.\nSee [LICENSE.md](LICENSE.md) for details.\n\n[API documentation][api]\n\n## Contents\n\n\u003c!-- vim-markdown-toc GFM --\u003e\n\n* [Overview](#overview)\n* [Status](#status)\n* [Installing](#installing)\n* [Structure of the library](#structure-of-the-library)\n* [Tutorial](#tutorial)\n  * [A basic echo service](#a-basic-echo-service)\n  * [Passing capabilities](#passing-capabilities)\n  * [Networking](#networking)\n    * [The server side](#the-server-side)\n    * [The client side](#the-client-side)\n    * [Separate processes](#separate-processes)\n* [Pipelining](#pipelining)\n* [Guide to sturdy refs](#guide-to-sturdy-refs)\n  * [Hosting multiple sturdy refs](#hosting-multiple-sturdy-refs)\n  * [Creating services dynamically](#creating-services-dynamically)\n  * [The Persistence API](#the-persistence-api)\n  * [Persisting sturdy refs](#persisting-sturdy-refs)\n* [Further reading](#further-reading)\n* [FAQ](#faq)\n  * [Why does my connection stop working after 10 minutes?](#why-does-my-connection-stop-working-after-10-minutes)\n  * [How can I return multiple results?](#how-can-i-return-multiple-results)\n  * [Can I create multiple instances of an interface dynamically?](#can-i-create-multiple-instances-of-an-interface-dynamically)\n  * [Can I get debug output?](#can-i-get-debug-output)\n  * [How can I debug reference counting problems?](#how-can-i-debug-reference-counting-problems)\n  * [How can I import a sturdy ref that I need to start my vat?](#how-can-i-import-a-sturdy-ref-that-i-need-to-start-my-vat)\n  * [How can I release other resources when my service is released?](#how-can-i-release-other-resources-when-my-service-is-released)\n  * [Is there an interactive version I can use for debugging?](#is-there-an-interactive-version-i-can-use-for-debugging)\n  * [Can I set up a direct 2-party connection over a pre-existing channel?](#can-i-set-up-a-direct-2-party-connection-over-a-pre-existing-channel)\n* [Contributing](#contributing)\n  * [Conceptual model](#conceptual-model)\n  * [Building](#building)\n  * [Testing](#testing)\n  * [Fuzzing](#fuzzing)\n\n\u003c!-- vim-markdown-toc --\u003e\n\n## Overview\n\n[Cap'n Proto][] is a capability-based RPC system with bindings for many languages.\nSome key features:\n\n- APIs are defined using a schema file, which is compiled to create bindings for different languages automatically.\n\n- Schemas can be upgraded in many ways without breaking backwards-compatibility.\n\n- Messages are built up and read in-place, making it very fast.\n\n- Messages can contain *capability references*, allowing the sender to share access to a service. Access control is handled automatically.\n\n- Messages can be *pipelined*. For example, you can ask one service where another one is, and then immediately start calling methods on it. The requests will be sent to the first service, which will either handle them (in the common case where the second service is in the same place) or forward them until you can establish a direct connection.\n\n- Messages are delivered in [E-Order][], which means that messages sent over a reference will arrive in the order in which they were sent, even if the path they take through the network gets optimised at some point.\n\nThis library should be used with the [capnp-ocaml][] schema compiler, which generates bindings from schema files.\n\n\n## Status\n\nRPC Level 2 is complete, with encryption and authentication using TLS and support for persistence.\n\nThe library has unit tests and AFL fuzz tests that cover most of the core logic.\nIt is used as the RPC system in [ocaml-ci][] and [ocluster][].\n\nThe default network provided supports TCP and Unix-domain sockets, both with or without TLS.\nFor two-party networking, you can provide any bi-directional byte stream (satisfying the `Eio.Flow.two_way` signature)\nto the library to create a connection.\nYou can also define your own network types.\n\nLevel 3 support is not implemented yet, so if host Alice has connections to hosts Bob and Carol and passes an object hosted at Bob to Carol, the resulting messages between Carol and Bob will be routed via Alice.\nUntil that is implemented, Carol can ask Bob for a persistent reference (sturdy ref) and then connect directly to that.\n\n\n## Installing\n\nTo install, you will need a platform with the capnproto package available (e.g. Debian \u003e= 9). Then (using opam 2.1 or later):\n\n    opam install capnp-rpc-unix\n    \n## Structure of the library\n\n**Note:** This README documents the newer Eio API. For the 1.x Lwt API, see an older version of the README. See the [CHANGES.md](./CHANGES.md) file for instructions on migrating to 2.0.\n\nThe code is split into several packages:\n\n- `capnp-rpc` allows you to define and use services.\n\n- `capnp-rpc-net` adds networking support, including TLS.\n\n- `capnp-rpc-unix` adds helper functions for parsing command-line arguments and setting up connections over Unix sockets.\n\n**Libraries** that consume or provide Cap'n Proto services should normally depend only on `capnp-rpc`,\nsince they shouldn't care whether the services they use are local or accessed over some kind of network.\n\n**Applications** will normally want to use `capnp-rpc-net` and, in most cases, `capnp-rpc-unix`.\n\n## Tutorial\n\nThis tutorial creates a simple echo service and then extends it.\nIt shows how to use most of the features of the library, including defining services,\nsending and receiving messages, and using encryption and authentication over network links.\n\nWhen following the tutorial, you should be able to create all the files yourself, following the instructions.\nIf you get stuck, you can find complete solutions in the [examples](./examples/) directory.\n\n### A basic echo service\n\nWe start by writing a [Cap'n Proto schema file][schema]:\n\n```capnp\ninterface Echo {\n  ping @0 (msg :Text) -\u003e (reply :Text);\n}\n```\n\nThis defines the `Echo` interface as having a single method called `ping`\nwhich takes a struct containing a text field called `msg`\nand returns a struct containing another text field called `reply`.\n\nSave this as `echo_api.capnp` and compile it using capnp:\n\n\u003c!-- $MDX skip --\u003e\n```sh\n$ capnp compile echo_api.capnp -o ocaml\necho_api.capnp:1:1: error: File does not declare an ID.  I've generated one for you.\nAdd this line to your file:\n@0xb287252b6cbed46e;\n```\n\nEvery interface needs a globally unique ID.\nIf you don't have one, capnp will pick one for you, as shown above.\nAdd the line to the start of the file to get:\n\n\u003c!-- $MDX include,file=examples/v1/echo_api.capnp --\u003e\n```capnp\n@0xb287252b6cbed46e;\n\ninterface Echo {\n  ping @0 (msg :Text) -\u003e (reply :Text);\n}\n```\n\nNow it can be compiled:\n\n\u003c!-- $MDX dir=examples/v1 --\u003e\n```sh\n$ capnp compile echo_api.capnp -o ocaml\necho_api.capnp --\u003e echo_api.mli echo_api.ml\n```\n\nThe next step is to implement a client and server (in a new `echo.ml` file) using the generated `Echo_api` OCaml module.\n\nFor the server, you should inherit from the generated `Api.Service.Echo.service` class:\n\n\u003c!-- $MDX include,file=examples/v1/echo.ml,part=server --\u003e\n```ocaml\nmodule Api = Echo_api.MakeRPC(Capnp_rpc)\n\nopen Capnp_rpc.Std\n\nlet local =\n  let module Echo = Api.Service.Echo in\n  Echo.local @@ object\n    inherit Echo.service\n\n    method ping_impl params release_param_caps =\n      let open Echo.Ping in\n      let msg = Params.msg_get params in\n      release_param_caps ();\n      let response, results = Service.Response.create Results.init_pointer in\n      Results.reply_set results (\"echo:\" ^ msg);\n      Service.return response\n  end\n```\n\nThe first line (`module Api`) instantiates the generated code to use this library's RPC implementation.\n\nThe service object must provide one OCaml method for each method defined in the schema file, with `_impl` on the end of each one.\n\nThere's a bit of ugly boilerplate here, but it's quite simple:\n\n- The `Api.Service.Echo.Ping` module defines the server-side API for the `ping` method.\n- `Ping.Params` is a reader for the parameters.\n- `Ping.Results` is a builder for the results.\n- `msg` is the string value of the `msg` field.\n- `release_param_caps` releases any capabilities passed in the parameters.\n  In this case there aren't any, but remember that a client using some future\n  version of this protocol might pass some optional capabilities, and so you\n  should always free them anyway.\n- `Service.Response.create Results.init_pointer` creates a new response message, using `Ping.Results.init_pointer` to initialise the payload contents.\n- `response` is the complete message to be sent back, and `results` is the data part of it.\n- `Service.return` returns the results immediately (rather than returning a promise).\n\nThe client implementation is similar, but uses `Api.Client` instead of `Api.Service`.\nHere, we have a *builder* for the parameters and a *reader* for the results.\n`Api.Client.Echo.Ping.method_id` is a globally unique identifier for the ping method.\n\n\u003c!-- $MDX include,file=examples/v1/echo.ml,part=client --\u003e\n```ocaml\nmodule Echo = Api.Client.Echo\n\nlet ping t msg =\n  let open Echo.Ping in\n  let request, params = Capability.Request.create Params.init_pointer in\n  Params.msg_set params msg;\n  Capability.call_for_value_exn t method_id request |\u003e Results.reply_get\n```\n\n`Capability.call_for_value_exn` sends the request message to the service and waits for the response to arrive.\nIf the response is an error, it raises an exception.\n`Results.reply_get` extracts the `reply` field of the result.\n\nWe don't need to release the capabilities of the results, as `call_for_value_exn` does that automatically.\nWe'll see how to handle capabilities later.\n\nWith the boilerplate out of the way, we can now write a `main.ml` to test it:\n\n\u003c!-- $MDX include,file=examples/v1/main.ml --\u003e\n```ocaml\nopen Eio.Std\n\nlet () =\n  Logs.set_level (Some Logs.Warning);\n  Logs.set_reporter (Logs_fmt.reporter ())\n\nlet () =\n  Eio_main.run @@ fun _ -\u003e\n  let service = Echo.local in\n  let reply = Echo.ping service \"foo\" in\n  traceln \"Got reply %S\" reply\n```\n\n\u003cp align='center'\u003e\n  \u003cimg src=\"./diagrams/ping.svg\"/\u003e\n\u003c/p\u003e\n\nHere's a suitable `dune` file to compile the schema file and then the generated OCaml files:\n\n\u003c!-- $MDX include,file=examples/v1/dune --\u003e\n```\n(executable\n (name main)\n (libraries eio_main capnp-rpc logs.fmt))\n\n(rule\n (targets echo_api.ml echo_api.mli)\n (deps    echo_api.capnp)\n (action (run capnp compile -o %{bin:capnpc-ocaml} %{deps})))\n```\n\nWith this, you can now delete the generated files from your source directory:\n\n\u003c!-- $MDX dir=examples/v1 --\u003e\n```sh\n$ rm echo_api.ml echo_api.mli\n```\n\nThe service is now usable:\n\n\u003c!-- $MDX skip --\u003e\n```bash\n$ opam install capnp-rpc\n```\n\u003c!-- $MDX dir=examples/v1 --\u003e\n```bash\n$ dune exec ./main.exe\n+Got reply \"echo:foo\"\n```\n\nThis isn't very exciting, so let's add some capabilities to the protocol...\n\n### Passing capabilities\n\nLet's update the schema:\n\n\u003c!-- $MDX include,file=examples/v2/echo_api.capnp --\u003e\n```capnp\n@0xb287252b6cbed46e;\n\ninterface Callback {\n  log @0 (msg :Text) -\u003e ();\n}\n\ninterface Echo {\n  ping      @0 (msg :Text) -\u003e (reply :Text);\n  heartbeat @1 (msg :Text, callback :Callback) -\u003e ();\n}\n```\n\nThis version of the protocol adds a `heartbeat` method.\nInstead of returning the text directly, it will send it to a callback at regular intervals.\n\nThe new `heartbeat_impl` method looks like this:\n\n\u003c!-- $MDX include,file=examples/v2/echo.ml,part=server-heartbeat --\u003e\n```ocaml\n    method heartbeat_impl params release_params =\n      let open Echo.Heartbeat in\n      let msg = Params.msg_get params in\n      let callback = Params.callback_get params in\n      release_params ();\n      match callback with\n      | None -\u003e Service.fail \"No callback parameter!\"\n      | Some callback -\u003e\n        Capability.with_ref callback (notify ~delay msg)\n```\n\nNote that all parameters in Cap'n Proto are optional, so we have to check for `callback` not being set\n(data parameters such as `msg` get a default value from the schema, which is\n`\"\"` for strings if not set explicitly).\n\nYou'll need to add a `~delay` argument to `local` too, to configure the time between messages.\n\n`Capability.with_ref x f` calls `f x` and then releases `x` (capabilities are ref-counted).\n\n`notify ~delay msg callback` just sends a few messages to `callback` in a loop:\n\n\u003c!-- $MDX include,file=examples/v2/echo.ml,part=notify --\u003e\n```ocaml\nlet notify ~delay msg callback =\n  let rec loop = function\n    | 0 -\u003e\n      Service.return_empty ()\n    | i -\u003e\n      match Callback.log callback msg with\n      | Error (`Capnp e) -\u003e Service.error e\n      | Ok () -\u003e\n        Eio.Time.Timeout.sleep delay;\n        loop (i - 1)\n  in\n  loop 3\n```\n\nExercise: create a `Callback` submodule in `echo.ml` and implement the client-side `Callback.log` function (hint: it's very similar to `ping`, but use `Capability.call_for_unit` because we don't care about the value of the result and we want to handle errors manually). If you get stuck, the solution can be found in the `examples/v2` directory.\n\nTo write the client for `Echo.heartbeat`, we take a user-provided callback object\nand put it into the request:\n\n\u003c!-- $MDX include,file=examples/v2/echo.ml,part=client-heartbeat --\u003e\n```ocaml\nlet heartbeat t msg callback =\n  let open Echo.Heartbeat in\n  let request, params = Capability.Request.create Params.init_pointer in\n  Params.msg_set params msg;\n  Params.callback_set params (Some callback);\n  Capability.call_for_unit_exn t method_id request\n```\n\n`Capability.call_for_unit_exn` is a convenience wrapper around\n`Capability.call_for_value_exn` that discards the result.\n\nIn `main.ml`, we can now wrap a regular OCaml function as the callback:\n\n\u003c!-- $MDX include,file=examples/v2/main.ml --\u003e\n```ocaml\nopen Eio.Std\nopen Capnp_rpc.Std\n\nlet delay = if Sys.getenv_opt \"CI\" = None then 1.0 else 0.0\n\nlet () =\n  Logs.set_level (Some Logs.Warning);\n  Logs.set_reporter (Logs_fmt.reporter ())\n\nlet callback_fn msg =\n  traceln \"Callback got %S\" msg\n\nlet run_client service =\n  Capability.with_ref (Echo.Callback.local callback_fn) @@ fun callback -\u003e\n  Echo.heartbeat service \"foo\" callback\n\nlet () =\n  Eio_main.run @@ fun env -\u003e\n  let delay = Eio.Time.Timeout.seconds env#mono_clock delay in\n  let service = Echo.local ~delay in\n  run_client service\n```\n\nStep 1: The client creates the callback:\n\n\u003cp align='center'\u003e\n  \u003cimg src=\"./diagrams/callback1.svg\"/\u003e\n\u003c/p\u003e\n\nStep 2: The client calls the `heartbeat` method, passing the callback as an argument:\n\n\u003cp align='center'\u003e\n  \u003cimg src=\"./diagrams/callback2.svg\"/\u003e\n\u003c/p\u003e\n\nStep 3: The service receives the callback and calls the `log` method on it:\n\n\u003cp align='center'\u003e\n  \u003cimg src=\"./diagrams/callback3.svg\"/\u003e\n\u003c/p\u003e\n\n\nExercise: implement `Callback.local fn` (hint: it's similar to the original `ping` service, but pass the message to `fn` and return with `Service.return_empty ()`)\n\nAnd testing it should give (three times, at one second intervals):\n\n\u003c!-- $MDX dir=examples/v2,set-CI=true --\u003e\n```sh\n$ dune exec -- ./main.exe\n+Callback got \"foo\"\n+Callback got \"foo\"\n+Callback got \"foo\"\n```\n\nNote that the client gives the echo service permission to call its callback service by sending a message containing the callback to the service.\nNo other access control updates are needed.\n\nNote also a design choice here in the API: we could have made the `Echo.heartbeat` function take an OCaml callback and wrap it, but instead we chose to take a service and make `main.ml` do the wrapping.\nThe advantage to doing it this way is that `main.ml` may one day want to pass a remote callback, as we'll see later.\n\nThis still isn't very exciting, because we just stored an OCaml object pointer in a message and then pulled it out again.\nHowever, we can use the same code with the echo client and service in separate processes, communicating over the network...\n\n### Networking\n\nLet's put a network connection between the client and the server.\nHere's the new `main.ml` (the top half is the same as before):\n\n\u003c!-- $MDX include,file=examples/v3/main.ml --\u003e\n```ocaml\nopen Eio.Std\nopen Capnp_rpc.Std\n\nlet delay = if Sys.getenv_opt \"CI\" = None then 1.0 else 0.0\n\nlet () =\n  Logs.set_level (Some Logs.Warning);\n  Logs.set_reporter (Logs_fmt.reporter ())\n\nlet callback_fn msg =\n  traceln \"Callback got %S\" msg\n\nlet run_client service =\n  Capability.with_ref (Echo.Callback.local callback_fn) @@ fun callback -\u003e\n  Echo.heartbeat service \"foo\" callback\n\nlet secret_key = `Ephemeral\nlet listen_address = `TCP (\"127.0.0.1\", 7000)\n\nlet start_server ~sw ~delay net =\n  let config = Capnp_rpc_unix.Vat_config.create ~secret_key ~net listen_address in\n  let service_id = Capnp_rpc_unix.Vat_config.derived_id config \"main\" in\n  let restore = Capnp_rpc_net.Restorer.single service_id (Echo.local ~delay) in\n  let vat = Capnp_rpc_unix.serve ~sw ~restore config in\n  Capnp_rpc_unix.Vat.sturdy_uri vat service_id\n\nlet () =\n  Eio_main.run @@ fun env -\u003e\n  Switch.run @@ fun sw -\u003e\n  let delay = Eio.Time.Timeout.seconds env#mono_clock delay in\n  let uri = start_server ~sw ~delay env#net in\n  traceln \"Connecting to echo service at: %a\" Uri.pp_hum uri;\n  let client_vat = Capnp_rpc_unix.client_only_vat ~sw env#net in\n  let sr = Capnp_rpc_unix.Vat.import_exn client_vat uri in\n  Sturdy_ref.with_cap_exn sr run_client\n```\n\n\u003cp align='center'\u003e\n  \u003cimg src=\"./diagrams/vats.svg\"/\u003e\n\u003c/p\u003e\n\nYou'll need to edit your `dune` file to add a dependency on `capnp-rpc-unix` in the `(libraries ...)` line and also:\n\n\u003c!-- $MDX skip --\u003e\n```sh\n$ opam install capnp-rpc-unix\n```\n\nRunning this will give something like:\n\n\u003c!-- $MDX dir=examples/v3,non-deterministic,set-CI=true --\u003e\n```sh\n$ dune exec ./main.exe\nConnecting to echo service at: capnp://sha-256:3Tj5y5Q2qpqN3Sbh0GRPxgORZw98_NtrU2nLI0-Tn6g@127.0.0.1:7000/eBIndzZyoVDxaJdZ8uh_xBx5V1lfXWTJCDX-qEkgNZ4\nCallback got \"foo\"\nCallback got \"foo\"\nCallback got \"foo\"\n```\n\nOnce the server vat is running, we get a \"sturdy ref\" for the echo service, which is displayed as a \"capnp://\" URL.\nThe URL contains several pieces of information:\n\n- The `sha-256:3Tj5y5Q2qpqN3Sbh0GRPxgORZw98_NtrU2nLI0-Tn6g` part is the fingerprint of the server's public key.\n  When the client connects, it uses this to verify that it is connected to the right server (not an imposter).\n  Therefore, a Cap'n Proto vat does not need to be certified by a CA (and cannot be compromised by a rogue CA).\n\n- `127.0.0.1:7000` is the address to which clients will try to connect to reach the server vat.\n\n- `eBIndzZyoVDxaJdZ8uh_xBx5V1lfXWTJCDX-qEkgNZ4` is the (base64-encoded) service ID.\n  This is a secret that both identifies the service to use within the vat, and also grants access to it.\n\n#### The server side\n\nThe ``let secret_key = `Ephemeral`` line causes a new server key to be generated each time the program runs,\nso if you run it again you'll see a different capnp URL.\nFor a real system you'll want to save the key so that the server's identity doesn't change when it is restarted.\nYou can use ``let secret_key = `File (cwd / \"secret-key.pem\")`` for that.\nThen the file `secret-key.pem` will be created automatically the first time you start the service,\nand reused on future runs.\n\nIt is also possible to disable the use of encryption using `Vat_config.create ~serve_tls:false ...`.\nThat might be useful if you need to interoperate with a client that doesn't support TLS.\n\n`listen_address` tells the server where to listen for incoming connections.\nYou can use `` `Unix path`` for a Unix-domain socket at `path`, or\n`` `TCP (host, port)`` to accept connections over TCP.\n\nFor TCP, you might want to listen on one address but advertise a different one, e.g.\n\n\u003c!-- $MDX skip --\u003e\n```ocaml\nlet listen_address = `TCP (\"0.0.0.0\", 7000)\t(* Listen on all interfaces *)\nlet public_address = `TCP (\"192.168.1.3\", 7000)\t(* Tell clients to connect here *)\n\nlet start_server () =\n  let config = Capnp_rpc_unix.Vat_config.create ~secret_key ~net ~public_address listen_address in\n```\n\nIn `start_server`:\n\n- `let service_id = Capnp_rpc_unix.Vat_config.derived_id config \"main\"` creates the secret ID that\n  grants access to the service. `derived_id` generates the ID deterministically from the secret key\n  and the name. This means that the ID will be stable as long as the server's key doesn't change.\n  The name used (\"main\" here) isn't important - it just needs to be unique.\n\n- `let restore = Capnp_rpc_net.Restorer.single service_id (Echo.local ~delay)`\n  configures a simple \"restorer\" that answers requests for `service_id` with\n  our `Echo.local` service.\n\n- `Capnp_rpc_unix.serve ~sw ~net ~restore config` creates the service vat using the\n  previous configuration items and starts it listening for incoming connections.\n\n- `Capnp_rpc_unix.Vat.sturdy_uri vat service_id` returns a \"capnp://\" URI for\n  the given service within the vat.\n\n#### The client side\n\nAfter starting the server and getting the sturdy URI, we create a client vat and connect to the sturdy ref.\nThe result is a proxy to the remote service via the network that can be used in\nexactly the same way as the direct reference we used before.\n\n#### Separate processes\n\nThe example above runs the client and server in a single process, which is convenient for testing.\nTo run them in separate processes we just need to split `main.ml` into separate files\nand add some command-line parsing to let the user pass the URL.\n\nEdit the `dune` file to build a client and server:\n\n\u003c!-- $MDX include,file=examples/v4/dune --\u003e\n```\n(executables\n (names client server)\n (libraries eio_main capnp-rpc logs.fmt capnp-rpc-unix))\n\n(rule\n (targets echo_api.ml echo_api.mli)\n (deps    echo_api.capnp)\n (action (run capnp compile -o %{bin:capnpc-ocaml} %{deps})))\n```\n\nHere's a suitable `server.ml`:\n\n\u003c!-- $MDX include,file=examples/v4/server.ml --\u003e\n```ocaml\nopen Eio.Std\nopen Capnp_rpc_net\n\nlet delay = if Sys.getenv_opt \"CI\" = None then 1.0 else 0.0\n\nlet () =\n  Logs.set_level (Some Logs.Warning);\n  Logs.set_reporter (Logs_fmt.reporter ())\n\nlet cap_file = \"echo.cap\"\n\nlet serve ~delay config =\n  Switch.run @@ fun sw -\u003e\n  let service_id = Capnp_rpc_unix.Vat_config.derived_id config \"main\" in\n  let restore = Restorer.single service_id (Echo.local ~delay) in\n  let vat = Capnp_rpc_unix.serve ~sw ~restore config in\n  match Capnp_rpc_unix.Cap_file.save_service vat service_id cap_file with\n  | Error `Msg m -\u003e failwith m\n  | Ok () -\u003e\n    traceln \"Server running. Connect using %S.\" cap_file;\n    Fiber.await_cancel ()\n\nopen Cmdliner\n\nlet ( $$ ) f x = Term.(const f $ x)\n\nlet serve_cmd env =\n  let doc = \"run the server\" in\n  let info = Cmd.info \"serve\" ~doc in\n  let delay = Eio.Time.Timeout.seconds env#mono_clock delay in\n  Cmd.v info (serve ~delay $$ Capnp_rpc_unix.Vat_config.cmd env)\n\nlet () =\n  exit @@ Eio_main.run @@ fun env -\u003e\n  Cmd.eval (serve_cmd env)\n```\n\nThe cmdliner term `Capnp_rpc_unix.Vat_config.cmd` provides an easy way to get a suitable `Vat_config`\nbased on command-line arguments provided by the user.\n\nAnd here's the corresponding `client.ml`:\n\n\u003c!-- $MDX include,file=examples/v4/client.ml --\u003e\n```ocaml\nopen Eio.Std\nopen Capnp_rpc.Std\n\nlet () =\n  Logs.set_level (Some Logs.Warning);\n  Logs.set_reporter (Logs_fmt.reporter ())\n\nlet callback_fn msg =\n  traceln \"Callback got %S\" msg\n\nlet run_client service =\n  Capability.with_ref (Echo.Callback.local callback_fn) @@ fun callback -\u003e\n  Echo.heartbeat service \"foo\" callback\n\nlet connect net uri =\n  Switch.run @@ fun sw -\u003e\n  let client_vat = Capnp_rpc_unix.client_only_vat ~sw net in\n  let sr = Capnp_rpc_unix.Vat.import_exn client_vat uri in\n  Capnp_rpc_unix.with_cap_exn sr run_client\n\nopen Cmdliner\n\nlet ( $$ ) f x = Term.(const f $ x)\n\nlet connect_addr =\n  let i = Arg.info [] ~docv:\"ADDR\" ~doc:\"Address of server (capnp://...)\" in\n  Arg.(required @@ pos 0 (some Capnp_rpc_unix.sturdy_uri) None i)\n\nlet connect_cmd env =\n  let doc = \"run the client\" in\n  let info = Cmd.info \"connect\" ~doc in\n  Cmd.v info (connect env#net $$ connect_addr)\n\nlet () =\n  exit @@ Eio_main.run @@ fun env -\u003e\n  Cmd.eval (connect_cmd env)\n```\n\nTo test, start the server running:\n\n\u003c!-- $MDX skip --\u003e\n```sh\n$ dune exec -- ./server.exe \\\n    --capnp-secret-key-file key.pem \\\n    --capnp-listen-address tcp:localhost:7000\nServer running. Connect using \"echo.cap\".\n```\n\nWith the server still running in another window, run the client using the `echo.cap` file generated by the server:\n\n\u003c!-- $MDX skip --\u003e\n```sh\n$ dune exec ./client.exe echo.cap\nCallback got \"foo\"\nCallback got \"foo\"\nCallback got \"foo\"\n```\n\nNote that we're using `Capnp_rpc_unix.with_cap_exn` here instead of `Sturdy_ref.with_cap_exn`.\nIt's almost the same, except that it displays a suitable progress indicator if the connection takes too long.\n\nCongratulations on finishing the main tutorial! You now know how to:\n\n- Define Cap'n Proto services and clients, independently of any networking.\n- Pass capability references in method arguments and results.\n- Stretch capabilities over a network link, with encryption, authentication and access control.\n- Configure a vat using command-line arguments.\n\n## Pipelining\n\nImagine we have a server with the following API:\n\n\u003c!-- $MDX include,file=examples/pipelining/echo_api.capnp --\u003e\n```capnp\n@0xb287252b6cbed46e;\n\ninterface Callback {\n  log @0 (msg :Text) -\u003e ();\n}\n\ninterface Echo {\n  ping      @0 (msg :Text) -\u003e (reply :Text);\n  heartbeat @1 (msg :Text, callback :Callback) -\u003e ();\n  getLogger @2 () -\u003e (callback :Callback);\n}\n```\n\nIt's the same API we saw in the tutorial above, but extended with a logging service,\nwhich the client can get from the main echo service by calling `getLogger`.\n\nThe implementation of the new method in the service is simple -\nwe export the callback in the response in the same way we previously exported the client's callback in the request:\n\n\u003c!-- $MDX include,file=examples/pipelining/echo.ml,part=server-get-logger --\u003e\n```ocaml\n    method get_logger_impl _ release_params =\n      let open Echo.GetLogger in\n      release_params ();\n      let response, results = Service.Response.create Results.init_pointer in\n      Results.callback_set results (Some service_logger);\n      Service.return response\n```\n\nExercise: create a `service_logger` that prints out whatever it gets (hint: use `Callback.local`)\nSee [examples/pipelining](./examples/pipelining/) for the solution.\n\nThe client side is more interesting:\n\n\u003c!-- $MDX include,file=examples/pipelining/echo.ml,part=client-get-logger --\u003e\n```ocaml\nlet get_logger t =\n  let open Echo.GetLogger in\n  let request = Capability.Request.create_no_args () in\n  Capability.call_for_caps t method_id request Results.callback_get_pipelined\n```\n\nWe could have used `call_and_wait` here\n(which is similar to `call_for_value` but doesn't automatically discard any capabilities in the result).\nHowever, that would mean waiting for the response to be sent back to us over the network before we could use it.\nInstead, we use `callback_get_pipelined` to get a promise for the capability from the promise of the `getLogger` call's result.\n\nNote: the last argument to `call_for_caps` is a function for extracting the capabilities from the promised result.\nIn the common case where you just want one and it's in the root result struct, you can just pass the accessor directly,\nas shown.\nDoing it this way allows `call_for_caps` to release any unused capabilities in the result automatically for us.\n\nWe can test it as follows:\n\n\u003c!-- $MDX include,file=examples/pipelining/main.ml,part=run-client --\u003e\n```ocaml\nlet run_client service =\n  let logger = Echo.get_logger service in\n  match Echo.Callback.log logger \"Message from client\" with\n  | Ok () -\u003e ()\n  | Error (`Capnp err) -\u003e\n    Fmt.epr \"Server's logger failed: %a\" Capnp_rpc.Error.pp err\n```\n\n\u003cp align='center'\u003e\n  \u003cimg src=\"./diagrams/pipeline.svg\"/\u003e\n\u003c/p\u003e\n\nThis should print (in the server's output) something like:\n\n\u003c!-- $MDX dir=examples/pipelining --\u003e\n```sh\n$ dune exec ./main.exe\n+[client] Connecting to echo service...\n+[server] Received \"Message from client\"\n```\n\nIn this case, we didn't wait for the `getLogger` call to return before using the logger.\nThe RPC library pipelined the `log` call directly to the promised logger from its previous question.\nOn the wire, the messages are sent together, and look like:\n\n1. What is your logger?\n2. Please call the object returned in answer to my previous question (1).\n\nNow, let's say we'd like the server to send heartbeats to itself:\n\n\u003c!-- $MDX skip --\u003e\n```ocaml\nlet run_client service =\n  Capability.with_ref (Echo.get_logger service) @@ fun callback -\u003e\n  Echo.heartbeat service \"foo\" callback\n```\n\nHere, we ask the server for its logger and then (without waiting for the reply), tell it to send heartbeat messages to the promised logger (you should see the messages appear in the server's output).\n\nPreviously, when we exported our local `callback` object, it arrived at the service as a proxy that sent messages back to the client over the network.\nBut when we send the (promise of the) server's own logger back to it, the RPC system detects this and \"shortens\" the path;\nthe capability reference that the `heartbeat` handler gets is a direct reference to its own logger, which\nit can call without using the network.\n\nThese optimisations are very important because they allow us to build APIs like this with small functions that can be composed easily.\nWithout pipelining, we would be tempted to clutter the protocol with specialised methods like `heartbeatToYourself` to avoid the extra round-trips most RPC protocols would otherwise require.\n\n## Guide to sturdy refs\n\nA `Capability.t` is a \"live\" reference to a service, and is typically tied to a TCP connection.\nIf the TCP connection fails, or the client or server is restarted, the capability becomes broken and can no longer be used.\nBy contrast, a \"sturdy ref\" is an offline reference to a service, which can be saved as a URL in a file, emailed to someone, etc.\nA sturdy ref can be used to get a live ref when needed.\n\n### Hosting multiple sturdy refs\n\nThe `Restorer.single` restorer used in the tutorial above is useful for vats hosting a single sturdy ref.\nHowever, you may want to host multiple sturdy refs,\nperhaps to provide separate \"admin\" and \"user\" capabilities to different clients,\nor to allow services to be created and persisted as sturdy refs dynamically.\nTo do this, we can use `Restorer.Table`.\n\n[examples/sturdy-refs](./examples/sturdy-refs) is an example of this.\nIt defines a simple API for a logging service:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs/api.capnp --\u003e\n```capnp\n@0x9ab198a53301be6e;\n\ninterface Logger {\n  log @0 (msg :Text) -\u003e ();\n}\n```\n\nHere is an example using it.\nThe server writes out two cap files, for two different logger instances.\nTwo different clients load these two files and send messages to the server:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs/main.ml,part=main --\u003e\n```ocaml\nlet make_service ~config ~services name =\n  let service = Logger.local name in\n  let id = Capnp_rpc_unix.Vat_config.derived_id config name in\n  Restorer.Table.add services id service;\n  name, id\n\nlet start_server ~sw net =\n  let config = Capnp_rpc_unix.Vat_config.create ~secret_key ~net listen_address in\n  let make_sturdy = Capnp_rpc_unix.Vat_config.sturdy_uri config in\n  let services = Restorer.Table.create ~sw make_sturdy in\n  let restore = Restorer.of_table services in\n  let services = List.map (make_service ~config ~services) [\"alice\"; \"bob\"] in\n  let vat = Capnp_rpc_unix.serve ~sw ~restore config in\n  services |\u003e List.iter (fun (name, id) -\u003e\n      let cap_file = name ^ \".cap\" in\n      Capnp_rpc_unix.Cap_file.save_service vat id cap_file |\u003e or_fail;\n      Printf.printf \"[server] saved %S\\n%!\" cap_file\n    )\n\nlet run_client ~net cap_file msg =\n  Switch.run @@ fun sw -\u003e\n  let vat = Capnp_rpc_unix.client_only_vat ~sw net in\n  let sr = Capnp_rpc_unix.Cap_file.load vat cap_file |\u003e or_fail in\n  Printf.printf \"[client] loaded %S\\n%!\" cap_file;\n  Sturdy_ref.with_cap_exn sr @@ fun cap -\u003e\n  Logger.log cap msg\n\nlet () =\n  Eio_main.run @@ fun env -\u003e\n  Switch.run @@ fun sw -\u003e\n  let net = env#net in\n  start_server ~sw net;\n  run_client ~net \"./alice.cap\" \"Message from Alice\";\n  run_client ~net \"./bob.cap\" \"Message from Bob\"\n```\n\n\u003c!-- $MDX dir=examples/sturdy-refs --\u003e\n```sh\n$ dune exec ./main.exe\n[server] saved \"alice.cap\"\n[server] saved \"bob.cap\"\n[client] loaded \"./alice.cap\"\n[server] \"alice\" says \"Message from Alice\"\n[client] loaded \"./bob.cap\"\n[server] \"bob\" says \"Message from Bob\"\n```\n\n### Creating services dynamically\n\nSo far, we have been providing a static set of sturdy refs.\nWe can also generate new services, with new sturdy refs, dynamically, and return them to clients.\nLet's allow holders of a logger to create nested sub-loggers.\nThen the admin can create `alice.cap` and `bob.cap` using the API, instead of hard-coding them,\nand Alice and Bob can create further delegate to other users as needed.\n\n\u003c!-- $MDX include,file=examples/sturdy-refs-2/api.capnp --\u003e\n```capnp\n@0x9ab198a53301be6e;\n\ninterface Logger {\n  log @0 (msg :Text) -\u003e ();\n  sub @1 (label :Text) -\u003e (logger :Logger);\n}\n```\n\nTo begin with, we'll just create live refs dynamically.\nWe can use the new API like this:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs-2/main.ml,part=main --\u003e\n```ocaml\nlet () =\n  Eio_main.run @@ fun env -\u003e\n  Switch.run @@ fun sw -\u003e\n  let net = env#net in\n  let root_uri = start_server ~sw net in\n  let vat = Capnp_rpc_unix.client_only_vat ~sw net in\n  let root_sr = Capnp_rpc_unix.Vat.import vat root_uri |\u003e or_fail in\n  Sturdy_ref.with_cap_exn root_sr @@ fun root -\u003e\n  Logger.log root \"Message from Admin\";\n  Capability.with_ref (Logger.sub root \"alice\") @@ fun for_alice -\u003e\n  Capability.with_ref (Logger.sub root \"bob\") @@ fun for_bob -\u003e\n  Logger.log for_alice \"Message from Alice\";\n  Logger.log for_bob \"Message from Bob\"\n```\n\n\u003c!-- $MDX dir=examples/sturdy-refs-2 --\u003e\n```sh\n$ dune exec ./main.exe\n[server] \"root\" says \"Message from Admin\"\n[server] \"root/alice\" says \"Message from Alice\"\n[server] \"root/bob\" says \"Message from Bob\"\n```\n\n### The Persistence API\n\nHowever, we'd like to be able to turn these live capabilities (`for_alice` and `for_bob`)\ninto URLs that we can send to Alice and Bob over some other transport (e.g. ssh or email).\n\nCap'n Proto defines a standard [Persistence API][] which services can implement\nto allow clients to request their sturdy ref.\n\nOn the client side, calling `Persistence.save_exn cap` will send a request to `cap`\nasking for its sturdy ref. For example, after getting a live capability,\nthe admin can request the sturdy ref like this:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs-3/main.ml,part=save --\u003e\n```ocaml\n  (* The admin creates a logger for Alice and saves it: *)\n  let uri = Capability.with_ref (Logger.sub root \"alice\") Capnp_rpc.Persistence.save_exn in\n  Capnp_rpc_unix.Cap_file.save_uri uri \"alice.cap\" |\u003e or_fail;\n  (* Alice uses it: *)\n  run_client ~net \"alice.cap\"\n```\n\nIf successful, the client can use this sturdy ref to connect directly to the logger in future:\n\n\u003c!-- $MDX dir=examples/sturdy-refs-3 --\u003e\n```sh\n$ dune exec ./main.exe\n[server] \"root\" says \"Message from Admin\"\n[server] \"root/alice\" says \"Message from Alice\"\n```\n\nIf you try the above, it will fail with `Unimplemented: Unknown interface 14468694717054801553UL`.\nTo add support on the server side, we must tell each logger instance what its public address is\nand have it implement the persistence interface.\nThe simplest way to do this is to wrap the `Callback.local` call with `Persistence.with_sturdy_ref`:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs-3/logger.ml,part=local --\u003e\n```ocaml\nlet rec local ~services sr label =\n  let module Logger = Api.Service.Logger in\n  Capnp_rpc.Persistence.with_sturdy_ref sr Logger.local @@ object\n```\n\nThen pass the `services` and `sr` arguments when creating the logger:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs-3/main.ml,part=root --\u003e\n```ocaml\n  let root_id = Capnp_rpc_unix.Vat_config.derived_id config \"root\" in\n  let root =\n    let sr = Capnp_rpc_net.Restorer.Table.sturdy_ref services root_id in\n    Logger.local ~services sr \"root\"\n  in\n```\n\n### Persisting sturdy refs\n\nCurrently, each time we run the server we generate a new ID for Alice's logger\n(`alice.cap` will be different each time).\nFor a real service, we will want to persist the IDs somehow.\n\n`Table.add` is not a good choice for dynamic services because\nit requires all capabilities to be loaded into the table at start-up,\nwhich may be a performance problem.\n\nInstead, we can create the table using `Table.of_loader`.\nWhen the user asks for a sturdy ref that is not in the table,\nit calls our `load` function to load the capability dynamically\nfrom storage\n(you can still use `Table.add` to register static services, as before).\n\nA database such as sqlite3 is often a good choice for the dynamic services,\nbut as Cap'n Proto is also a useful on-disk format, we'll just use that in this guide.\n\nHere's the interface for a `Db` module that loads and saves loggers:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs-4/db.mli --\u003e\n```ocaml\nopen Capnp_rpc.Std\nopen Capnp_rpc_net\n\ninclude Restorer.LOADER\n\ntype loader = [`Logger_beacebd78653e9af] Sturdy_ref.t -\u003e label:string -\u003e Restorer.resolution\n(** A function to create a new in-memory logger with the given label and sturdy-ref. *)\n\nval create : make_sturdy:(Restorer.Id.t -\u003e Uri.t) -\u003e _ Eio.Path.t -\u003e t * loader Eio.Promise.u\n(** [create ~make_sturdy dir] is a database that persists services in [dir] and\n    a resolver to let you set the loader (we're not ready to set the loader\n    when we create the database). *)\n\nval save_new : t -\u003e label:string -\u003e Restorer.Id.t\n(** [save_new t ~label] adds a new logger with label [label] to the store and\n    returns its newly-generated ID. *)\n```\n\nThere is a `Capnp_rpc_unix.File_store` module that can persist Cap'n Proto structs to disk.\nFirst, define a suitable Cap'n Proto data structure to hold the information we need to store.\nIn this case, it's just the label:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs-4/store.capnp --\u003e\n```capnp\n@0x97ed384f584feb4a;\n\nstruct SavedLogger {\n  label @0 :Text;\n}\n\nstruct SavedService {\n  logger @0 :SavedLogger;\n}\n```\n\nUsing Cap'n Proto for this makes it easy to add extra fields or service types later if needed\n(`SavedService.logger` can be upgraded to a union if we decide to add more service types later).\nWe can use this with `File_store` to implement `Db`:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs-4/db.ml --\u003e\n```ocaml\nopen Eio.Std\nopen Capnp_rpc.Std\nopen Capnp_rpc_net\n\nmodule File_store = Capnp_rpc_unix.File_store\nmodule Store = Store.Make(Capnp.BytesMessage)\n\ntype loader = [`Logger_beacebd78653e9af] Sturdy_ref.t -\u003e label:string -\u003e Restorer.resolution\n\ntype t = {\n  store : Store.Reader.SavedService.struct_t File_store.t;\n  loader : loader Promise.t;\n  make_sturdy : Restorer.Id.t -\u003e Uri.t;\n}\n\nlet hash _ = `SHA256\n\nlet make_sturdy t = t.make_sturdy\n\nlet save t ~digest label =\n  let open Store.Builder in\n  let service = SavedService.init_root () in\n  let logger = SavedService.logger_init service in\n  SavedLogger.label_set logger label;\n  File_store.save t.store ~digest @@ SavedService.to_reader service\n\nlet save_new t ~label =\n  let id = Restorer.Id.generate () in\n  let digest = Restorer.Id.digest (hash t) id in\n  save t ~digest label;\n  id\n\nlet load t sr digest =\n  match File_store.load t.store ~digest with\n  | None -\u003e Restorer.unknown_service_id\n  | Some saved_service -\u003e\n    let logger = Store.Reader.SavedService.logger_get saved_service in\n    let label = Store.Reader.SavedLogger.label_get logger in\n    let sr = Capnp_rpc.Sturdy_ref.cast sr in\n    let loader = Promise.await t.loader in\n    loader sr ~label\n\nlet create ~make_sturdy dir =\n  let loader, set_loader = Promise.create () in\n  if not (Eio.Path.is_directory dir) then\n    Eio.Path.mkdir dir ~perm:0o755;\n  let store = File_store.create dir in\n  {store; loader; make_sturdy}, set_loader\n```\n\nNote: to avoid possible timing attacks, the `load` function is called with the digest of the service ID rather than with the ID itself. This means that even if the load function takes a different amount of time to respond depending on how much of a valid ID the client guessed, the client will only learn the digest (which is of no use to them), not the ID.\nThe file store uses the digest as the filename, which avoids needing to check the ID the client gives for special characters, and also means that someone getting a copy of the store (e.g. an old backup) doesn't get the IDs (which would allow them to access the real service).\n\nThe main `start_server` function then uses `Db` to create the table:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs-4/main.ml,part=server --\u003e\n```ocaml\nlet serve store_dir config =\n  Switch.run @@ fun sw -\u003e\n  (* Create the on-disk store *)\n  let make_sturdy = Capnp_rpc_unix.Vat_config.sturdy_uri config in\n  let db, set_loader = Db.create ~make_sturdy store_dir in\n  (* Create the restorer *)\n  let services = Restorer.Table.of_loader ~sw (module Db) db in\n  Switch.on_release sw (fun () -\u003e Restorer.Table.clear services);\n  let restore = Restorer.of_table services in\n  (* Add the root service *)\n  let persist_new ~label =\n    let id = Db.save_new db ~label in\n    Capnp_rpc_net.Restorer.restore restore id\n  in\n  let root_id = Capnp_rpc_unix.Vat_config.derived_id config \"root\" in\n  let root =\n    let sr = Capnp_rpc_net.Restorer.Table.sturdy_ref services root_id in\n    Logger.local ~persist_new sr \"root\"\n  in\n  Restorer.Table.add services root_id root;\n  (* Tell the database how to restore saved loggers *)\n  Promise.resolve set_loader (fun sr ~label -\u003e Restorer.grant @@ Logger.local ~persist_new sr label);\n  (* Run the server *)\n  let _vat = Capnp_rpc_unix.serve ~sw ~restore config in\n  let uri = Capnp_rpc_unix.Vat_config.sturdy_uri config root_id in\n  Capnp_rpc_unix.Cap_file.save_uri uri \"admin.cap\" |\u003e or_fail;\n  print_endline \"Wrote admin.cap\";\n  Fiber.await_cancel ()\n```\n\nThe server implementation of the `sub` method gets the label from the parameters\nand uses `persist_new` to save the new logger to the database:\n\n\u003c!-- $MDX include,file=examples/sturdy-refs-4/logger.ml,part=sub-impl --\u003e\n```ocaml\n    method sub_impl params release_param_caps =\n      let open Logger.Sub in\n      let sub_label = Params.label_get params in\n      release_param_caps ();\n      let label = Printf.sprintf \"%s/%s\" label sub_label in\n      match persist_new ~label with\n      | Error e -\u003e Service.error (`Exception e)\n      | Ok logger -\u003e\n        let response, results = Service.Response.create Results.init_pointer in\n        Results.logger_set results (Some logger);\n        Capability.dec_ref logger;\n        Service.return response\n```\n\n\u003c!-- $MDX dir=examples/sturdy-refs-4,non-deterministic --\u003e\n```sh\n$ dune exec -- ./main.exe serve --capnp-secret-key=server.key --capnp-listen-address unix:/tmp/demo.sock \u0026\nWrote admin.cap\n\n$ dune exec -- ./main.exe log admin.cap \"Hello from Admin\"\n[server] \"root\" says \"Hello from Admin\"\n\n$ dune exec -- ./main.exe sub admin.cap alice\nWrote \"alice.cap\"\n\n$ dune exec -- ./main.exe log alice.cap \"Hello from Alice\"\n[server] \"root/alice\" says \"Hello from Alice\"\n\n$ dune exec -- ./main.exe sub alice.cap bob\nWrote \"bob.cap\"\n\n$ dune exec ./main.exe log bob.cap \"Hello from Bob\"\n[server] \"root/alice/bob\" says \"Hello from Bob\"\n```\n\nYou should find that the loggers now persist even if the service is restarted.\n\n\n## Further reading\n\n* [`capnp_rpc.mli`](capnp-rpc/capnp_rpc.mli) and [`s.ml`](capnp-rpc/s.ml) describe the OCaml API.\n* [Cap'n Proto schema file format][schema] shows how to build more complex structures, and the \"Evolving Your Protocol\" section explains how to change the schema without breaking backwards compatibility.\n* \u003chttps://discuss.ocaml.org/\u003e is a good place to ask questions (tag them as \"capnp\").\n* [The capnp-ocaml site][capnp-ocaml] explains how to read and build more complex types using the OCaml interface.\n* [E Reference Mechanics][] gives some insight into how distributed promises work.\n\n## FAQ\n\n### Why does my connection stop working after 10 minutes?\n\nCap'n Proto connections are often idle for long periods of time, and some networks automatically close idle connections.\nTo avoid this, capnp-rpc-unix sets the `SO_KEEPALIVE` option when connecting to another vat,\nso that the initiator of the connection will send a TCP keep-alive message at regular intervals.\nHowever, TCP keep-alives are sent after the connection has been idle for 2 hours by default,\nand this isn't frequent enough for e.g. Docker's libnetwork,\nwhich silently breaks idle TCP connections after about 10 minutes.\n\nA typical sequence looks like this:\n\n1. A client connects to a server and configures a notification callback.\n2. The connection is idle for 10 minutes. libnetwork removes the connection from its routing table.\n3. Later, the server tries to send the notification and discovers that the connection has failed.\n4. After 2 hours, the client sends a keep-alive message and it too discovers that the connection has failed.\n   It establishes a new connection and retries.\n\nOn some platforms, capnp-rpc-unix (\u003e= 0.9.0) is able to reduce the timeout to 1 minute by setting the `TCP_KEEPIDLE` socket option.\nOn other platforms, you may have to configure this setting globally (e.g. with `sudo sysctl net.ipv4.tcp_keepalive_time=60`).\n\n### How can I return multiple results?\n\nEvery Cap'n Proto method returns a struct, although the examples in this README only use a single field.\nYou can return multiple fields by defining a method as e.g. `-\u003e (foo :Foo, bar :Bar)`.\nFor more complex types, it may be more convenient to define the structure elsewhere and then refer to it as\n`-\u003e MyResults`.\n\n### Can I create multiple instances of an interface dynamically?\n\nYes. e.g. in the example above we can use `Callback.local fn` many times to create multiple loggers.\nJust remember to call `Capability.dec_ref` on them when you're finished so that they can be released\npromptly (but if the TCP connection is closed, all references on it will be freed anyway).\nUsing `Capability.with_ref` makes it easier to ensure that `dec_ref` gets called in all cases.\n\n### Can I get debug output?\n\nFirst, always make sure logging is enabled so you can at least see warnings.\nThe `main.ml` examples in this document enable some basic logging.\n\nIf you turn up the log level to `Debug`, you'll see lots of information about what is going on.\nTurning on colour in the logs will help too - see `test-bin/calc.ml` for an example.\n\nMany references will be displayed with their reference count (e.g. as `rc=3`).\nYou can also print a capability for debugging with `Capability.pp`.\n\n`CapTP.dump` will dump out the state of an entire connection,\nwhich will show you what services you’re currently importing and exporting over the connection.\n\nIf you override your service’s `pp` method, you can include extra information in the output too.\nUse `Capnp_rpc.Debug.OID` to generate and display a unique object identifier for logging.\n\n### How can I debug reference counting problems?\n\nIf a capability gets GC'd with a non-zero ref-count, you should get a warning.\nFor testing, you can use `Gc.full_major` to force a check.\n\nIf you try to use something after releasing it, you'll get an error.\n\nBut the simple rule is: any time you create a local capability or extract a capability from a message,\nyou must eventually call `Capability.dec_ref` on it.\n\n### How can I import a sturdy ref that I need to start my vat?\n\nLet's say you have a capnp service that internally requires the use of another capnp service:\n\n\u003cp align='center'\u003e\n  \u003cimg src=\"./diagrams/three_vats.svg\"/\u003e\n\u003c/p\u003e\n\nHere, creating the `Frontend` service requires a sturdy ref for the `Backend` service.\nBut this sturdy ref must be imported into the frontend vat.\nCreating the frontend vat requires passing a restorer, which needs `Frontend`!\n\nThe solution here is to construct `Frontend` with a *promise* for the sturdy ref, e.g.\n\n\u003c!-- $MDX skip --\u003e\n```ocaml\nlet run_frontend ~sw ~net backend_uri =\n  let backend_promise, resolver = Promise.create () in\n  let frontend = Frontend.make backend_promise in\n  let restore = Restorer.single id frontend in\n  let vat = Capnp_rpc_unix.serve ~sw ~net ~restore config in\n  Promise.resolve resolver (Capnp_rpc_unix.Vat.import_exn vat backend_uri)\n```\n\n### How can I release other resources when my service is released?\n\nOverride the `release` method. It gets called when there are no more references to your service.\n\n### Is there an interactive version I can use for debugging?\n\n[The Python bindings][pycapnp] provide a good interactive environment.\nFor example, start the test service above and leave it running:\n\n\u003c!-- $MDX skip --\u003e\n```\n$ ./_build/default/main.exe\nConnecting to server at capnp://insecure@127.0.0.1:7000\n[...]\n```\n\nNote that you must run without encryption for this, and use a non-secret ID:\n\n\u003c!-- $MDX skip --\u003e\n```ocaml\nlet config = Capnp_rpc_unix.Vat_config.create ~serve_tls:false ~secret_key listen_address in\nlet service_id = Restorer.Id.public \"\" in\n```\n\nRun `python` from the directory containing your `echo_api.capnp` file and do:\n\n```python\nimport capnp\nimport echo_api_capnp\nclient = capnp.TwoPartyClient('127.0.0.1:7000')\necho = client.bootstrap().cast_as(echo_api_capnp.Echo)\n```\n\nImporting a module named `foo_capnp` will load the Cap'n Proto schema file `foo.capnp`.\n\nTo call the `ping` method:\n\n```python\necho.ping(\"From Python\").wait()\n```\n    \u003cecho_api_capnp:Echo.ping$Results reader (reply = \"echo:From Python\")\u003e\n\nTo call the heartbeat method, with results going to the server's own logger:\n\n```python\necho.heartbeat(\"From Python\", echo.getLogger().callback).wait()\n```\n    Service logger: \"From Python\"\n\nTo call the heartbeat method, with results going to a Python callback:\n\n```python\nclass CallbackImpl(echo_api_capnp.Callback.Server):\n    def log(self, msg, _context): print(\"Python callback got %s\" % msg)\n\necho.heartbeat(\"From Python\", CallbackImpl())\ncapnp.wait_forever()\n```\n    Python callback got From Python\n    Python callback got From Python\n    Python callback got From Python\n\nNote that calling `wait_forever` prevents further use of the session, however.\n\n### Can I set up a direct 2-party connection over a pre-existing channel?\n\nThe normal way to connect to a remote service is using a sturdy ref, as described above.\nThis uses the [NETWORK][] to open a new connection to the server, or reuses an existing connection\nif there is one. However, it is sometimes useful to use a pre-existing connection directly.\n\nFor example, a process may want to spawn a child process and communicate with it\nover a socketpair. The [calc_direct.ml][] example shows how to do this:\n\n```\n$ dune exec -- ./test-bin/calc_direct.exe\nparent: application: Connecting to child process...\nparent: application: Sending request...\n child: application: Serving requests...\n child: application: 21.000000 op 2.000000 -\u003e 42.000000\nparent: application: Result: 42.000000\nparent: application: Shutting down...\nparent:   capnp-rpc: Connection closed\nparent: application: Waiting for child to exit...\nparent: application: Done\n```\n\n## Contributing\n\n### Conceptual model\n\nAn RPC system contains multiple communicating actors (just ordinary OCaml objects).\nAn actor can hold *capabilities* to other objects.\nA capability here is just a regular OCaml object pointer.\n\nEssentially, each object provides a `call` method, which takes:\n\n- some pure-data message content (typically an array of bytes created by the Cap'n Proto serialisation), and\n- an array of pointers to other objects (providing the same API).\n\nThe data part of the message says which method to invoke and provides the arguments.\nWhenever an argument needs to refer to another object, it gives the index of a pointer in the pointers array.\n\nFor example, a call to a method that transfers data between two stores might look something like this:\n\n```yaml\n- Content:\n  - InterfaceID: xxx\n  - MethodID: yyy\n  - Params:\n    - Source: 0\n    - Target: 1\n- Pointers:\n  - \u003csource\u003e\n  - \u003ctarget\u003e\n```\n\nA call also takes a *resolver*, which it will call with the answer when it's ready.\nThe answer will also contain data and pointer parts.\n\nOn top of this basic model the Cap'n Proto schema compiler ([capnp-ocaml]) generates a typed API, so that application code can only generate or attempt to consume messages that match the schema.\nApplication code does not need to worry about interface or method IDs, for example.\n\nThis might seem like a rather clumsy system, but it has the advantage that such messages can be sent not just within a process,\nlike regular OCaml method calls, but also over the network to remote objects.\n\nThe network is made up of communicating \"vats\" of objects.\nYou can think of a Unix process as a single vat.\nThe vats are peers - there is no difference between a \"client\" and a \"server\" at the protocol level.\nHowever, some vats may not be listening for incoming network connections, and you might like to think of such vats as clients.\n\nWhen a connection is established between two vats, each can choose to ask the other for access to some service.\nServices are usually identified by a long random secret (a \"Swiss number\") so that only authorised clients can get access to them.\nThe capability they get back is a proxy object that acts like a local service but forwards all calls over the network.\nWhen a message is sent that contains pointers, the RPC system holds onto the pointers and makes each object available over that network connection.\nEach vat only needs to expose at most a single bootstrap object,\nsince the bootstrap object can provide methods to get access to any other required services.\n\nAll shared objects are scoped to the network connection, and will be released if the connection is closed for any reason.\n\nThe RPC system is smart enough that if you export a local object to a remote service and it later exports the same object back to you, it will switch to sending directly to the local service (once any pipelined messages in flight have been delivered).\n\nYou can also export an object that you received from a third-party, and the receiver will be able to use it.\nIdeally, the receiver should be able to establish a direct connection to the third-party, but\nthis isn't yet implemented and instead the RPC system will forward messages and responses in this case.\n\n\n### Building\n\nTo build:\n\n    git clone https://github.com/mirage/capnp-rpc.git\n    cd capnp-rpc\n    opam pin add -ny .\n    opam install --deps-only -t .\n    make test\n\nIf you have trouble building, you can use the Dockerfile shown in the CI logs (click the green tick on the main page).\n\n### Testing\n\nRunning `make test` will run through the tests in the `test` directory, which run some in-process examples.\n\nThe calculator example can also be run across two Unix processes.\n\nStart the server with:\n\n```\n$ dune exec -- ./test-bin/calc.exe serve \\\n    --capnp-listen-address unix:/tmp/calc.socket \\\n    --capnp-secret-key-file=key.pem\nWaiting for incoming connections at:\ncapnp://sha-256:LPp-7l74zqvGcRgcP8b7-kdSpwwzxlA555lYC8W8prc@/tmp/calc.socket\n```\n\nNote that `key.pem` does not need to exist. A new key will be generated and saved if the file does not yet exist.\n\nIn another terminal, run the client and connect to the address displayed by the server:\n\n```\ndune exec -- ./test-bin/calc.exe connect capnp://sha-256:LPp-7l74zqvGcRgcP8b7-kdSpwwzxlA555lYC8W8prc@/tmp/calc.socket/\n```\n\nYou can also use `--capnp-disable-tls` if you prefer to run without encryption\n(e.g. for interoperability with another Cap'n Proto implementation that doesn't support TLS).\nIn that case, the client URL would be `capnp://insecure@/tmp/calc.socket`.\n\n### Fuzzing\n\nRunning `make fuzz` will run the AFL fuzz tester. You will need to use a version of the OCaml compiler with AFL support\n(e.g. `opam switch create 5.2-afl ocaml-variants.5.2.0+options ocaml-option-afl`).\n\nThe fuzzing code is in the `fuzz` directory.\nThe tests set up some vats in a single process and then have them perform operations based on input from the fuzzer.\nAt each step it selects one vat and performs a random (fuzzer-chosen) operation out of:\n\n1. Request a bootstrap capability from a random peer.\n2. Handle one message on an incoming queue.\n3. Call a random capability, passing randomly-selected capabilities as arguments.\n4. Finish a random question.\n5. Release a random capability.\n6. Add a capability to a new local service.\n7. Answer a random question, passing random-selected capability as the response.\n\nThe content of each call is a (mutable) record with counters for messages sent and received on the capability reference used.\nThis is used to check that messages arrive in the expected order.\n\nThe tests also set up a shadow reference graph, which is like the regular object capability reference graph except that references between vats are just regular OCaml pointers (this is only possible because all the tests run in a single process, of course).\nWhen a message arrives, the tests compare the service that the CapTP network handler selected as the target with the expected target in this simpler shadow network.\nThis should ensure that messages always arrive at the correct target.\n\nIn future, more properties should be tested (e.g. forked references, that messages always eventually arrive when there are no cycles, etc).\nWe should also test with some malicious vats (that don't follow the protocol correctly).\n\n[schema]: https://capnproto.org/language.html\n[capnp-ocaml]: https://github.com/capnproto/capnp-ocaml\n[Cap'n Proto]: https://capnproto.org/\n[Cap'n Proto RPC Protocol]: https://capnproto.org/rpc.html\n[E-Order]: http://erights.org/elib/concurrency/partial-order.html\n[E Reference Mechanics]: http://www.erights.org/elib/concurrency/refmech.html\n[pycapnp]: http://jparyani.github.io/pycapnp/\n[Persistence API]: https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/persistent.capnp\n[ocaml-ci]: https://github.com/ocurrent/ocaml-ci\n[ocluster]: https://github.com/ocurrent/ocluster\n[api]: https://mirage.github.io/capnp-rpc/\n[NETWORK]: https://mirage.github.io/capnp-rpc/capnp-rpc-net/Capnp_rpc_net/S/module-type-NETWORK/index.html\n[calc_direct.ml]: ./test-bin/calc_direct.ml\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmirage%2Fcapnp-rpc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmirage%2Fcapnp-rpc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmirage%2Fcapnp-rpc/lists"}