NTL is in active development. Everything below is real and runs today, but the
CLI and API are not yet stable.
Prerequisites
- Node 18+ (for the npm install) or Rust 1.85+ (install)
- A terminal
Install
Initialize a Node
~/.ntl (override with --home or $NTL_HOME):
Output:
--class edge | full-node | high-traffic. The class sets learning rate, queue depth, refractory period,
and fanout together, so the combination stays coherent.
Two Nodes, One Signal
NTL is peer-to-peer with no broker, so the smallest interesting network is two nodes. Use two terminals. Terminal 1 — the receiver:weight 0.450, not 0.500. The signal was attenuated by the synapse it
crossed (0.5 × 0.9). Weight decays with distance, which is what stops a
flood from travelling forever.
--acknowledged. Without it the signal is best-effort: it may be
absorbed silently and the sender never learns. With it, the sender is
guaranteed an outcome — a receipt or a timeout, never silence. Use it for
anything with a side effect. See
delivery-semantics.
weight +0.0322. That is the routing model updating. The receipt came
back, resolved the journalled routing decision, and strengthened the synapse
that carried it. Emit again and it rises again — bounded by the total weight
budget and the per-peer influence cap.
Watch the Model
WEIGHT is learned, not configured. AFFINITY is learned per signal type,
so a peer can become preferred for Query while staying unremarkable for
Command.
- exploration near zero — the node has stopped trying alternatives, so it
can no longer discover a better path. (With only one eligible synapse there
is nothing to explore, and
ntl statuswill not warn.) - pending near 100% — no receipts are arriving, so the weights reflect
nothing. Usually it means peers are emitting
best-efforttraffic only.
See It Learn
The two-node exchange above moves a weight once. To see the model actually separate a good peer from a bad one:* marks an exploratory choice. Note that peer B keeps being tried even
after it starts failing — that is deliberate, and it is what lets B recover
if it starts working again. A router that stopped probing a failing path
could never notice it had recovered.
The demo advances a simulated clock by an hour per round, because a
per-identity influence cap bounds how much one peer can move a weight inside
one window. Learning is rate-limited on purpose; see
learning-model §2.4.
Running a Long-Lived Node
--dev binds loopback only, so a development node is not reachable from the
network by accident. For a real deployment use ntl start --listen 0.0.0.0:4433 --peer <addr>, or the ntl-node / ntl-edge binaries, which
read $NTL_HOME, $NTL_LISTEN, and $NTL_PEERS.
What Is Not Built Yet
Being explicit, so you do not go looking:Next
Your First Signal
Signal types, weights, scopes, and tags in depth
Storage Backends
SQLite, Postgres, graph, and KV backends
Learning Model
How weights are actually updated
Threat Model
What NTL defends against, and what it does not