this repo has no description

docs: add README with usage example and interop instructions

Changed files
+177
+177
README.md
··· 1 + # swim 2 + 3 + An OCaml 5 implementation of the SWIM (Scalable Weakly-consistent Infection-style Process Group Membership) protocol for cluster membership and failure detection. 4 + 5 + ## Overview 6 + 7 + This library provides: 8 + 9 + - **Membership Management**: Automatic discovery and tracking of cluster nodes 10 + - **Failure Detection**: Identifies unreachable nodes using periodic probes and indirect checks 11 + - **Gossip Protocol**: Propagates state changes (Alive/Suspect/Dead) across the cluster 12 + - **Messaging**: Cluster-wide broadcast (gossip-based) and direct point-to-point UDP messaging 13 + - **Encryption**: Optional AES-256-GCM encryption for all network traffic 14 + 15 + Built on [Eio](https://github.com/ocaml-multicore/eio) for effect-based concurrency and [Kcas](https://github.com/ocaml-multicore/kcas) for lock-free shared state. 16 + 17 + ## Requirements 18 + 19 + - OCaml >= 5.1 20 + - Dune >= 3.20 21 + 22 + ## Installation 23 + 24 + ```bash 25 + opam install . 26 + ``` 27 + 28 + Or add to your dune-project: 29 + 30 + ``` 31 + (depends (swim (>= 0.1.0))) 32 + ``` 33 + 34 + ## Usage 35 + 36 + ### Basic Example 37 + 38 + ```ocaml 39 + open Swim.Types 40 + 41 + let config = { 42 + default_config with 43 + bind_port = 7946; 44 + node_name = Some "node-1"; 45 + secret_key = "your-32-byte-secret-key-here!!!"; (* 32 bytes for AES-256 *) 46 + encryption_enabled = true; 47 + } 48 + 49 + let () = 50 + Eio_main.run @@ fun env -> 51 + Eio.Switch.run @@ fun sw -> 52 + let env_wrap = { stdenv = env; sw } in 53 + match Swim.Cluster.create ~sw ~env:env_wrap ~config with 54 + | Error `Invalid_key -> failwith "Invalid secret key" 55 + | Ok cluster -> 56 + Swim.Cluster.start cluster; 57 + 58 + (* Join an existing cluster *) 59 + let seed_nodes = ["192.168.1.10:7946"] in 60 + (match Swim.Cluster.join cluster ~seed_nodes with 61 + | Ok () -> Printf.printf "Joined cluster\n" 62 + | Error `No_seeds_reachable -> Printf.printf "Failed to join\n"); 63 + 64 + (* Send a broadcast message to all nodes *) 65 + Swim.Cluster.broadcast cluster ~topic:"config" ~payload:"v2"; 66 + 67 + (* Send a direct message to a specific node *) 68 + let target = node_id_of_string "node-2" in 69 + Swim.Cluster.send cluster ~target ~topic:"ping" ~payload:"hello"; 70 + 71 + (* Handle incoming messages *) 72 + Swim.Cluster.on_message cluster (fun sender topic payload -> 73 + Printf.printf "From %s: [%s] %s\n" 74 + (node_id_to_string sender.id) topic payload); 75 + 76 + (* Listen for membership events *) 77 + Eio.Fiber.fork ~sw (fun () -> 78 + let stream = Swim.Cluster.events cluster in 79 + while true do 80 + match Eio.Stream.take stream with 81 + | Join node -> Printf.printf "Joined: %s\n" (node_id_to_string node.id) 82 + | Leave node -> Printf.printf "Left: %s\n" (node_id_to_string node.id) 83 + | Suspect_event node -> Printf.printf "Suspect: %s\n" (node_id_to_string node.id) 84 + | Alive_event node -> Printf.printf "Alive: %s\n" (node_id_to_string node.id) 85 + | Update _ -> () 86 + done); 87 + 88 + Eio.Fiber.await_cancel () 89 + ``` 90 + 91 + ### Configuration Options 92 + 93 + | Field | Default | Description | 94 + |-------|---------|-------------| 95 + | `bind_addr` | "0.0.0.0" | Interface to bind listeners | 96 + | `bind_port` | 7946 | Port for SWIM protocol | 97 + | `protocol_interval` | 1.0 | Seconds between probe rounds | 98 + | `probe_timeout` | 0.5 | Seconds to wait for Ack | 99 + | `indirect_checks` | 3 | Peers to ask for indirect probes | 100 + | `secret_key` | (zeros) | 32-byte key for AES-256-GCM | 101 + | `encryption_enabled` | false | Enable encryption | 102 + 103 + ## Interoperability Testing 104 + 105 + The library includes interoperability tests with HashiCorp's [memberlist](https://github.com/hashicorp/memberlist) (Go). This verifies protocol compatibility with the reference implementation. 106 + 107 + ### Prerequisites 108 + 109 + - Go >= 1.19 110 + - OCaml environment with dune 111 + 112 + ### Running Interop Tests 113 + 114 + The interop test suite starts a Go memberlist node and an OCaml node, then verifies they can discover each other and exchange messages. 115 + 116 + ```bash 117 + # Build the OCaml project 118 + dune build 119 + 120 + # Build the Go memberlist server 121 + cd interop && go build -o memberlist-server main.go && cd .. 122 + 123 + # Run the interop test 124 + bash test/scripts/test_interop.sh 125 + 126 + # Run with encryption enabled 127 + bash test/scripts/test_interop_encrypted.sh 128 + ``` 129 + 130 + ### Manual Interop Testing 131 + 132 + Start the Go node: 133 + 134 + ```bash 135 + cd interop 136 + go run main.go -name go-node -bind 127.0.0.1 -port 7946 137 + ``` 138 + 139 + In another terminal, start the OCaml node: 140 + 141 + ```bash 142 + dune exec swim-interop-test 143 + ``` 144 + 145 + The OCaml node will connect to the Go node and print membership statistics for 30 seconds. 146 + 147 + ### Available Test Scripts 148 + 149 + | Script | Description | 150 + |--------|-------------| 151 + | `test/scripts/test_interop.sh` | Basic interop test | 152 + | `test/scripts/test_interop_encrypted.sh` | Interop with AES encryption | 153 + | `test/scripts/test_interop_udp_only.sh` | UDP-only communication test | 154 + | `test/scripts/test_interop_go_joins.sh` | Go node joining OCaml cluster | 155 + 156 + ### Debug Utilities 157 + 158 + ```bash 159 + # Test packet encoding/decoding 160 + dune exec swim-debug-codec 161 + 162 + # Receive and display incoming SWIM packets 163 + dune exec swim-debug-recv 164 + 165 + # Send manual ping to a target node 166 + dune exec swim-debug-ping 167 + ``` 168 + 169 + ## Running Tests 170 + 171 + ```bash 172 + dune runtest 173 + ``` 174 + 175 + ## License 176 + 177 + ISC License. See [LICENSE](LICENSE) for details.