Creates an AutoInterface. Discovery sockets are opened by connect.
Optionaloptions: AutoInterfaceOptions = {}Protected_Whether a terminal closed event has already been dispatched for the
current connection episode (dedupe guard).
Protected_AbortController for the current reconnect wait, so disconnect() can
cancel an in-flight backoff immediately.
Protected_Reconnect attempt counter for the current drop episode. Reset to 0 at the start of each Interface._runReconnectLoop run.
Protected_Single-flight guard: only one reconnect loop runs at a time.
Times an announce burst has latched on this interface.
Whether the hardware MTU is autoconfigured from the nominal bitrate by
optimiseMtu (matching the Python reference's AUTOCONFIGURE_MTU).
Off on the base class; transport-grade interfaces (Local, TCP, Backbone)
opt in.
Whether automatic reconnection is enabled for this initiator interface.
Set whenever peer/link state changes a way that should reset announce-rate control.
Per-dial connect timeout in seconds.
Epoch milliseconds when the interface was constructed.
Per-interface default IFAC size (bytes) when IFAC is enabled but no
explicit ifacSize was given. Subclasses override (16 for
Auto/Backbone, 8 for AX.25 in the reference implementations); the base
default of 16 matches the common case.
Permanent stop signal read by the reconnect loop. Set by disconnect().
Per-interface path preference weight (default 0 via
Reticulum.defaultGravity). When the same announce reaches this node
over multiple interfaces, the path table prefers the entry learned via
the higher-gravity interface (e.g. a wired backbone over a slow radio
link). null means "no preference" — import("../core/reticulum.js").Reticulum
substitutes its defaultGravity at addInterface time.
Announces dropped (not held) because the held table was at its cap.
Held announces released back into the inbound pipeline.
Announces held while an ingress burst is latched, keyed by destination hash hex. Drained by processHeldAnnounces on the transport sweep.
The hardware MTU of this interface in bytes — the largest frame the
medium can carry in one piece (HW_MTU in the reference
implementations; None/null means unbounded/unknown).
Unlike the Python reference (a class-level constant re-optimised per
instance), this is an instance field set by optimiseMtu or the
subclass constructor; interfaces with a fixed medium MTU initialize it
directly (e.g. RNode's 508).
Incoming-announce timestamp ring (seconds).
Earliest held-announce release time (seconds); set on burst activation.
Remaining quiet evaluations before a latched PR burst unlatches.
Derived IFAC Ed25519 identity (only its signing ability is used).
Populated lazily by _ensureIfacMaterial; null while IFAC is
disabled or before first use.
Derived 64-byte IFAC key (HKDF over import("../core/ifac.js").IFAC_SALT).
Shared passphrase enabling IFAC (ifac_netkey). See ifacNetname.
Shared network name enabling IFAC (ifac_netname). When set together
with ifacNetkey (or alone), packets on this interface are
authenticated and obfuscated. Both endpoints must share the same value.
IFAC signature of fullHash(ifacKey), published in the discovery
announce.
IFAC-specific violations: missing/invalid/short IFAC fields.
Whether ingress burst control is enabled on this interface. Disabling
makes shouldIngressLimit and shouldIngressLimitPr always
return false.
Whether this interface is the initiator (the outbound dialer). Only initiators reconnect; adopted/server-spawned sockets never do.
Incoming path-request timestamp ring (seconds).
Maximum reconnection attempts per drop. Infinity retries forever.
Multi-interface dedup deque: recent data-packet hashes with their expiry. Capped at MULTI_IF_DEQUE_LEN entries; a hit within MULTI_IF_DEQUE_TTL_SECS drops a duplicate seen on another interface.
Outgoing-announce timestamp ring (seconds).
Outgoing path-request timestamp ring (seconds).
Inbound packet-filter (dedup) hits.
Times a path? burst has latched on this interface.
Unique-tag path requests dropped while a PR burst was latched.
Generic protocol violations: malformed packets, bad signatures, etc.
Seconds to wait between reconnection attempts.
Total bytes received on this interface. Counted as the deserialized RNS packet length, so it reflects the on-the-wire RNS payload, not framing overhead. Apps derive a transfer rate by sampling this counter over time.
The underlying socket, when this interface is backed by a Node.js stream.
Transport that owns this interface, set via attachTransport. When
present, spawned peers are auto-registered with it; otherwise the caller
registers them via the "connection" event.
Total bytes transmitted on this interface.
StaticANNOUNCE_Seconds after which an unanswered announce sample decays out of the
deque (AR_FREQ_DECAY = 1/AR_MINFREQ_HZ = 10 s).
StaticFREQ_Rolling-sample cap for the announce/PR frequency deques (48 samples, shared across announce and PR tracking).
StaticIC_Announce burst threshold for established interfaces, Hz (IC_BURST_FREQ).
StaticIC_Announce burst threshold for new interfaces, Hz (IC_BURST_FREQ_NEW).
StaticIC_Seconds a burst stays latched after activation (IC_BURST_HOLD_SECS).
StaticIC_Seconds before held announces may release after a burst (IC_BURST_PENALTY_SECS).
StaticIC_Minimum deque samples before a frequency is reported
(IC_DEQUE_MIN_SAMPLE = 2 — i.e. > 2 samples).
StaticIC_Seconds between held-announce releases once draining
(IC_HELD_RELEASE_INTERVAL_SECS).
StaticIC_Interface age in seconds below which the stricter "new interface"
burst thresholds apply (IC_NEW_TIME_SECS = 2 h).
StaticIC_Quiet evaluations required to unlatch a PR burst after the hold
(ic_pr_burst_cooldown = 3; any above-threshold evaluation resets it).
Anti-flapping hysteresis added upstream in "Improved PR ingress
limiter" — the announce limiter has no cooldown.
StaticIC_Path-request burst threshold for established interfaces, Hz (IC_PR_BURST_FREQ).
StaticIC_Path-request burst threshold for new interfaces, Hz (IC_PR_BURST_FREQ_NEW).
StaticMAX_Maximum held announces buffered per interface while an announce burst
is latched (MAX_HELD_ANNOUNCES). A held table at this size silently
drops further announces for destinations not already held.
StaticPR_Seconds after which a PR sample decays (PR_FREQ_DECAY = 10 s).
Whether IFAC is enabled on this interface (a shared secret is configured).
Number of spawned peer data interfaces (the peer count).
The parent AutoInterface has no RNS byte stream of its own — it only
spawns per-peer interfaces that do. Returns null (rather than throwing)
so TransportCore.addInterface can attach the parent to receive the
transport ref via attachTransport without trying to grab a writer.
Protected_Protected
Signals the reconnect loop to stop and cancels any in-flight backoff.
Client subclasses call this at the top of their disconnect().
Protected_Protected
Dispatches a terminal closed event exactly once per connection episode.
Protected_Protected
Counts an inbound packet against rxb and dispatches the "packet"
event, the single inbound chokepoint each interface's read loop funnels
through.
Uses the deserialized packet's cached raw bytes when available (set by
Packet.deserialize), avoiding a re-serialize. RNodeInterface dispatches
its own packets (it counts the IFAC-inclusive payload) and does not call
this.
Protected_Protected
Derives and caches the IFAC key/identity/signature from the configured
ifacNetname / ifacNetkey. No-op (resolves false) when
IFAC is disabled. Memoised so the HKDF + Ed25519 key load runs at most
once.
true if IFAC material is available.
Protected_Protected
Dials the peer and sets up the RNS streams, resolving once connected and
dispatching connected. Implemented by reconnect-capable client
subclasses; used both for the initial connection and each reconnect
attempt by the shared Interface._runReconnectLoop.
Protected_Protected
Called when the underlying connection drops (the inbound stream ends or
errors). For an initiator with auto-reconnect enabled and not deliberately
detached, dispatches disconnected and kicks off the reconnect loop;
otherwise dispatches a terminal closed event — matching the Python
reference, which reconnects the initiator on any termination and tears
down (non-reconnecting) everyone else.
Protected_Protected
Initializes shared reconnect state from constructor options. Called by client interface subclasses (TCP, WebSocket) that support reconnection.
Subclasses must also set Interface.initiator: true for an
outbound dialer, false for an adopted/server-spawned socket.
Decides whether an interface should be adopted, matching the Python reference's per-interface skip chain.
When an allow-list (AutoInterface.allowedInterfaces, devices) is
set, only those names are adopted, and they bypass the ignore lists
(including loopback), matching the Python reference, with the same
relaxation that lets the single-host loopback smoketest pin lo0
explicitly. (The Python reference unconditionally skips lo0 on Darwin
even when allowed; we relax only that, so production parity holds:
loopback is never adopted without devices.)
Multi-interface dedup check. Returns true (and remembers nothing) if the packet hash was seen within MULTI_IF_DEQUE_TTL_SECS; otherwise remembers it and returns false, matching the Python reference's multi-interface dedup.
SHA-256 of the raw datagram bytes.
true if the packet is a recent duplicate.
Wall-clock seconds.
Protected_Protected
Authenticates an inbound discovery datagram and, on success, records the
peer. Verifies that the first FULL_HASH_LENGTH bytes equal
SHA-256(group_id || src_addr), exactly as the Python reference does.
Self-multicast-echoes (src is one of our own link-local addresses)
feed the carrier watchdog state instead of adding a peer.
Protected_Protected
Verifies and unseals inbound raw wire bytes.
Enforces the flag-presence rules: an IFAC-enabled interface drops a
flag-clear packet, and a plain interface drops a flag-set packet — both
return null (silent drop). For an IFAC interface it then unmasks,
strips the IFAC and verifies it by re-signing; a mismatch also yields
null. Subclasses/interfaces call this at the chokepoint where a frame
has been unframed to bytes, just before Packet.deserialize.
Sealed or plain wire bytes straight off the medium.
The unsealed bytes, or null to drop.
Protected_Protected
Records an outbound packet against txb. Subclasses (or the
interface's outbound stream write callback) call this at the point a
packet is handed to the medium — the single chokepoint where every
transmitted packet passes, whether sent via send, the transport
router, or a broadcast.
RNodeInterface overrides its own counting (it measures the IFAC-inclusive wire payload) and does not call this.
Protected_Protected
One peer-jobs tick (matching the Python reference's peer jobs). Expires silent peers (and tears down their spawned interfaces), sends reverse-peering packets to peers due for one, rebinds the per-interface data socket when its link-local address changes, and runs the multicast-echo carrier watchdog.
Protected_Protected
Runs the single-flight reconnect loop. Repeatedly waits reconnectWait
seconds then attempts to re-establish the connection via the subclass
_establishConnection() hook, until it succeeds, the interface is
detached, or maxReconnectTries is exceeded (terminal closed).
Each attempt fires a reconnecting event with the upcoming attempt
number, the wait, and the cap, for observability. A successful reconnect
fires connected (via _establishConnection).
Protected_Protected
Seals raw (un-IFACed) wire bytes for transmit. No-op passthrough when
IFAC is disabled; otherwise derives the IFAC material on first use, then
signs, sets the ifac_flag, inserts the IFAC field and XOR-masks the
packet. Subclasses/interfaces call this at the chokepoint where a packet
is serialised to bytes, just before framing.
Serialised, unsealed wire bytes.
The bytes to put on the medium.
Sends a raw RNS packet to a peer on the given interface, from that
interface's data socket. The destination is re-scoped with %ifname so the
OS routes it out the correct link-local. Semantically matches the Python
reference's peer send path (it uses a shared unbound outbound socket +
addr%ifindex; we reuse the per-interface bound
data socket, which already has the correct source address).
Descope'd peer link-local address.
Protected_Protected
Resolves after ms, or immediately if signal aborts. Used so
disconnect() can cancel an in-flight reconnect backoff at once.
The addEventListener() method of the EventTarget interface sets up a function that will be called whenever the specified event is delivered to the target.
Optionaloptions: boolean | AddEventListenerOptionsRecords a discovered peer, or refreshes a known one, matching the Python
reference. For a new peer this spawns an AutoInterfacePeer data
interface, registers it with the attached transport (if any), and dispatches
a "connection" event with the peer for parity with the other server
interfaces.
If addr is one of our own link-local addresses, this is our own multicast
echo: it feeds the carrier watchdog (multicastEchoes/initialEchoes)
rather than adding a peer.
Descope'd source link-local address.
Age of this interface in seconds.
Applies node-global ingress-control overrides to this interface (the
reference implementations apply node-level ic_* defaults to every
interface — there is no per-interface config for these). Only keys
present in overrides are assigned; absent keys keep the class
constants. Called by
import("../core/reticulum.js").Reticulum#addInterface when the
node was constructed with an ingressControl config block. These are
deliberately not constructor options / interface schema properties:
they scope to the whole node.
Receives the owning transport from TransportCore.addInterface so spawned
peers can be auto-registered (Open Q1 of work doc #12). Also registers any
peers spawned before the transport was attached, so the
addInterface/connect call order doesn't matter.
Enumerates, adopts suitable interfaces, opens the per-interface multicast +
unicast discovery sockets, and starts the announce loop and receive
handlers. Resolves once all adopted interfaces are listening and dispatches
"connected".
The discovery address and per-interface tokens are derived lazily here
(rather than in the constructor) because Identity.fullHash is async.
Closes all discovery and data sockets, stops the announce loops,
disconnects every spawned peer (which dispatches "closed" so an attached
transport removes it), drops all tracking state, and dispatches
"closed".
The dispatchEvent() method of the EventTarget sends an Event to the object, (synchronously) invoking the affected event listeners in the appropriate order. The normal event processing rules (including the capturing and optional bubbling phase) also apply to events dispatched manually with dispatchEvent().
Returns a snapshot of traffic and link statistics for this interface, for observability and UIs.
Subclasses that carry medium-specific telemetry (notably import("./rnode.js").RNodeInterface, which exposes RNode airtime, channel load and signal quality) override this to extend the snapshot.
Buffers an announce for delayed processing while an ingress burst is
latched. Announces at or beyond PATHFINDER_M - 1 (127) hops are
dropped rather than held; a destination already in the table always
replaces its entry (newest emission wins); beyond
icMaxHeldAnnounces distinct destinations, new ones are silently
dropped.
Validated announce.
Records an IFAC (interface authentication code) violation on this
interface: missing IFAC flag, insufficient packet size for the IFAC
field, or an IFAC that fails re-verification. Increments
ifacViolations, logs at DEBUG, returns null.
Optionaldescription: string | nullIncoming announce rate in Hz over the current sample window. Returns 0 with fewer than Interface.IC_DEQUE_MIN_SAMPLE+1 samples; a sample older than arFreqDecay decays out of the window.
Incoming path? request rate in Hz. Same sampling rules as
incomingAnnounceFrequency, with the PR decay window.
Autoconfigures hwMtu from the nominal bitrate when
autoconfigureMtu is set, using the reference implementations'
bitrate→MTU table (faster media amortize framing overhead over larger
frames). With autoconfiguration off this is a no-op — a subclass that
set a fixed hwMtu keeps it.
Called by interface constructors and by the node setup once the
configured bitrate is known (the Python reference calls optimise_mtu()
from Reticulum._add_interface and per-connection spawn sites).
Outgoing announce rate in Hz. Needs more than one sample.
Outgoing path? request rate in Hz. Needs more than one sample.
Records a packet-filter (dedup) hit on this interface: an inbound
non-announce packet whose hash is already in the dedup ring. Increments
packetFilterHits, returns null.
Releases one held announce if conditions allow: at most one announce per icHeldReleaseInterval, never before icHeldRelease, and only while the incoming announce frequency is back below the burst threshold. Selection prefers the lowest hop count (nearest destinations converge first). The caller re-injects the returned packet into the normal inbound pipeline.
The announce to
re-inject, or null when nothing is releasable.
Records a generic protocol violation on this interface: malformed
packets, invalid announce signatures, tagless / oversized path requests,
undecodable MTU signalling, inbound processing exceptions. Increments
protocolViolations, logs at DEBUG, and returns null so it
chains as the return value at every drop site.
Optionaldescription: string | null
Optional human-readable detail.
Records an inbound announce into iaFreqDeque. Spawned interfaces propagate the sample to their parent so bursts are detected at the medium level.
OptionalfromSpawned: boolean
Internal: true when called on a parent.
Records an inbound path? request into ipFreqDeque. Spawned
interfaces propagate to their parent.
OptionalfromSpawned: boolean
Internal: true when called on a parent.
Refreshes a known peer's last-heard timestamp.
The removeEventListener() method of the EventTarget interface removes an event listener previously registered with EventTarget.addEventListener() from the target. The event listener to be removed is identified using a combination of the event type, the event listener function itself, and various optional options that may affect the matching process; see Matching event listeners for removal.
Optionaloptions: boolean | EventListenerOptionsSends bytes wrapped in KISS framing
Records an outbound announce into oaFreqDeque; counted by
TransportCore.broadcast at the transmit chokepoint, and surfaced as
outgoingAnnounceFrequency for the future announce-rate-table
work (#31 step 6).
OptionalfromSpawned: boolean
Internal: true when called on a parent.
Records an outbound path? request into opFreqDeque; consumed by
egress PR limiting (work doc #31 step 4).
OptionalfromSpawned: boolean
Internal: true when called on a parent.
Whether announce ingress should be limited right now. Latches a burst
when the incoming announce frequency exceeds the threshold for the
interface's age — stricter (icBurstFreqNew) during the first
icNewTime seconds. Once latched, stays limiting for at least
icBurstHold seconds and until the frequency drops back below the
threshold; the call that
unlatches still reports true (mirroring the Python reference: the
next packet after it flows normally).
Consumers: held-announce buffering for unknown destinations (work doc #31 step 3). The announce frequency side effects (latching plus arming icHeldRelease with the icBurstPenalty) keep the state correct when the announce-rate-table work lands.
Whether path? request ingress should be limited right now (including
cooldown hysteresis). Latches when the incoming PR frequency exceeds the
age-dependent threshold (icPrBurstFreqNew during the first
icNewTime seconds, icPrBurstFreq after). Once latched,
stays limiting for at least icBurstHold seconds; after the hold,
unlatching takes Interface.IC_PR_BURST_COOLDOWN_SECS+1 consecutive
below-threshold evaluations — any above-threshold evaluation resets the
cooldown (anti-flapping at the boundary). Consumers: TransportCore
drops unique-tag path requests while a burst is latched (work doc #31
step 2 — the inline-processing equivalent of an ingress-limited
traffic-class demotion).
StaticgetReturns the JSON Schema describing the options accepted by the AutoInterface constructor, for dynamically-generated setup UIs.
A JSON Schema object.
AutoInterface — zero-config IPv6-multicast local-network peering.
Phases 1–3 of work doc #12: discovery + peer tracking (1), the data path (2), and the lifecycle jobs — peer expiry, reverse-peering send, link-local rebind, multicast-echo watchdog (3). Phase 4 (register in the interface registry; schema already present) remains. (
AutoInterfacePeer) and registering it with a transport arrives in Phase 2; the lifecycle jobs (expiry, reverse-peering send, rebind, watchdog) arrive in Phase 3.The parent interface dispatches:
"connected"once discovery sockets are up,"peer"with{ address, ifname }when a new peer is authenticated (Phase 2 will additionally spawn a peer interface and dispatch"connection"),"closed"ondisconnect(),"error"on socket errors.