pynltp 1.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pynltp-1.2.0/LICENSE +21 -0
- pynltp-1.2.0/MANIFEST.in +20 -0
- pynltp-1.2.0/PKG-INFO +392 -0
- pynltp-1.2.0/README.md +351 -0
- pynltp-1.2.0/necklink/__init__.py +103 -0
- pynltp-1.2.0/necklink/__main__.py +4 -0
- pynltp-1.2.0/necklink/baselines.py +551 -0
- pynltp-1.2.0/necklink/c_export.py +594 -0
- pynltp-1.2.0/necklink/cli.py +246 -0
- pynltp-1.2.0/necklink/codec.py +536 -0
- pynltp-1.2.0/necklink/connectome_priors.py +143 -0
- pynltp-1.2.0/necklink/internet.py +514 -0
- pynltp-1.2.0/necklink/nltp.py +1737 -0
- pynltp-1.2.0/necklink/quic_adapter.py +547 -0
- pynltp-1.2.0/necklink/socket_api.py +731 -0
- pynltp-1.2.0/necklink/workloads.py +228 -0
- pynltp-1.2.0/nltp/__init__.py +3 -0
- pynltp-1.2.0/nltp/__main__.py +5 -0
- pynltp-1.2.0/pynltp/__init__.py +3 -0
- pynltp-1.2.0/pynltp/__main__.py +5 -0
- pynltp-1.2.0/pynltp.egg-info/PKG-INFO +392 -0
- pynltp-1.2.0/pynltp.egg-info/SOURCES.txt +26 -0
- pynltp-1.2.0/pynltp.egg-info/dependency_links.txt +1 -0
- pynltp-1.2.0/pynltp.egg-info/entry_points.txt +4 -0
- pynltp-1.2.0/pynltp.egg-info/requires.txt +12 -0
- pynltp-1.2.0/pynltp.egg-info/top_level.txt +3 -0
- pynltp-1.2.0/pyproject.toml +72 -0
- pynltp-1.2.0/setup.cfg +4 -0
pynltp-1.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Amandeep Bains
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pynltp-1.2.0/MANIFEST.in
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
include LICENSE
|
|
2
|
+
include README.md
|
|
3
|
+
include pyproject.toml
|
|
4
|
+
recursive-include pynltp *.py
|
|
5
|
+
recursive-include nltp *.py
|
|
6
|
+
recursive-include necklink *.py
|
|
7
|
+
prune arduino
|
|
8
|
+
prune assets
|
|
9
|
+
prune backup
|
|
10
|
+
prune csrc
|
|
11
|
+
prune deliverables
|
|
12
|
+
prune docs
|
|
13
|
+
prune esp32
|
|
14
|
+
prune examples
|
|
15
|
+
prune experiments
|
|
16
|
+
prune java
|
|
17
|
+
prune js
|
|
18
|
+
prune results
|
|
19
|
+
prune scripts
|
|
20
|
+
prune tests
|
pynltp-1.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pynltp
|
|
3
|
+
Version: 1.2.0
|
|
4
|
+
Summary: NLTP v1.2: loss-resilient ChaCha20-Poly1305 encrypted UDP file & telemetry transport with GF(2^8) Cauchy FEC and 4G CGNAT traversal
|
|
5
|
+
Author: Amandeep Singh Bains
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/indian-aman/necklink
|
|
8
|
+
Project-URL: Repository, https://github.com/indian-aman/necklink
|
|
9
|
+
Project-URL: Releases, https://github.com/indian-aman/necklink/releases
|
|
10
|
+
Project-URL: Issues, https://github.com/indian-aman/necklink/issues
|
|
11
|
+
Project-URL: Documentation, https://github.com/indian-aman/necklink#readme
|
|
12
|
+
Keywords: nltp,pynltp,necklink,udp,fec,cauchy,galois-field,telemetry,marine,iot,esp32,stun,cgnat,nat-traversal,chacha20-poly1305
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Intended Audience :: Telecommunications Industry
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: C
|
|
19
|
+
Classifier: Programming Language :: Java
|
|
20
|
+
Classifier: Programming Language :: JavaScript
|
|
21
|
+
Classifier: Programming Language :: Python :: 3
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
26
|
+
Classifier: Topic :: Internet
|
|
27
|
+
Classifier: Topic :: System :: Networking
|
|
28
|
+
Requires-Python: >=3.9
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Requires-Dist: numpy>=1.24
|
|
32
|
+
Requires-Dist: cryptography>=41.0
|
|
33
|
+
Provides-Extra: quic
|
|
34
|
+
Requires-Dist: aioquic>=1.0; extra == "quic"
|
|
35
|
+
Provides-Extra: torch
|
|
36
|
+
Requires-Dist: torch>=2.0; extra == "torch"
|
|
37
|
+
Provides-Extra: dev
|
|
38
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
39
|
+
Requires-Dist: aioquic>=1.0; extra == "dev"
|
|
40
|
+
Dynamic: license-file
|
|
41
|
+
|
|
42
|
+
<div align="center">
|
|
43
|
+
|
|
44
|
+
# NLTP (`necklink` v1.2.0)
|
|
45
|
+
|
|
46
|
+
**Loss-resilient, two-way UDP file and telemetry transport for wireless, marine radio, `4G`/`5G` CGNAT, and satellite links — in Python, JavaScript / TypeScript, Java, zero-heap ANSI C99, and Arduino / `ESP32`.**
|
|
47
|
+
|
|
48
|
+
[](https://github.com/indian-aman/necklink/releases/tag/v1.2.0)
|
|
49
|
+
[](docs/INSTALL_AND_PYPI.md)
|
|
50
|
+
[](js/)
|
|
51
|
+
[](java/)
|
|
52
|
+
[](csrc/)
|
|
53
|
+
[](https://github.com/indian-aman/necklink/releases/download/v1.2.0/NLTP-Arduino-1.2.0.zip)
|
|
54
|
+
[](scripts/multilang_interop_test.py)
|
|
55
|
+
[](results/esp32_espnow_vs_nltp.json)
|
|
56
|
+
[](LICENSE)
|
|
57
|
+
|
|
58
|
+
[**30-Second Quick Start**](#-30-second-quick-start) ·
|
|
59
|
+
[**JavaScript / TypeScript**](#2-javascript--typescript-nodejs--browser--webrtc) ·
|
|
60
|
+
[**Java 11+ / Android**](#3-java-11--android-ionecklinknltp) ·
|
|
61
|
+
[**Arduino / ESP32**](#4-arduino-ide--platformio-esp32) ·
|
|
62
|
+
[**4G/5G CGNAT Relay**](#5-traverse-4g5g-carrier-cgnat-in-3-commands) ·
|
|
63
|
+
[**Benchmarks**](#-post-upgrade-benchmarks--hardware-verification) ·
|
|
64
|
+
[**NLTP vs. ESP-NOW**](#3-nltp-v12-vs-esp-now-on-esp32-hardware)
|
|
65
|
+
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<p align="center">
|
|
69
|
+
<img src="https://raw.githubusercontent.com/indian-aman/necklink/main/assets/nltp_v12_benchmarks.png" alt="NLTP v1.2 Multi-Protocol & ESP32 Hardware Benchmarks" width="100%">
|
|
70
|
+
</p>
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Why NLTP?
|
|
75
|
+
|
|
76
|
+
Standard `TCP` and `QUIC` stall on bursty wireless fades, while raw microcontroller protocols like `ESP-NOW` cannot cross routers/SIM cards or reassemble multi-kilobyte files when chunks drop. **NLTP v1.2** bridges embedded hardware, mobile/web gateways, and cloud/shore software with a single encrypted, FEC-protected UDP wire format:
|
|
77
|
+
|
|
78
|
+
- **Survives `20%` Packet Loss & Bursty Fades**: Systematic `GF(2^8)` Cauchy forward error correction (8-shard generations + incremental Gauss-Jordan decoding) reconstructs lost packets without waiting for round-trip retransmission timeouts.
|
|
79
|
+
- **1-RTT Bottleneck Pacing**: Adaptive BBR-style delivery-rate and packet-train probing learns link capacity in a single round trip without overflowing router queues.
|
|
80
|
+
- **One Wire Format Across 5 Platforms (`ESP32` / C99 ↔ Python ↔ JavaScript/TS ↔ Java)**: Zero-malloc ANSI C99 (`~65 KB` RAM on `ESP32`) talks two-way (`tx` + `rx` on one UDP port) directly to Python, Node.js/Browser, and Java/Android endpoints, verified by `ChaCha20-Poly1305` (256-bit AEAD), `RFC 6479` replay protection, and end-to-end `SHA-256` receipts (`210/210` cross-language pairings verified).
|
|
81
|
+
- **Built-In NAT & Carrier CGNAT Traversal**: `RFC 5389` STUN discovery, low-TTL (`TTL=2`) direct UDP hole-punching, keepalives, and automatic 3-second fallback to a zero-knowledge 1-port UDP relay (`NLTPRelayHub`).
|
|
82
|
+
|
|
83
|
+
```mermaid
|
|
84
|
+
flowchart LR
|
|
85
|
+
A["ESP32 / C99 Node (Zero-Heap, ~65 KB RAM)"] <-->|"Wi-Fi / Marine Radio / Satellite"| B["Direct UDP + Cauchy FEC + ChaCha20-Poly1305"]
|
|
86
|
+
B <--> C["Python · Node.js / TS · Java / Android Shore & Cloud"]
|
|
87
|
+
A <-->|"4G/5G SIM Behind CGNAT"| D["STUN P2P Punch or 1-Port NLTPRelayHub (Zero Keys)"]
|
|
88
|
+
D <--> C
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## ⚡ 30-Second Quick Start
|
|
94
|
+
|
|
95
|
+
### 1. Python (`pynltp` — `import nltp` / `import pynltp` + `nltp` CLI)
|
|
96
|
+
|
|
97
|
+
Install on Windows, Linux, or macOS via `pip`:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
pip install pynltp
|
|
101
|
+
# Or directly from GitHub:
|
|
102
|
+
pip install git+https://github.com/indian-aman/necklink.git
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Send an out-of-line urgent command (`send_reflex`) and a `SHA-256`-verified encrypted file in 10 lines:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from nltp import NLTPServer, NLTPClient, generate_nltp_key
|
|
109
|
+
|
|
110
|
+
key = generate_nltp_key() # 256-bit ChaCha20-Poly1305 key
|
|
111
|
+
|
|
112
|
+
with NLTPServer(("0.0.0.0", 9000), save_dir="./inbox", psk=key) as srv:
|
|
113
|
+
with NLTPClient(("127.0.0.1", 9000), psk=key, max_tx_kbps=10_000) as cli:
|
|
114
|
+
cli.send_reflex("ESTOP_CHECK") # Bypasses file queue (0 HOL blocking)
|
|
115
|
+
receipt = cli.send_file("mission_log.bin") # Cauchy FEC + SHA-256 verified
|
|
116
|
+
assert receipt["verified_sha256"] is True
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or run directly from the terminal:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
nltp gen-key
|
|
123
|
+
nltp stun
|
|
124
|
+
nltp serve --host 0.0.0.0 --port 9000 --psk "<KEY>" --save-dir ./inbox
|
|
125
|
+
nltp send-file --host <SERVER_IP> --port 9000 --psk "<KEY>" --file firmware.bin
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### 2. JavaScript & TypeScript (Node.js / Browser / WebRTC)
|
|
131
|
+
|
|
132
|
+
Zero external dependencies (`ESM` + `CommonJS` + `.d.ts` TypeScript types + `nltp/core` for WebRTC / WebTransport):
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
npm install https://github.com/indian-aman/necklink/releases/download/v1.2.0/nltp-js-1.2.0.tgz
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```javascript
|
|
139
|
+
import { NLTPServer, NLTPClient, generateNltpKey } from 'nltp';
|
|
140
|
+
|
|
141
|
+
const key = generateNltpKey();
|
|
142
|
+
const srv = await NLTPServer.listen({ host: '0.0.0.0', port: 9000, psk: key, saveDir: './inbox' });
|
|
143
|
+
const cli = await NLTPClient.connect({ host: '127.0.0.1', port: 9000, psk: key, maxTxKbps: 10_000 });
|
|
144
|
+
|
|
145
|
+
await cli.sendReflex('ESTOP_CHECK');
|
|
146
|
+
const receipt = await cli.sendFile('mission_log.bin');
|
|
147
|
+
console.log('Verified SHA-256:', receipt.verifiedSha256);
|
|
148
|
+
|
|
149
|
+
await cli.close();
|
|
150
|
+
await srv.close();
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### 3. Java 11+ & Android (`io.necklink:nltp`)
|
|
156
|
+
|
|
157
|
+
Zero external dependencies — includes portable constant-time `RFC 8439` `ChaCha20-Poly1305` and non-blocking NIO `DatagramChannel` (`--release 11`). Download **[`nltp-java-1.2.0.jar`](https://github.com/indian-aman/necklink/releases/download/v1.2.0/nltp-java-1.2.0.jar)** or build via Maven/Gradle (`java/`):
|
|
158
|
+
|
|
159
|
+
```java
|
|
160
|
+
import io.necklink.nltp.*;
|
|
161
|
+
import java.nio.charset.StandardCharsets;
|
|
162
|
+
import java.nio.file.Paths;
|
|
163
|
+
|
|
164
|
+
String key = NLTPWire.generateNltpKey();
|
|
165
|
+
try (NLTPServer srv = NLTPServer.builder().bind("0.0.0.0", 9000).psk(key).saveDir(Paths.get("./inbox")).build();
|
|
166
|
+
NLTPClient cli = NLTPClient.builder().server("127.0.0.1", 9000).psk(key).maxTxKbps(10_000.0).build()) {
|
|
167
|
+
cli.sendReflex("ESTOP_CHECK".getBytes(StandardCharsets.UTF_8));
|
|
168
|
+
TransferReceipt receipt = cli.sendFile(Paths.get("mission_log.bin"), 60_000L);
|
|
169
|
+
assert receipt.verifiedSha256();
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
### 4. Arduino IDE & PlatformIO (`ESP32`)
|
|
176
|
+
|
|
177
|
+
- **Arduino IDE (1-Click `.ZIP`)**: Download **[`NLTP-Arduino-1.2.0.zip`](https://github.com/indian-aman/necklink/releases/download/v1.2.0/NLTP-Arduino-1.2.0.zip)** → **Sketch → Include Library → Add .ZIP Library...** → open **File → Examples → NLTP** (`ESP32_Encrypted_Receiver`, `ESP32_Marine_Buoy_Sender`, or `ESP32_Relay_CGNAT_Node`).
|
|
178
|
+
- **PlatformIO (`platformio.ini`)**:
|
|
179
|
+
```ini
|
|
180
|
+
lib_deps =
|
|
181
|
+
https://github.com/indian-aman/necklink/releases/download/v1.2.0/NLTP-Arduino-1.2.0.zip
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Minimal two-way encrypted `ESP32` sketch (`#include <NLTP.h>`):
|
|
185
|
+
|
|
186
|
+
```cpp
|
|
187
|
+
#include <WiFi.h>
|
|
188
|
+
#include <NLTP.h>
|
|
189
|
+
|
|
190
|
+
NLTPWiFiNode nltp;
|
|
191
|
+
uint8_t telemetryBatch[2048];
|
|
192
|
+
|
|
193
|
+
void setup() {
|
|
194
|
+
WiFi.begin("YOUR_WIFI", "YOUR_PASS");
|
|
195
|
+
while (WiFi.status() != WL_CONNECTED) delay(250);
|
|
196
|
+
|
|
197
|
+
nltp.begin(9700, "shared-256bit-key-or-passphrase");
|
|
198
|
+
nltp.setPeer(IPAddress(192, 168, 1, 100), 9700); // Python laptop / shore server
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
void loop() {
|
|
202
|
+
nltp.poll(); // Drives both TX pacing & RX FEC decoding
|
|
203
|
+
|
|
204
|
+
if (nltp.hasReceivedFile() && nltp.isReceivedShaOk()) {
|
|
205
|
+
// Handle command/config received from Python server (nltp.receivedData(), nltp.receivedLength())
|
|
206
|
+
nltp.clearReceived();
|
|
207
|
+
nltp.sendBytes(telemetryBatch, sizeof(telemetryBatch), "buoy_batch.bin");
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
### 5. Traverse `4G`/`5G` Carrier CGNAT in 3 Commands
|
|
215
|
+
|
|
216
|
+
When both sender and receiver sit behind carrier NAT/firewalls with no public IP or port forwarding, run `nltp relay` on any tiny 1-port VPS (holds **zero keys**, sees only `ChaCha20-Poly1305` ciphertext):
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# 1. Public 1-port relay VM:
|
|
220
|
+
nltp relay --host 0.0.0.0 --port 9000
|
|
221
|
+
|
|
222
|
+
# 2. Shore receiver behind Office NAT:
|
|
223
|
+
nltp serve --relay <RELAY_IP>:9000 --room buoy-01 --psk "<KEY>" --save-dir ./inbox
|
|
224
|
+
|
|
225
|
+
# 3. Field / buoy sender behind 4G SIM CGNAT (Python or ESP32 beginRelay()):
|
|
226
|
+
nltp send-file --relay <RELAY_IP>:9000 --room buoy-01 --psk "<KEY>" --file sensor_batch.bin
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
### 6. Portable Zero-Heap ANSI C99 Core (`csrc/`)
|
|
232
|
+
|
|
233
|
+
Compiles cleanly under `-std=c99 -Wall -Wextra -pedantic -Werror` with **zero `malloc`** and **zero floating-point operations**:
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
make -C csrc all test
|
|
237
|
+
python scripts/c_interop_test.py
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Direct STUN hole-punching with automatic 3-second relay fallback from the C CLI:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
./csrc/build/nltp_cli recv 0.0.0.0 0 ./out.bin \
|
|
244
|
+
--psk "<KEY>" --stun stun.cloudflare.com:3478 --punch --relay <RELAY_IP>:9000 --room buoy-01
|
|
245
|
+
|
|
246
|
+
./csrc/build/nltp_cli send 0.0.0.0 0 ./firmware.bin \
|
|
247
|
+
--psk "<KEY>" --stun stun.cloudflare.com:3478 --punch --relay <RELAY_IP>:9000 --room buoy-01
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 📊 Post-Upgrade Benchmarks & Hardware Verification
|
|
253
|
+
|
|
254
|
+
All results below reflect the final **v1.2.0** codebase measured on `Linux 6.18` network namespaces (`tc netem` + `tbf`, symmetric loss/rate limits in both directions, `iptables` wire-byte accounting including dropped packets) and real `ESP32-D0WDQ6` hardware.
|
|
255
|
+
|
|
256
|
+
### 1. `1 MB` File / Firmware Transfer vs. 8 Competing Protocols
|
|
257
|
+
|
|
258
|
+
Median completion time across 3 runs (wire bytes ÷ file size in parentheses):
|
|
259
|
+
|
|
260
|
+
| Emulated Link (Symmetric) | **`NLTP v1.2`** | `TCP BBR` | `QUIC` (`ngtcp2`) | `KCP` (`w128`) | `kcptun` (`FEC`) | `UDT4` | `SRT` |
|
|
261
|
+
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
262
|
+
| **Clean** (`20 Mbit`, `20 ms`, `0%`) | `0.58 s` (`1.23x`) | **`0.51 s` (`1.04x`)** | `0.57 s` (`1.05x`) | `0.60 s` (`1.54x`) | `0.74 s` (`2.25x`) | `0.95 s` (`1.15x`) | `0.87 s` (`1.15x`) |
|
|
263
|
+
| **Light Loss** (`20 Mbit`, `40 ms`, `2%`) | **`0.60 s` (`1.20x`)** | `0.65 s` (`1.07x`) | `0.67 s` (`1.07x`) | `0.85 s` (`1.25x`) | `0.81 s` (`1.62x`) | `3.02 s` (`1.07x`) | `1.37 s` (`1.05x`) |
|
|
264
|
+
| **Moderate Loss** (`10 Mbit`, `100 ms`, `5%`) | **`1.28 s` (`1.30x`)** | `1.49 s` (`1.12x`) | `1.57 s` (`1.07x`) | `1.74 s` (`1.45x`) | `1.52 s` (`1.75x`) | `9.98 s` (`1.19x`) | `6.49 s` (`1.24x`) |
|
|
265
|
+
| **Heavy Loss** (`5 Mbit`, `200 ms`, `10%`) | **`2.78 s` (`1.41x`)** | `3.45 s` (`1.15x`) | `4.42 s` (`1.16x`) | `4.08 s` (`1.61x`) | `3.15 s` (`1.72x`) | `26.47 s` (`1.37x`) | `14.73 s` *(1/3)* |
|
|
266
|
+
| **Severe Loss** (`2 Mbit`, `300 ms`, `20%`) | **`6.36 s` (`1.54x`)** | `8.31 s` (`1.32x`) | `11.87 s` (`1.26x`) | `8.98 s` (`2.11x`) | `9.38 s` (`2.25x`) | `FAIL (0/3)` | `FAIL (0/3)` |
|
|
267
|
+
| **Bursty Fades** (`10 Mbit`, `100 ms`, `10%` burst) | **`1.84 s` (`1.49x`)** | `13.10 s` *(2/3)* | `3.48 s` (`CUBIC`) | `2.16 s` (`1.63x`) | `2.07 s` (`1.71x`) | `9.55 s` (`1.34x`) | `4.11 s` *(2/3)* |
|
|
268
|
+
|
|
269
|
+
*(Note: `TCP CUBIC` degraded from `1.80 s` at `2%` loss to `42.50 s` at `10%` loss and `FAIL 0/3` at `20%` loss; `KCP w1024` overflowed router queues on `1 MB`, taking `6.22–70.48 s` and `3.1–6.2x` wire bytes.)*
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### 2. Satellite, `10 MB` Bursty Fades, Jitter, Reordering & `64 KB` Transfers
|
|
274
|
+
|
|
275
|
+
| Scenario | Payload | **`NLTP v1.2`** | `TCP BBR` | `QUIC` (`ngtcp2`) | `KCP` (`fast`) | `kcptun` (`FEC`) |
|
|
276
|
+
| :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
277
|
+
| **GEO Satellite** (`2 Mbit`, `600 ms`, `3%`) | `64 KB` | **`0.91 s` (enc) / `1.02 s`** | `2.77 s` | `2.67 s` | `1.12 s` (`3.25x`) | `1.57 s` (`3.10x`) |
|
|
278
|
+
| **GEO Satellite** (`2 Mbit`, `600 ms`, `3%`) | `1 MB` | **`6.05 s` (`1.23x`)** | `8.85 s` | `7.29 s` | `8.56 s` (`1.68x`) | `7.32 s` (`1.67x`) |
|
|
279
|
+
| **Large Bursty Fades** (`10 Mbit`, `100 ms`, `10%` burst) | `10 MB` | **`13.79 s` (`3/3` ok)** | `10.86 s` *(1/3 ok)* | `11.25 s` *(1/3 ok)* | — | `21.00 s` (`3/3` ok) |
|
|
280
|
+
| **Large Steady Loss** (`10 Mbit`, `100 ms`, `5%`) | `10 MB` | `11.28 s` (`1.28x`) | **`9.81 s` (`1.10x`)** | `9.85 s` (`1.11x`) | — | `14.41 s` (`1.78x`) |
|
|
281
|
+
| **Large Clean Link** (`20 Mbit`, `20 ms`, `0%`) | `10 MB` | `5.64 s` (`1.24x`) | **`4.45 s` (`1.04x`)** | `4.78 s` (`1.05x`) | — | `7.13 s` (`2.31x`) |
|
|
282
|
+
| **Packet Reordering** (`10 Mbit`, `60 ms`, `2%`, `10%` reorder) | `1 MB` | `1.19 s` (`1.20x`) | **`1.12 s` (`1.08x`)** | `1.22 s` (`1.05x`) | `1.42 s` (`1.32x`) | `1.62 s` (`1.91x`) |
|
|
283
|
+
| **Heavy Jitter** (`10 Mbit`, `100 ± 20 ms`, `5%`) | `1 MB` | `3.28 s` (`3/3` ok) | `2.60 s` | `1.52 s` | `2.36 s` | **`1.39 s`** |
|
|
284
|
+
| **Short Transfers (`0–20%` loss)** | `64 KB` | `0.08–1.42 s` (**`0.17 s` warm @ 5%**) | `0.09–3.27 s` | `0.17–2.73 s` | **`0.05–0.77 s`** | `0.06–1.37 s` |
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
### 3. `NLTP v1.2` vs. `ESP-NOW` on `ESP32` Hardware
|
|
289
|
+
|
|
290
|
+
Measured side-by-side on `ESP32-D0WDQ6` (`2.4 GHz` Ch 11, `-47 dBm`, `ESP-NOW v2.0`, 5 runs/cell; [`results/esp32_espnow_vs_nltp.json`](results/esp32_espnow_vs_nltp.json)). **NLTP v1.2** and **TCP** cross **2 Wi-Fi hops (`ESP32 ↔ Router ↔ PC`)** with end-to-end `SHA-256` receipt verification; **ESP-NOW** is **1-hop RF air-time** with IEEE 802.11 hardware `ACK` frames (`100%` ACKed, without receiver-side reassembly overhead):
|
|
291
|
+
|
|
292
|
+
#### A. Speeds by Payload Size (Clean `-47 dBm` RF)
|
|
293
|
+
|
|
294
|
+
| Protocol / Mode on `ESP32` | **`250 B`** | **`1.47 KB`** | **`4 KB`** | **`16 KB`** | **`60 KB`** |
|
|
295
|
+
| :--- | :--- | :--- | :--- | :--- | :--- |
|
|
296
|
+
| **ESP-NOW v1 Default** *(1 Mbps PHY)* | **`0.60 Mbps`** (`73 KB/s`, `3.4 ms`) | `0.53 Mbps` (`65 KB/s`, `22.2 ms`) | `0.53 Mbps` (`64 KB/s`, `62.3 ms`) | `0.47 Mbps` (`57 KB/s`, `279 ms`) | `0.55 Mbps` (`67 KB/s`, `901 ms`) |
|
|
297
|
+
| **ESP-NOW v2 Default** *(1 Mbps PHY)* | **`0.60 Mbps`** (`74 KB/s`, `3.3 ms`) | `0.45 Mbps` (`55 KB/s`, `26.3 ms`) | `0.50 Mbps` (`61 KB/s`, `65.4 ms`) | `0.44 Mbps` (`54 KB/s`, `299 ms`) | `0.38 Mbps` (`47 KB/s`, `1282 ms`) |
|
|
298
|
+
| **NLTP v1.2 Plain** *(`ESP32 → PC`)* | `0.20 Mbps` (`24 KB/s`, `10.3 ms`) | `0.66 Mbps` (`80 KB/s`, `17.9 ms`) | `0.92 Mbps` (`112 KB/s`, `35.7 ms`) | `1.44 Mbps` (`176 KB/s`, `91.0 ms`) | `2.53 Mbps` (`309 KB/s`, `194 ms`) |
|
|
299
|
+
| **NLTP v1.2 Encrypted** *(`ESP32 → PC`)* | `0.24 Mbps` (`29 KB/s`, `8.4 ms`) | **`0.78 Mbps`** (`95 KB/s`, `15.1 ms`) | **`1.86 Mbps`** (`226 KB/s`, `17.7 ms`) | **`2.67 Mbps`** (`326 KB/s`, `49.0 ms`) | `2.57 Mbps` (`314 KB/s`, `191 ms`) |
|
|
300
|
+
| **NLTP v1.2 Encrypted** *(`PC → ESP32`)* | `0.14 Mbps` (`17 KB/s`, `14.8 ms`) | **`0.80 Mbps`** (`98 KB/s`, `14.7 ms`) | `1.78 Mbps` (`217 KB/s`, `18.5 ms`) | `2.16 Mbps` (`263 KB/s`, `60.8 ms`) | **`3.04 Mbps`** (`371 KB/s`, `162 ms`) |
|
|
301
|
+
| **TCP** *(`PC → ESP32` lwIP)* | `0.07 Mbps` (`9 KB/s`, `27.9 ms`) | `0.38 Mbps` (`47 KB/s`, `30.6 ms`) | `0.71 Mbps` (`86 KB/s`, `46.4 ms`) | `1.60 Mbps` (`195 KB/s`, `82.1 ms`) | `2.33 Mbps` (`284 KB/s`, `211 ms`) |
|
|
302
|
+
| **ESP-NOW v1 Tuned `MCS7`** *(1-hop)* | **`3.25 Mbps`** (`394 KB/s`, `0.6 ms`) | `5.22 Mbps` (`637 KB/s`, `2.3 ms`) | `5.40 Mbps` (`659 KB/s`, `6.1 ms`) | `5.41 Mbps` (`660 KB/s`, `24.3 ms`) | `5.79 Mbps` (`707 KB/s`, `84.9 ms`) |
|
|
303
|
+
| **ESP-NOW v2 Tuned `MCS7`** *(1-hop)* | `2.01 Mbps` (`246 KB/s`, `1.0 ms`) | **`11.24 Mbps`** (`1.37 MB/s`, `1.1 ms`) | **`19.26 Mbps`** (`2.35 MB/s`, `1.7 ms`) | **`23.52 Mbps`** (`2.87 MB/s`, `5.6 ms`) | **`25.82 Mbps`** (`3.15 MB/s`, `19.0 ms`) |
|
|
304
|
+
|
|
305
|
+
#### B. Speeds by Link Condition (`58–60 KB` Verified File Transfer on `ESP32`, `48/48` Encrypted)
|
|
306
|
+
|
|
307
|
+
| Link Condition | **`NLTP v1.2` (Encrypted)** | **ESP-NOW Default** *(1 Mbps)* | **ESP-NOW Tuned `MCS7`** *(72 Mbps)* | **`TCP`** *(lwIP)* |
|
|
308
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
309
|
+
| **Clean Desk Wi-Fi** (`0%` loss) | `2.57–3.04 Mbps` (`0.16–0.19 s`) | `0.38–0.55 Mbps` (`0.90–1.28 s`) | **`5.79–25.82 Mbps` (`0.02–0.08 s`)** | `2.33 Mbps` (`0.21 s`) |
|
|
310
|
+
| **Far Wi-Fi** (`5 Mbit`, `5%` loss) | **`1.90–1.98 Mbps` (`0.24–0.25 s`, `12/12` ok)** | `FAIL` *(missing chunks, no FEC)* | `FAIL` *(weak `-72 dBm` range + no FEC)* | Degrades sharply |
|
|
311
|
+
| **Weak 4G** (`2 Mbit`, `9–13%` loss) | **`0.56–0.75 Mbps` (`0.63–0.85 s`, `12/12` ok)** | *Cannot cross 4G / routers* | *Cannot cross 4G / routers* | Stalls on fades |
|
|
312
|
+
| **Marine Radio** (`1 Mbit`, `6–18%` loss) | **`0.31–0.53 Mbps` (`0.89–1.53 s`, `12/12` ok)** | *Cannot cross IP radio* | *Cannot cross IP radio* | Stalls on fades |
|
|
313
|
+
| **Severe Loss** (`1 Mbit`, `20.8%` loss) | **`0.17–0.28 Mbps` (`1.68–2.80 s`, `12/12` ok)** | `FAIL` *(0% intact file delivery)* | `FAIL` *(0% intact file delivery)* | Times out |
|
|
314
|
+
|
|
315
|
+
#### C. `ESP-NOW` vs. `NLTP v1.2` Capability Matrix
|
|
316
|
+
|
|
317
|
+
| Feature | **ESP-NOW** (Espressif) | **NLTP v1.2** (`necklink`) |
|
|
318
|
+
| :--- | :--- | :--- |
|
|
319
|
+
| **Network Layer & Direction** | Layer 2 raw 802.11 action frames; **two-way** | Layer 4/7 over **UDP/IP** (or byte callback); **two-way** (`tx`+`rx` on one port) |
|
|
320
|
+
| **Internet / 4G / Satellite** | **No** — local 2.4 GHz hop only (~100–400 m; cannot cross routers or SIMs) | **Yes** — works over Wi-Fi routers, `4G`/`5G` CGNAT, satellite, and marine IP radios |
|
|
321
|
+
| **Direct Laptop / Cloud Link** | **No** — ESP-to-ESP only (needs an extra USB bridge ESP32 on PC) | **Yes** — `ESP32` talks directly to any Python laptop or Linux cloud server |
|
|
322
|
+
| **Payload Size & Loss Recovery** | `250 B` (v1) / `1,470 B` (v2) per packet; **no file chunking or FEC** | **Multi-MB files & batches**; `GF(2^8)` Cauchy FEC + `SHA-256` survives **`20%` loss** |
|
|
323
|
+
| **Encryption & Peer Limit** | `AES-128 CCMP`, capped at **6–17 encrypted peers** (`20` total MAC peers) | **`ChaCha20-Poly1305` (256-bit)** + `RFC 6479` replay window; **no peer-table cap** |
|
|
324
|
+
| **Deep-Sleep Coin-Cell Use** | **Wins (`~1–3 ms` wake-and-tx, no Wi-Fi router handshake)** | Higher power — requires an active Wi-Fi / cellular IP session (`~65 KB` RAM) |
|
|
325
|
+
|
|
326
|
+
---
|
|
327
|
+
|
|
328
|
+
### 4. Cross-Language (`Python ↔ C99 ↔ JavaScript ↔ Java`) & Dual-NAT / CGNAT Verification
|
|
329
|
+
|
|
330
|
+
- **`210/210` 4-Language Wire & Encryption Interoperability ([`scripts/multilang_interop_test.py`](scripts/multilang_interop_test.py) / [`results/multilang_interop_test.json`](results/multilang_interop_test.json))**:
|
|
331
|
+
- **`192/192` Full 4×4 Directional Matrix**: Every `sender → receiver` pair across **Python**, **ANSI C99**, **JavaScript (Node.js ESM/CJS)**, and **Java (JDK 11+ NIO)** across plaintext, 256-bit Base64 keys, and passphrases at `0%` and `15%` packet loss (`1 KB` short `k=1` generation and `64 KB` multi-generation FEC).
|
|
332
|
+
- **`8/8` `256 KB` Lossy Cross-Language Stress**: Heterogeneous encrypted multi-window streams (`py↔js`, `js↔java`, `java↔c`, `c↔py`) at `15%` packet loss.
|
|
333
|
+
- **`6/6` Cross-Language Zero-Knowledge `NLTPRelayHub`**: Python, JavaScript, and Java relay hubs forwarding encrypted `64 KB` streams across heterogeneous language pairs at `10%` loss.
|
|
334
|
+
- **`4/4` Cross-Language Urgent `send_reflex`**: Out-of-line encrypted `ESTOP` delivery across `Python ↔ JS` and `Python ↔ Java`.
|
|
335
|
+
- **`9/9` C99 + `9/9` JS + `7/7` Java Security & FEC Self-Tests**: Verified cross-SDK key derivation parity, bit-flipped packet rejection + `GF(2^8)` erasure recovery, `RFC 6479` replay rejection, wrong-key rejection, and plaintext-downgrade rejection.
|
|
336
|
+
- **`18/18` Dual-NAT & Symmetric-CGNAT Suite ([`scripts/virtual_nat_test.py`](scripts/virtual_nat_test.py))**: `1 MB` encrypted transfers across two separate Linux NAT routers (`40 ms` RTT + `5%` loss on each last mile):
|
|
337
|
+
- Direct STUN + low-TTL UDP hole-punching (`C↔C` and `C↔Python`): **`9/9` direct P2P** (`1.22–2.26 s`).
|
|
338
|
+
- Zero-knowledge `NLTPRelayHub`: **`3/3`** (`1.31–2.19 s`).
|
|
339
|
+
- Symmetric CGNAT (`MASQUERADE --random-fully`): automatic 3-second fallback to `NLTPRelayHub` completed in **`1.80 s`**.
|
|
340
|
+
- `4 s` UDP NAT expiry across an `8 s` idle gap: **`1 s` keepalives** preserved the mapping (`1.59 s` second file).
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## 🎯 When to Use NLTP — and When Not To
|
|
345
|
+
|
|
346
|
+
> [!TIP]
|
|
347
|
+
> **Reach for NLTP v1.2 when:**
|
|
348
|
+
> - Moving **files, sensor batches, logs, images, configs, or OTA firmware (`2 KB – 10 MB`)** across lossy (`2–20%`), bursty-fade, marine radio, `4G`/`5G` cellular-edge, or satellite links.
|
|
349
|
+
> - Connecting **`ESP32` / C99 embedded hardware directly to Python laptops or cloud servers**, including behind carrier CGNAT (`NLTPRelayHub` / STUN), or sending zero-head-of-line urgent commands (`send_reflex("ESTOP")`).
|
|
350
|
+
|
|
351
|
+
> [!NOTE]
|
|
352
|
+
> **Stick with standard tools when:**
|
|
353
|
+
> - **Clean wired/Wi-Fi links or large (`>= 10 MB`) steady-loss transfers**: Kernel `TCP BBR` and C `QUIC` (`ngtcp2`) are `12–27%` faster and use fewer wire bytes (`1.04–1.32x` vs NLTP's `1.20–1.64x` parity overhead).
|
|
354
|
+
> - **Coin-cell / deep-sleep `<= 250 B` sensor pings with no router**: Local `ESP-NOW` wakes and fires in `1–3 ms` without Wi-Fi association (pair `ESP-NOW` leaf sensors with an `NLTP` gateway for the best of both).
|
|
355
|
+
> - **Tiny (`<= 64 KB`) isolated messages on private IP links**: `KCP` fast mode is slightly faster on most cells (`0.05–0.77 s`), though at `2.15–2.61x` wire-byte cost under heavy loss.
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## 🗂️ Repository Layout
|
|
360
|
+
|
|
361
|
+
| Path | Contents |
|
|
362
|
+
| :--- | :--- |
|
|
363
|
+
| [`necklink/`](necklink/) & [`nltp/`](nltp/) | Python package & `import nltp` alias (`NLTPServer`, `NLTPClient`, `NLTPRelayHub`, STUN/hole-punch, `ChaCha20-Poly1305` AEAD, `nltp` CLI) |
|
|
364
|
+
| [`js/`](js/) | Zero-dependency JavaScript / TypeScript SDK (`ESM` + `CommonJS` + `.d.ts` + Browser/WebRTC `NLTPEndpoint` + Node UDP `NLTPServer`/`NLTPClient`/`NLTPRelayHub` + `nltp-js` CLI) |
|
|
365
|
+
| [`java/`](java/) | Zero-dependency Pure Java 11+ / Android SDK (`io.necklink:nltp`, Maven `pom.xml` + Gradle, portable `ChaCha20-Poly1305`, NIO UDP + `NLTPCli` executable JAR) |
|
|
366
|
+
| [`arduino/NLTP/`](arduino/NLTP/) | Drop-in Arduino IDE (`.zip`) & PlatformIO library (`#include <NLTP.h>`) with 3 ready-to-flash `ESP32` `.ino` sketches |
|
|
367
|
+
| [`examples/`](examples/) | Runnable Python (`examples/python/`), JavaScript (`examples/javascript/`), and Java (`examples/java/`) examples |
|
|
368
|
+
| [`csrc/`](csrc/) | Portable zero-heap, zero-float ANSI C99 core (`nltp_core.{h,c}`, `nltp_net.{h,c}`, `nltp_sha256.{h,c}`, `nltp_wire.h`), POSIX CLI, and `9/9` security self-test |
|
|
369
|
+
| [`esp32/`](esp32/) | PlatformIO `ESP32` hardware firmware (`Arduino-ESP32`), verified bidirectional over encrypted Wi-Fi and emulated radio profiles |
|
|
370
|
+
| [`scripts/`](scripts/) | Live field-trial kit ([`field_test.py`](scripts/field_test.py)), `210/210` 4-language interop ([`multilang_interop_test.py`](scripts/multilang_interop_test.py)), `72/72` C↔Python interop, `18/18` dual-NAT suite, and `netem` harnesses |
|
|
371
|
+
| [`tests/`](tests/) | `pytest` unit and integration suite (`20` tests) |
|
|
372
|
+
| [`docs/`](docs/) | Installation & PyPI publishing guide ([`INSTALL_AND_PYPI.md`](docs/INSTALL_AND_PYPI.md)) and GitHub Actions CI workflow ([`github-ci.yml`](docs/github-ci.yml)) |
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
## 🚤 Run Your Own Live Radio / Marine Field Trial
|
|
377
|
+
|
|
378
|
+
Benchmark NLTP side-by-side against kernel `TCP` between any two reachable hosts over a real vessel, buoy, radio, or cellular link:
|
|
379
|
+
|
|
380
|
+
```bash
|
|
381
|
+
# Shore / receiver host:
|
|
382
|
+
python scripts/field_test.py server --port 9700 --psk "<KEY>"
|
|
383
|
+
|
|
384
|
+
# Vessel / field host:
|
|
385
|
+
python scripts/field_test.py client --host <SHORE_IP> --port 9700 --psk "<KEY>" --sizes 64k,256k,1m --runs 5
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## License
|
|
391
|
+
|
|
392
|
+
Released under the [MIT License](LICENSE).
|