Typed distributed messaging for Gleam on the BEAM.
distribute is a thin typed safety layer over Erlang's distribution
primitives. It puts binary codecs in front of :global and Subject so
the compiler catches protocol mismatches before messages leave the
process. Payload size and dead-target detection are enforced at every
I/O boundary, so a misbehaving peer cannot OOM your node or hang it.
gleam add distributeRequires Gleam 1.16 or newer, Erlang target only.
importdistributeimportdistribute/codecimportdistribute/receiverletgreeter=distribute.named("greeter",codec.string())letassertOk(_gs)=distribute.start_registered(greeter,Nil,fn(msg,_state){io.println("got: "<>msg)receiver.Continue(Nil)})letassertOk(target)=distribute.lookup(greeter)letassertOk(Nil)=distribute.send(target,"hello")For the full walkthrough see docs/quickstart.md.
All long-form docs live in docs/:
- Quickstart. Boot, configure, send.
- Recipes. Counter, pool, versioned protocol, cluster events, custom records.
- Actors and registry.
- Messaging.
send/receive/call. - Codecs and types. Wire format, custom records, tagged messages.
- Safety and limits. Payload limits, threat model, recommended sizing.
- Typed boundary.
TypedName(msg)binds a name to a codec. Registration and lookup share the samemsgtype, and the compiler rejects mismatches. - Hard payload caps.
max_payload_size_bytesis enforced before encode and before decode on every path:send,receive,call,reply, actor handlers, selectors. - Fast-fail calls.
global.callmonitors the target. A dead target returnsError(TargetDown)immediately. Late replies and DOWN messages are drained from the caller's mailbox. - OTP-native. Real OTP gen_server-flavored actors via
gleam_otp/actor, real supervisors, real child specs.observer,sys:get_status, restart strategies all work. No magic. - Single-source codec. Encoding and decoding happen through one
Codec(a)value. Combinators (map,list,option,tuple,tagged) cover the common cases without macros.
Seamlessly encode and decode your Algebraic Data Types (enums) with a fluent builder.
pubtypeMyMessage{Text(String)Ping}importdistribute/codecimportdistribute/codec/variantletmy_codec=variant.new()
|> variant.add(0,"Text",codec.string(),Text,fn(m){casem{Text(s)->Ok(s); _ -> Error(Nil) }
})
|> variant.unit(1, "Ping",Ping,fn(m){ m == Ping })
|> variant.build()Subscribe to cluster events (NodeUp, NodeDown) to react to node topology changes.
importdistributeimportdistribute/cluster/monitorletsubj=process.new_subject()letassertOk(m)=distribute.subscribe(subj)// In your actor/processcaseprocess.receive(subj,5000){Ok(monitor.NodeUp(node))->io.println("Node joined: "<>node)Ok(monitor.NodeDown(node))->io.println("Node left: "<>node)_->Nil}// Laterdistribute.unsubscribe(m)- Two codebases, one wire.
TypedNameenforces type safety inside one codebase. Mismatched codecs across separate codebases produce runtime decode errors, not compile errors. - No auto-derive. Gleam has no macros, so complex codecs are manual. The combinators keep them short.
terminatecaveat ingleam_otp/actor1.x. External shutdown paths do not currently invoke an OTP-styleterminatecallback. If an actor owns files, sockets, ETS tables, ports, or other external resources, use the linked resource-owner pattern documented in docs/recipes.md and docs/safety_and_limits.md.- One internal coupling. We construct
Subjectfrom a remote PID via one Erlang FFI function. That is the single point that depends ongleam_erlang's subject layout.
gleam test# full suite (includes mandatory real-cluster Z2/Z3)
epmd -daemon # start Erlang distribution daemon if not already running
gleam dev # multi-node playground
gleam docs build # local API docsMIT. See LICENSE.
