Overview
This document specifies the lifecycle of NTL synapses — the persistent, weighted connections between nodes that form the network topology.Synapse States
A synapse MUST be in one of the following states:State Transitions
Formation Handshake
Two nodes form a synapse through a three-step handshake:- SYN — Initiating node sends identity, capabilities, supported crypto modules, and a fresh random challenge
- SYN-ACK — Responding node confirms, sends its own identity, its own challenge, and a signature over the initiator’s challenge
- ACK — Initiating node signs the responder’s challenge; the synapse enters ACTIVE state
The Challenge Is Not Optional
A node MUST verify that the peer signed the challenge this node just issued, and that challenge MUST come from a cryptographically secure generator. Three requirements follow, each of which an implementation can get wrong while still appearing to authenticate:- A signed hello alone is not authentication. It is self-contained and replayable, so it proves that someone once held the private key — not that the party on this connection holds it. Since a node announces itself to any connection, such a hello can be collected by anyone able to open a socket and replayed elsewhere.
- A node MUST refuse a peer presenting its own identity. Reflecting a node’s own messages back at it satisfies every other check — the identity binding holds, the signatures are genuine, and a reflected proof answers the node’s own challenge — yet leaves the node with a session and an active synapse under its own identity, routing its own traffic to whoever holds the socket.
- A node SHOULD refuse a second session for an identity that already has a live one, rather than replacing it. Replacement means the most recent connection captures everything routed to that peer.
Eligibility to Carry Signals
A synapse may be chosen to carry a signal if and only if its state is Active or Weakening.
Including
Weakening is normative, and it is a correctness requirement
rather than a tuning preference. A synapse regains weight only by carrying
traffic that then succeeds (learning-model §2). A
state that is reachable from a single negative outcome and ineligible for
traffic is therefore a one-way trap — the synapse can never earn its way
back.
With initial_weight at 0.1 and the Active threshold also at 0.1, every
newly formed synapse sits exactly on that boundary: one rejection would drop
it to Weakening. An implementation that filtered candidates on
state == Active would permanently exclude any peer that failed once, and
routing would ossify around whichever peers happened to succeed first — the
precise failure that learning-model §4 exists to
prevent.
Dormant is genuinely unavailable: the connection is idle and must be
reactivated before it can carry anything. That is a different situation from
a live connection with a low weight.
Weight Management
Initial Weight
New synapses MUST start with weight ≤ 0.1. This prevents new or unknown nodes from immediately gaining high propagation priority.Weight Adjustment
Synapse weight is learned, not configured. The normative update rule, its bounds, and its hyperparameters are specified in learning-model; this section states only the lifecycle-relevant consequences. Weight changes on three occasions:
Two constraints bind every adjustment:
- Weight MUST NOT exceed
max_weight(default1.0) or fall beloww_min(default0.001). - Total outbound weight across all of a node’s synapses MUST be bounded by
W_max, rescaling all synapses when the bound is exceeded. Without this a node’s synapses all saturate and the scoring function stops discriminating.
0.1.0-draft specified
w += signal.weight * 0.01 on success and
w *= (1 - decay_rate) per interval. Both are superseded. The old
increment had no notion of whether delivery actually succeeded — it
rewarded transmission, not outcome — and interval-based decay gave a
different result depending on how often the sweep happened to run.Thresholds
Both thresholds MUST come from configuration. Hard-coding them means an
operator who retunes
initial_weight — or either threshold — silently gets
the shipped values, and the note above about initial_weight sitting exactly
on the Active boundary stops being checkable.
Forming Is Not Weight-Derived
The four weight-derived states are Active, Weakening, Dormant and Pruned. A weight update MUST NOT move a synapse out ofForming: weight says nothing
about whether two nodes have authenticated each other, and Forming exists
precisely to keep a synapse ineligible to carry signals until they have. Only
the completion of the handshake leaves Forming.
An implementation that promotes on the first weight update makes the state
skippable, and with it the guarantee that traffic only ever crosses an
authenticated synapse.
Dormant Must Not Be Terminal
Dormant is ineligible to carry signals, and weight is earned only by carrying
them (learning-model §2). So a node that provides no way
out of Dormant has made it terminal in all but name: the synapse cannot earn
its way back and simply waits out prune_after_hours.
A completed handshake with a dormant peer MUST return the synapse to
Active with its weight restored to initial_weight. The handshake is the
evidence the table above asks for — the peer is demonstrably reachable and
authenticated. An implementation that treats an existing synapse record as
nothing to do will strand every peer that was ever quiet for long enough.
Transport Negotiation
During handshake, nodes negotiate transport:- Both nodes advertise supported transports (QUIC, TCP, Unix, Bluetooth LE)
- The highest-priority mutually supported transport is selected
- QUIC is the RECOMMENDED default
Specified and unimplemented. The reference implementation performs no
negotiation:
ntl-net opens a TCP connection and runs the three-message
authenticated handshake over it. Nothing advertises a transport list and
nothing selects from one, so step 1 and step 2 have no counterpart in code
and the RECOMMENDED default in step 3 is not what runs.This is a gap in the implementation, not a defect in the requirement — but an
implementer should know that interoperating with the reference
implementation today means TCP, and that a peer expecting to negotiate will
find no one negotiating back. Tracking:
openNTL/ntl#15.Synapse Limits
Nodes SHOULD enforce a maximum synapse count to prevent resource exhaustion. The RECOMMENDED default is 1,000 active synapses per node. When the limit is reached, the node MUST reject new synapse requests or prune the weakest existing synapse.Related
- learning-model — how weight and affinity are learned
- propagation-rules — how weight is used in routing
- storage-interface §1 — what must survive a restart