← back 0x0 Distribution Jul 12, 2026I'm learning that adding a network to a programming language is easy if I lie to myself, just a bit.
I can call a socket, serialize a value, send some bytes, and say the language is distributed. The demo works. Two terminals print something. I am genius for a few seconds.
Then one node disappears after receiving a message but before acknowledging it. The sender does not know whether to retry. The retry may perform the work twice. A worker crashes, restarts, and finds an old message. Two nodes disagree about membership. A token leaks into a diagnostic report. The network delivers message
2before message1.The socket was the easy part.
The real distributed system begins when the answer to "what happened?" is "we cannot know from here."
That is the problem
0x0is starting to tackle.This follows my earlier attempt to describe 0x0 technically. The short version is that
0x0is a small, symbolic, pure-functional language kernel with a self-hosted compiler. It looks like a Lisp, emits native artifacts, and has an unhealthy interest in making its contracts inspectable.That last part turns out to be useful when the network gets involved.
A Remote Call Is Not A Function Call
A local function call has a fairly comfortable contract. Control enters a function and eventually returns, panics, or kills the process. The caller and callee share one machine's memory and failure boundary.
A remote operation adds uncomfortable states:
- the message was never sent;
- the message was sent but never received;
- the message was received but not processed;
- the message was processed but not acknowledged;
- the acknowledgement was sent but not received;
- the operation ran twice;
- the recipient restarted and forgot what it knew;
- the sender and recipient disagree about who belongs to the cluster.
This is why I do not want remote execution to look exactly like an ordinary function call. Hiding those states does not remove them. It only makes the failure policy accidental.
0x0 starts from actors and messages instead.
(ƒ send-job
(∷ (→ (RemoteActor Text) Text Any))
(↯ actor)
(worker payload)
(⟿ worker payload))
The RemoteActor type carries intent. The actor effect says this function
crosses the actor runtime boundary. The ⟿ form says this is a remote send.
There is an important limitation: this source form does not open a network connection by itself. The compiler lowers it to an ordinary tagged value:
(list "RemoteActorSend" worker payload)
That value is a descriptor. It says what the program wants the runtime to do. The runtime remains responsible for scheduling, transport, delivery, and failure policy.
I like this separation. Syntax records intent. Data carries the intent across the boundary. Effects prevent pure code from secretly performing the action.
The Actor-Shaped Core
The actor model gives each unit of work an identity and a mailbox. Instead of
sharing mutable state directly, actors exchange messages. In the model used by
0x0, an actor has:
- an actor reference containing a node, name, and message type;
- a FIFO mailbox;
- a processed-message log;
- a restart count;
- an alive or dead state.
Messages have a sender, recipient, payload, and type. The local model uses a deterministic scheduler: actor names have a stable order and one message is dequeued per step.
It creates useful failure boundaries. A worker can fail without making every actor share its stack and mutable state. A mailbox gives pending work an explicit home. An actor reference separates logical identity from a raw pointer or socket.
The repository also defines pure constructors for the runtime vocabulary:
(actor-ref node name)
(actor-mailbox owner messages)
(actor-typed-send sender recipient payload)
(actor-dead-letter sender recipient payload)
(actor-supervision-policy strategy max-restarts window)
(actor-crash-report actor failure fingerprint)
(actor-restart-limit policy count window)
They produce values such as:
(list "ActorSupervisionPolicy" strategy max-restarts window)
A policy can be inspected, serialized, hashed, tested, or rejected before a host runtime acts on it.
Fault Tolerance Means Deciding How To Fail
A failure has a bounded effect and a defined next decision.
0x0 breaks that decision into a few explicit pieces.
Supervision
A supervisor owns a restart strategy, a maximum restart count, and a time window. A crash becomes a typed report. The next action becomes a supervision decision: restart, stop, or escalate.
The useful part is the restart budget. It is a denial-of-service loop with optimistic branding. A bounded restart policy makes the failure visible when recovery is no longer working.
The local fixture models a worker receiving one, boom, and two. boom
causes a crash; the supervisor restarts the worker if its budget allows it; the
event report records the crash and restart.
Dead Letters And Acknowledgements
An undeliverable message is not discarded as an invisible transport detail. It
can become an ActorDeadLetter or DistributedDeadLetterReport. Successful
delivery has a DistributedDeliveryAck descriptor.
An acknowledgement still does not prove that business work happened exactly once. It only proves whatever layer the acknowledgement contract defines. That distinction matters. Distributed systems get dangerous when "received," "processed," and "committed" are treated as synonyms.
Replay
Actor events have a sequence and a replay fingerprint. The goal is to make a failure reproducible from the event stream.
Determinism helps here. If scheduling order is stable, replay and bounded model checking have fewer meaningless interleavings to explore. The repository has contracts for replay events, fingerprints, distributed replay plans, and counterexample reports.
The current replay gate is deliberately small. It validates fixture fingerprints and rejects known tampering cases; it is not yet a durable, cryptographically chained event log. The shape exists before the full storage system does.
Capability Boundaries
0x0 separates source effects from detailed runtime capabilities such as:
cap.actor.runtime
cap.replay.deterministic
cap.distributed.node
cap.network.client
cap.network.server
Cluster membership also carries capability advertisements. A node should not receive work it is not allowed or able to perform. The cluster contract names rejection cases for invalid credentials, sender spoofing, replayed nonces, and capability mismatch.
This is fault tolerance too. Rejecting an invalid transition before it mutates state is usually cheaper than recovering afterward.
Distribution As Layers
The current design is easier to understand as layers:
0x0 actor syntax
|
v
tagged intent values
|
v
deterministic actor and supervision model
|
v
host transport boundary
|
v
event, replay, and release evidence
At the source level, actor, remote-actor, !, ⟿, receive, and select
become tagged data.
At the runtime-contract level, runtime/distributed-actors.0x0 defines nodes,
envelopes, partitions, membership, authentication, acknowledgements, dead
letters, reconnect plans, supervision, and replay.
At the local behavior level, the actor gate checks fixture reports for FIFO mailboxes, crashes, restart policy, duplicates, out-of-order messages, partitions, unauthorized sends, and counterexamples.
At the transport level, the distributed-cluster gate opens one-shot loopback TCP listeners, probes three named nodes, records their addresses and capability advertisements, and verifies the report and replay contract.
What The Current Gate Actually Proves
The repository documentation carefully calls this a bounded loopback boundary. Looking at the implementation makes the boundary even clearer.
The cluster adapter really opens local TCP listeners and exchanges status JSON
over 127.0.0.1. That proves the host environment can create and reach the
transport endpoints used by the gate.
The current adapter also writes deterministic fixture evidence for delivery acknowledgements, duplicates, dead letters, supervised restarts, partitions, timeouts, reconnects, unauthorized tokens, spoofing, nonce replay, and capability mismatches. The tests verify that those report fields and stable diagnostics remain present, and that raw credentials do not appear in the report.
What it does not yet do is drive every one of those failure cases through a long-lived concurrent message transport. The actor adapter is also a fixed deterministic fixture, not yet a general scheduler for arbitrary application actors.
So:
0x0has source forms and pure contracts for actor and distributed intent;- it has deterministic supervision, failure, replay, and model-check evidence shapes;
- it has a real local TCP probe boundary and stable report gates;
- it does not yet have a production remote cluster runtime.
There are no durable distributed mailboxes, WAN partition claims, cross-version protocol negotiation, managed membership service, or remote datacenter deployment here yet.
A contract scaffold is valuable when it is presented as a contract scaffold. It gives the next implementation something precise to replace and something stable to preserve.
Why The Lisp-Like Syntax Helps
The parentheses are not what make a system distributed. They do make the language unusually convenient for describing one.
The Syntax Is Already A Tree
This:
(⟿ worker "compile")
is already close to its abstract syntax tree. The compiler does not need to
recover a complicated structure from many unrelated grammar rules. It sees a
list whose head is ⟿ and whose remaining elements are arguments.
The lowering is correspondingly boring:
(list "RemoteActorSend" worker "compile")
Boring lowering is good. There are fewer places for source intent to disappear between parser, checker, compiler, runtime, and replay tooling.
Code And Protocol Data Share A Shape
Actor messages, supervision policies, replay events, and cluster descriptors are tagged lists. Source code is also made of symbolic list forms.
I would call this homoiconic in spirit. The useful property today is simpler: the source tree and the runtime contract tree resemble each other.
That resemblance makes small domain-specific forms cheap to add. receive and
select do not require inventing an entirely separate mini-language. They are
new symbolic heads with checked argument shapes and explicit lowerings.
Effects Stay Visible
The function shape puts its contract next to its implementation:
(ƒ handle-message
(∷ (→ (Actor Text) Text Any))
(↯ actor)
(worker message)
(! worker message))
The type says what values cross the boundary. The effect row says what kind of world the function may touch. The body says what it does. All three are forms in the same tree and can be inspected before code generation.
For a distributed language, that is more than visual consistency. It lets the compiler reject pure code that attempts an actor or network effect. The syntax makes authority reviewable.
Small Syntax Supports Multiple Interpreters
0x0 has a self-hosted compiler, an assembly recovery seed, native backends,
runtime tooling, and evidence checkers. A uniform s-expression surface reduces
how many different parsers and representations those paths need to agree on.
When the project is trying to verify compiler succession, ABI rules, runtime capabilities, and replay contracts, a small grammar is not minimalism for its own sake. It is a way to reduce the trusted surface.
The tradeoff is real. Unicode symbols such as ƒ, ∷, ↯, and ⟿ are compact
and visually distinct, but they are harder to type, search for, and explain to
tools that assume ASCII. Parentheses also make malformed nesting easy for a
human to miss without editor support.
I still think the structural win is worth exploring. The syntax makes policy look like data, and distributed systems are mostly policy wearing a network cable.
The Lesson
The interesting part of distribution in 0x0 is not that it can open a local
socket. Almost every general-purpose language can do that.
The interesting part is the attempt to make the uncomfortable decisions explicit:
- who owns a mailbox;
- what identifies a node and an actor;
- what a message is allowed to contain;
- which effects a function may perform;
- what happens when a worker crashes;
- how often it may restart;
- where an undeliverable message goes;
- what an acknowledgement means;
- which event order is replayed;
- what a gate actually proves.
The Lisp-like syntax helps because every one of those decisions can become a small symbolic form, a tagged value, or a checked policy. The fault-tolerance story helps because every failure can become an event and a bounded next decision instead of an invisible exception somewhere behind a remote call.
Glossary
Actor: an isolated unit of computation with an identity and a mailbox.
Mailbox: the queue of messages waiting for an actor.
Supervision: a policy for deciding whether a failed actor should restart, stop, or escalate its failure.
Dead letter: a message that could not be delivered to its intended recipient.
Replay: reproducing or validating behavior from a recorded event sequence.
Capability: explicit authority to perform an effect such as actor execution, network access, or file access.
Partition: a condition where nodes cannot communicate even though one or both may still be running.
S-expression: a parenthesized symbolic list used to represent code or data.
Homoiconicity: the property of a language whose code has the same basic
representation as its data. 0x0 currently benefits from this resemblance
without claiming a complete Lisp macro environment.