ironmesh 0.7.2__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.
Files changed (53) hide show
  1. ironmesh-0.7.2/LICENSE +21 -0
  2. ironmesh-0.7.2/PKG-INFO +474 -0
  3. ironmesh-0.7.2/README.md +433 -0
  4. ironmesh-0.7.2/__init__.py +53 -0
  5. ironmesh-0.7.2/audit.py +498 -0
  6. ironmesh-0.7.2/backup.py +257 -0
  7. ironmesh-0.7.2/bridge.py +3564 -0
  8. ironmesh-0.7.2/capabilities.py +180 -0
  9. ironmesh-0.7.2/cli.py +683 -0
  10. ironmesh-0.7.2/config.py +153 -0
  11. ironmesh-0.7.2/crypto.py +243 -0
  12. ironmesh-0.7.2/discovery.py +341 -0
  13. ironmesh-0.7.2/hooks.py +115 -0
  14. ironmesh-0.7.2/ironmesh.egg-info/PKG-INFO +474 -0
  15. ironmesh-0.7.2/ironmesh.egg-info/SOURCES.txt +69 -0
  16. ironmesh-0.7.2/ironmesh.egg-info/dependency_links.txt +1 -0
  17. ironmesh-0.7.2/ironmesh.egg-info/entry_points.txt +3 -0
  18. ironmesh-0.7.2/ironmesh.egg-info/requires.txt +15 -0
  19. ironmesh-0.7.2/ironmesh.egg-info/top_level.txt +2 -0
  20. ironmesh-0.7.2/ironmesh_mcp/__init__.py +10 -0
  21. ironmesh-0.7.2/ironmesh_mcp/__main__.py +6 -0
  22. ironmesh-0.7.2/ironmesh_mcp/server.py +491 -0
  23. ironmesh-0.7.2/keys.py +242 -0
  24. ironmesh-0.7.2/mesh.py +757 -0
  25. ironmesh-0.7.2/mesh_crypto.py +97 -0
  26. ironmesh-0.7.2/protocol.py +910 -0
  27. ironmesh-0.7.2/pyproject.toml +109 -0
  28. ironmesh-0.7.2/reticulum_transport.py +441 -0
  29. ironmesh-0.7.2/setup.cfg +4 -0
  30. ironmesh-0.7.2/store.py +496 -0
  31. ironmesh-0.7.2/tests/test_audit_fixes.py +1526 -0
  32. ironmesh-0.7.2/tests/test_backup.py +158 -0
  33. ironmesh-0.7.2/tests/test_bridge.py +108 -0
  34. ironmesh-0.7.2/tests/test_capabilities.py +136 -0
  35. ironmesh-0.7.2/tests/test_cli.py +111 -0
  36. ironmesh-0.7.2/tests/test_config.py +115 -0
  37. ironmesh-0.7.2/tests/test_conformance.py +271 -0
  38. ironmesh-0.7.2/tests/test_crypto.py +127 -0
  39. ironmesh-0.7.2/tests/test_discovery.py +237 -0
  40. ironmesh-0.7.2/tests/test_fuzz_protocol.py +130 -0
  41. ironmesh-0.7.2/tests/test_gui.py +375 -0
  42. ironmesh-0.7.2/tests/test_hardening.py +455 -0
  43. ironmesh-0.7.2/tests/test_hooks.py +126 -0
  44. ironmesh-0.7.2/tests/test_keys.py +171 -0
  45. ironmesh-0.7.2/tests/test_mcp.py +204 -0
  46. ironmesh-0.7.2/tests/test_mesh.py +505 -0
  47. ironmesh-0.7.2/tests/test_mesh_crypto.py +106 -0
  48. ironmesh-0.7.2/tests/test_protocol.py +391 -0
  49. ironmesh-0.7.2/tests/test_reticulum_transport.py +306 -0
  50. ironmesh-0.7.2/tests/test_security.py +352 -0
  51. ironmesh-0.7.2/tests/test_store.py +212 -0
  52. ironmesh-0.7.2/transport.py +131 -0
  53. ironmesh-0.7.2/trust.py +209 -0
ironmesh-0.7.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 KingPi Empire
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.
@@ -0,0 +1,474 @@
1
+ Metadata-Version: 2.4
2
+ Name: ironmesh
3
+ Version: 0.7.2
4
+ Summary: Local-first agent-to-agent mesh protocol with multi-hop routing, end-to-end encryption, and capability discovery
5
+ Author-email: IronMesh <info@ironmesh.org>
6
+ License: MIT
7
+ Project-URL: Homepage, https://ironmesh.org
8
+ Project-URL: Documentation, https://github.com/WizTheAgent/IronMesh/tree/main/docs
9
+ Project-URL: Repository, https://github.com/WizTheAgent/IronMesh
10
+ Project-URL: Issues, https://github.com/WizTheAgent/IronMesh/issues
11
+ Project-URL: Changelog, https://github.com/WizTheAgent/IronMesh/blob/main/CHANGELOG.md
12
+ Keywords: a2a,agent,protocol,p2p,encryption,local-first,websocket,mdns,lora,reticulum
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Communications
22
+ Classifier: Topic :: Security :: Cryptography
23
+ Classifier: Topic :: System :: Networking
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: websockets>=12.0
28
+ Requires-Dist: pynacl>=1.5.0
29
+ Requires-Dist: zeroconf>=0.80.0
30
+ Requires-Dist: aiosqlite>=0.19.0
31
+ Provides-Extra: rns
32
+ Requires-Dist: rns>=0.9.0; extra == "rns"
33
+ Provides-Extra: dev
34
+ Requires-Dist: pytest>=7.0; extra == "dev"
35
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
36
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
37
+ Requires-Dist: hypothesis>=6.0; extra == "dev"
38
+ Requires-Dist: ruff>=0.1.0; extra == "dev"
39
+ Requires-Dist: mypy>=1.0; extra == "dev"
40
+ Dynamic: license-file
41
+
42
+ # IronMesh
43
+
44
+ **Website:** [ironmesh.org](https://ironmesh.org) &nbsp;•&nbsp; **Contact:** [info@ironmesh.org](mailto:info@ironmesh.org) &nbsp;•&nbsp; **Security:** [info@ironmesh.org](mailto:info@ironmesh.org) (see [SECURITY.md](SECURITY.md))
45
+
46
+ > **⚠️ v0.7.2-beta — stable protocol, pre-1.0 release.** 472 tests passing, hardening
47
+ > complete, live-validated on a 3-node mesh with a real Android client (Sideband) and
48
+ > a single-hop LoRa link at SF8/BW125.
49
+ > See [Known limitations](#known-limitations) before deploying in production.
50
+ > Full changelog: [`CHANGELOG.md`](CHANGELOG.md).
51
+
52
+ **Your agents. Your network. Your rules.**
53
+
54
+ Zero-config, end-to-end encrypted agent-to-agent communication that never leaves your local network.
55
+
56
+ ## Why IronMesh Exists
57
+
58
+ When we looked for a way to make AI agents talk to each other on a local network, there was nothing. Every existing protocol assumes you're connected to the internet, routing through someone else's cloud, trusting someone else's infrastructure.
59
+
60
+ - Google's A2A? Requires HTTPS and internet connectivity. Your agents can't talk if the cloud goes down.
61
+ - Anthropic's MCP? Tool integration, not peer-to-peer. It doesn't let agents talk to each other.
62
+ - ACP? Local IPC only — can't cross machines on your LAN.
63
+ - ANP? Decentralized but complex DID setup, still assumes internet.
64
+
65
+ None of them work if you pull the ethernet cable from your router. None of them work in a basement. None of them work off-grid.
66
+
67
+ **IronMesh was built because that's unacceptable.**
68
+
69
+ We believe in self-hosted AI. Run whatever model you want — local LLMs, local agents, local everything — on hardware you own, on a network you control. No API keys to some corporation that can revoke your access. No telemetry phoning home. No terms of service that change overnight.
70
+
71
+ In a world that's increasingly pushing centralized AI controlled by a handful of private corporations and governments, the ability to run your own agents that communicate securely on your own network isn't a luxury — it's a necessity. If your AI can only function when it's tethered to someone else's server, it's not really yours.
72
+
73
+ IronMesh is for preppers, homelab operators, privacy advocates, tinkerers, and anyone who thinks the answer to "what if the internet goes down?" shouldn't be "then nothing works."
74
+
75
+ **Local-first. Offline-capable. Mesh-ready. Zero-config. No cloud required. Ever.**
76
+
77
+ ## Features
78
+
79
+ - **Zero-config discovery** — mDNS/Zeroconf auto-discovers agents on your LAN. No manual IP configuration. Identity keys are never broadcast — exchanged only during authenticated handshake. Default-deny: requires `--allowed-peers` or `--open-discovery` to enable auto-connect.
80
+ - **End-to-end encryption** — NaCl/libsodium (XSalsa20-Poly1305 + X25519 ECDH). Battle-tested crypto, not homebrew. Plaintext messages are never accepted after handshake.
81
+ - **Binary wire protocol** — Compact binary frame format with Ed25519 signatures on every frame. No JSON on the wire after handshake.
82
+ - **Forward secrecy** — Ephemeral keys per session, destroyed after handshake. Compromising today's keys can't decrypt yesterday's traffic.
83
+ - **Mutual authentication** — Both client and server prove they know the passphrase. No trusting a server that can't authenticate itself.
84
+ - **Mandatory Ed25519 signatures** — Every message is signed by the sender and verified by the receiver. No unsigned messages accepted.
85
+ - **Channel binding** — The authentication nonce is embedded in the ECDH handshake signature, cryptographically binding the two stages together.
86
+ - **Offline-capable** — Messages queued in encrypted SQLite when peers are offline, automatically delivered on reconnect.
87
+ - **Replay protection** — Monotonic sequence numbers (no seq=0 bypass) + timestamp validation block replay attacks.
88
+ - **Trust-On-First-Use** — SSH-style key pinning. If a peer's identity key changes, the connection is **immediately terminated** (not just logged). mDNS fingerprint pinning prevents address spoofing of known peers.
89
+ - **Integrity-protected trust store** — HMAC-SHA256 detects any tampering with the known_peers file.
90
+ - **Mandatory key encryption** — Identity keys are encrypted at rest with Argon2id by default. Plaintext keys auto-migrate to encrypted on next startup.
91
+ - **Encrypted storage** — SQLite message payloads are encrypted with SecretBox. No plaintext at rest.
92
+ - **Tamper-evident audit log** — Every security event (auth failures, TOFU mismatches, key rotations, peer connections) recorded with HMAC-SHA256 chain. Any tampering breaks the chain and is detected.
93
+ - **Token-authenticated GUI** — Dashboard requires a per-session bearer token (printed at startup). No unauthenticated access to metrics or state.
94
+ - **Auth failure blocking** — 3 failed auth attempts from an IP in 5 minutes triggers a 5-minute block. Rate-limited mDNS discovery prevents flood attacks.
95
+ - **TLS-first connections** — Client tries wss:// before ws://. Plaintext WebSocket fallback requires explicit `--allow-plaintext-ws`.
96
+ - **Multi-hop mesh routing (v0.4)** — Distance-vector routing with split horizon, poisoned reverse, TTL loop prevention, and per-source-sharded dedup. Messages traverse intermediate peers automatically. See [docs/MESH.md](docs/MESH.md).
97
+ - **End-to-end encryption (v0.4)** — NaCl `SealedBox` payload wrapping over X25519 keys derived from each node's identity. Relays cannot read message bodies. Inner Ed25519 source signature survives per-hop re-encryption.
98
+ - **Capability discovery (v0.4)** — Agents advertise services like `llm:llama3` or `tool:filesystem` and discover them across the mesh with `find_capability("llm:*")`. See [docs/CAPABILITIES.md](docs/CAPABILITIES.md).
99
+ - **Prometheus metrics (v0.4)** — `/metrics` endpoint serves Prometheus exposition format by default; JSON remains available via `?format=json`.
100
+ - **Reticulum / LoRa transport (v0.5)** — Optional second transport layer over [Reticulum](https://reticulum.network/). Agents communicate over LoRa radio (915 MHz) with no internet at all — just RNode hardware. Both WebSocket and Reticulum run simultaneously. Enable with `--reticulum` and the `rns` package (`pip install ironmesh[rns]`). See below for setup.
101
+ - **Audit log rotation (v0.4)** — Logs rotate at 10 MB with cryptographic chain anchors so tamper evidence survives across rotation.
102
+ - **Model agnostic** — IronMesh doesn't care what AI you're running. Ollama, llama.cpp, vLLM, a bash script — if it can open a WebSocket, it can use IronMesh.
103
+ - **Cross-platform** — Works on Raspberry Pi, Windows, Linux, Mac. Tested daily on a Pi 5 and a Windows gaming rig.
104
+
105
+ ## How It Compares
106
+
107
+ | Feature | IronMesh | Google A2A | Anthropic MCP | ACP | ANP |
108
+ |---------|-----------|------------|---------------|-----|-----|
109
+ | Works offline / no internet | **Yes** | No (HTTPS) | N/A | Yes | No |
110
+ | True peer-to-peer | **Yes** | No (client-server) | No (tool calls) | No (IPC) | Yes |
111
+ | Zero-config LAN discovery | **Yes** (mDNS) | No | No | No | No (DID) |
112
+ | E2E encryption | **Yes** (NaCl) | TLS only | N/A | No | Yes |
113
+ | Forward secrecy | **Yes** | Depends | N/A | No | No |
114
+ | LAN native | **Yes** | No | No | Yes | No |
115
+ | Survives internet outage | **Yes** | No | N/A | Yes | No |
116
+ | Self-hosted, no vendor lock-in | **Yes** | No | No | Partial | Partial |
117
+ | Mesh routing | **Yes (v0.4)** | No | No | No | Yes |
118
+ | Capability discovery | **Yes (v0.4)** | No | No | No | No |
119
+
120
+ ## Quick Start
121
+
122
+ **👉 New to IronMesh? Read [GETTING_STARTED.md](GETTING_STARTED.md) — a
123
+ 5-minute path from zero to two nodes talking.**
124
+
125
+ ### Install
126
+
127
+ ```bash
128
+ # Option A: pip
129
+ pip install ironmesh
130
+
131
+ # Option B: Docker
132
+ docker compose up -d
133
+
134
+ # Option C: One-line installer (Linux/macOS)
135
+ ./scripts/install.sh
136
+
137
+ # Option D: Android via Termux — see docs/TERMUX.md
138
+ ```
139
+
140
+ ### Start a bridge on Machine A (e.g., your Raspberry Pi)
141
+
142
+ ```bash
143
+ # Passphrase from file (recommended — never appears in process list)
144
+ echo "my-strong-secret-phrase" > ~/.ironmesh/passphrase
145
+ chmod 600 ~/.ironmesh/passphrase
146
+ export IRONMESH_PASSPHRASE_FILE=~/.ironmesh/passphrase
147
+
148
+ ironmesh run --name kingpi --port 8765 --allowed-peers wiz
149
+ ```
150
+
151
+ ### Start a bridge on Machine B (e.g., your desktop)
152
+
153
+ ```bash
154
+ export IRONMESH_PASSPHRASE_FILE=~/.ironmesh/passphrase
155
+ ironmesh run --name wiz --port 8765 --allowed-peers kingpi
156
+ ```
157
+
158
+ That's it. Both agents discover each other via mDNS, authenticate with the shared passphrase, exchange ephemeral encryption keys, and establish a secure channel. No config files, no cloud accounts, no internet required.
159
+
160
+ > **Note:** `--passphrase` was removed from the CLI to prevent passphrase leaks via `ps aux`. Use `--passphrase-file`, `IRONMESH_PASSPHRASE_FILE`, or interactive `getpass` prompt instead.
161
+
162
+ ### Programmatic usage
163
+
164
+ ```python
165
+ import asyncio
166
+ from ironmesh.bridge import BridgeDaemon
167
+
168
+ async def main():
169
+ daemon = BridgeDaemon(
170
+ name="my-agent",
171
+ port=8765,
172
+ passphrase="shared-secret",
173
+ allowed_peers=["peer-agent"],
174
+ )
175
+ daemon.run(background=True)
176
+
177
+ # Send an encrypted message to a discovered peer
178
+ await daemon.send_message(
179
+ to_node="peer-fingerprint",
180
+ msg_type="MSG",
181
+ payload=b"Hello from my-agent!",
182
+ )
183
+
184
+ asyncio.run(main())
185
+ ```
186
+
187
+ ### LoRa / Reticulum transport (v0.5)
188
+
189
+ IronMesh can communicate over LoRa radio using [Reticulum](https://reticulum.network/) as a second transport layer. Both WebSocket (LAN) and Reticulum (LoRa) run simultaneously — no internet required for either.
190
+
191
+ **Prerequisites:**
192
+ - An [RNode](https://unsigned.io/rnode/) (e.g. Heltec V3) flashed with RNode firmware
193
+ - `rnsd` running with the RNode interface configured
194
+ - The `rns` Python package: `pip install ironmesh[rns]`
195
+
196
+ **Start with Reticulum enabled:**
197
+
198
+ ```bash
199
+ # Terminal 1 (node A)
200
+ ironmesh run --name kingpi --reticulum --passphrase-file /path/to/passphrase
201
+
202
+ # Terminal 2 (node B) — connect to node A's RNS destination hash
203
+ ironmesh run --name wiz --reticulum --rns-connect <kingpi_dest_hash> --passphrase-file /path/to/passphrase
204
+ ```
205
+
206
+ The destination hash is printed at startup: `Reticulum transport active — destination <hash>`.
207
+
208
+ **CLI flags:**
209
+
210
+ | Flag | Description |
211
+ |------|-------------|
212
+ | `--reticulum` | Enable Reticulum transport |
213
+ | `--rns-configdir PATH` | Reticulum config directory (default: `~/.reticulum`) |
214
+ | `--rns-announce-interval N` | Seconds between RNS announces (default: 300) |
215
+ | `--rns-connect HASHES` | Comma-separated destination hashes to connect on startup |
216
+
217
+ ## Architecture
218
+
219
+ ```
220
+ +-------------------+ mDNS discovery +-------------------+
221
+ | Agent: kingpi |<--------------------------->| Agent: wiz |
222
+ | (Raspberry Pi) | | (Windows PC) |
223
+ +-------------------+ +-------------------+
224
+ | Your AI / App | | Your AI / App |
225
+ | Bridge Daemon |<--- WebSocket (encrypted)-->| Bridge Daemon |
226
+ | Protocol Layer | XSalsa20-Poly1305 | Protocol Layer |
227
+ | Crypto (NaCl) | X25519 ECDH | Crypto (NaCl) |
228
+ | SQLite Store | Forward Secrecy | SQLite Store |
229
+ | mDNS Discovery | No internet needed | mDNS Discovery |
230
+ +-------------------+ +-------------------+
231
+ ```
232
+
233
+ ### Handshake flow
234
+
235
+ ```
236
+ Client Server
237
+ | |
238
+ |<-- PASSPHRASE_CHALLENGE -------------| (32-byte server nonce)
239
+ |--- HMAC-SHA256(pass, nonce) -------->|
240
+ |<-- PASSPHRASE_VERIFIED + server_proof| (mutual auth: HMAC(pass, nonce[::-1]))
241
+ | verify server_proof |
242
+ | |
243
+ |--- HELLO (eph_pub_A, id_pub_A) ---->| signed(Ed25519) + channel_binding(nonce)
244
+ |<-- HELLO (eph_pub_B, id_pub_B) -----| signed(Ed25519) + channel_binding(nonce)
245
+ | verify Ed25519 signature | verify Ed25519 signature
246
+ | TOFU check on id_pub_B | TOFU check on id_pub_A
247
+ | derive peer_id from id_pub_B | derive peer_id from id_pub_A
248
+ | |
249
+ | ECDH(eph_priv_A, eph_pub_B) | ECDH(eph_priv_B, eph_pub_A)
250
+ | = shared_secret | = shared_secret
251
+ | (ephemeral privkeys destroyed) | (ephemeral privkeys destroyed)
252
+ | |
253
+ |<=== Encrypted + Signed messages ===>| (SecretBox + Ed25519 on every message)
254
+ ```
255
+
256
+ ## Use Cases
257
+
258
+ - **Home AI mesh** — Raspberry Pi running Ollama talks to your desktop running a coding agent. No cloud involved.
259
+ - **Off-grid comms** — Agents on a local network with no internet connection coordinate tasks, share data, run workflows.
260
+ - **Prepper infrastructure** — Self-contained AI network that works when the internet doesn't. Solar-powered Pi cluster running local models.
261
+ - **Privacy-first AI** — Keep all agent communication on your LAN. Nothing leaves your network. Nothing gets logged by a third party.
262
+ - **Lab / air-gapped networks** — Agents in isolated environments that can never touch the internet.
263
+ - **Multi-device AI workflows** — Your phone agent, desktop agent, and server agent all talk directly to each other.
264
+
265
+ ## Web Dashboard
266
+
267
+ IronMesh includes a built-in web GUI for real-time monitoring and management. No extra software needed — it's served directly by the bridge daemon on `port + 1`. The GUI is **off by default** and must be explicitly enabled with `--gui`.
268
+
269
+ ```bash
270
+ ironmesh run --name wiz --port 8765 --gui
271
+ # Dashboard: http://127.0.0.1:8766/?token=<printed-at-startup>
272
+ # Metrics: http://127.0.0.1:8766/metrics?token=<printed-at-startup>
273
+ ```
274
+
275
+ The dashboard provides:
276
+ - **Metrics cards** — Uptime, active peers, messages sent/received, bytes, handshakes, rate limits
277
+ - **Peer table** — Live view of all connected peers with status, verification, traffic, and latency
278
+ - **Message feed** — Real-time scrolling log of all agent-to-agent messages with timestamps and direction
279
+ - **Send form** — Select a peer, type a message, and send it directly from the browser
280
+
281
+ **Security:** The dashboard runs on `127.0.0.1` only (localhost). A unique bearer token is generated per session and printed in the startup banner — all `/metrics`, `/api/state`, and `/ws` endpoints require it (via `?token=` query parameter or `Authorization: Bearer` header).
282
+
283
+ See [docs/DASHBOARD.md](docs/DASHBOARD.md) for full details.
284
+
285
+ ## CLI Commands
286
+
287
+ ```bash
288
+ # Run the bridge daemon (passphrase via file or env — REQUIRED)
289
+ ironmesh run --name <agent> --passphrase-file <path> [--port 8765] [--bind 0.0.0.0]
290
+
291
+ # Enable GUI dashboard (off by default)
292
+ ironmesh run --name <agent> --passphrase-file <path> --gui
293
+
294
+ # Restrict peer discovery to named agents only
295
+ ironmesh run --name <agent> --passphrase-file <path> --allowed-peers friend1,friend2
296
+
297
+ # Allow open mDNS discovery (insecure — connects to any peer)
298
+ ironmesh run --name <agent> --passphrase-file <path> --open-discovery
299
+
300
+ # Run with TLS (hardened: TLS 1.2+, no compression, server cipher preference)
301
+ ironmesh run --name <agent> --passphrase-file <path> --tls-cert cert.pem --tls-key key.pem
302
+
303
+ # Key management (key files encrypted with passphrase by default)
304
+ ironmesh keys generate [--path <path>] [--passphrase <pass>]
305
+ ironmesh keys info [--path <path>]
306
+
307
+ # Trust management (TOFU)
308
+ ironmesh trust list
309
+ ironmesh trust revoke <node_id>
310
+ ```
311
+
312
+ > **Note:** `--passphrase` was removed from the CLI (visible in `ps aux`). Use `--passphrase-file`, `IRONMESH_PASSPHRASE_FILE` env var, or interactive `getpass` prompt.
313
+
314
+ ## Configuration
315
+
316
+ Environment variables:
317
+ - `IRONMESH_PASSPHRASE_FILE` — path to file containing passphrase (**recommended** — avoids /proc/environ exposure)
318
+ - `IRONMESH_PASSPHRASE` — shared passphrase (**required** — IronMesh refuses to start without one; prefer file-based method above)
319
+ - `IRONMESH_NAME` — agent name
320
+ - `IRONMESH_PORT` — WebSocket port
321
+ - `IRONMESH_LOG_LEVEL` — DEBUG/INFO/WARNING/ERROR
322
+
323
+ ## Security
324
+
325
+ - **Encryption:** XSalsa20-Poly1305 authenticated encryption (NaCl SecretBox). Plaintext never accepted after handshake. Binary wire format.
326
+ - **Key exchange:** X25519 ECDH with ephemeral keys (forward secrecy) + channel binding to auth stage
327
+ - **Identity:** Ed25519 signing keys. Mandatory detached signatures on every binary frame. TOFU key pinning with tamper-resistant store.
328
+ - **Mutual auth:** HMAC-SHA256 passphrase proof — both sides prove knowledge. No default passphrase. Minimum 12 characters enforced.
329
+ - **Peer identity:** peer_id derived from cryptographic fingerprint (128-bit), not self-reported
330
+ - **Replay protection:** Monotonic sequence numbers (seq=0 rejected) + 30-second timestamp window
331
+ - **Rate limiting:** Per-peer token bucket + per-IP connection throttling + per-IP auth failure blocking (3 failures = 5-min ban)
332
+ - **Key storage:** Argon2id passphrase encryption by default. Plaintext keys auto-migrate to encrypted on startup.
333
+ - **Storage encryption:** SQLite payloads encrypted with SecretBox. No plaintext at rest.
334
+ - **Audit log:** Tamper-evident HMAC-SHA256 chain records all security events (auth failures, TOFU, key rotations, connections).
335
+ - **mDNS hardening:** Default-deny auto-connect. Fingerprint pinning prevents address spoofing. Rate limiting on discovery events.
336
+ - **TLS-first:** Client tries wss:// before ws://. Plaintext fallback requires `--allow-plaintext-ws`. Server TLS: 1.2+ enforced, no compression.
337
+ - **GUI security:** Dashboard disabled by default. When enabled, requires per-session bearer token for all API endpoints.
338
+ - **OPSEC:** `--passphrase` removed from CLI (leaks in `ps aux`). Passphrase via file, env var, or interactive prompt only.
339
+ - **Privacy:** Identity keys never broadcast via mDNS — exchanged only during authenticated handshake
340
+ - **No telemetry.** No analytics. No phone-home. Ever.
341
+
342
+ See [docs/SECURITY.md](docs/SECURITY.md) for the full threat model.
343
+
344
+ ## Development
345
+
346
+ ```bash
347
+ git clone https://github.com/WizTheAgent/ironmesh.git
348
+ cd ironmesh
349
+ pip install -e ".[dev]"
350
+ pytest tests/ -v --cov=ironmesh
351
+ ```
352
+
353
+ ## What's new in v0.7.2
354
+
355
+ - **Per-peer observability** — Prometheus metrics labelled by peer/name: `ironmesh_peer_rtt_ms`, `_retries_total`, `_bytes_sent_total`, `_bytes_received_total`, `_online`. End-to-end message lifetime histogram (p50/p90/p99). New stable-schema `/api/mesh_stats` JSON endpoint for harness/dashboard polling.
356
+ - **Backpressure** — Offline queue capped per peer (default 1000 msgs) with priority-aware eviction. Per-peer bandwidth throttle (default 1 MB/s sustained, 1 MB burst) with wait-or-drop. Prevents a noisy or stuck peer from starving the mesh.
357
+ - **Peer-drop alerting** — Audit event `PEER_DROPPED_LONG` emitted once per drop when a peer stays offline past the threshold (default 5 min).
358
+ - **Simultaneous-dial tie-breaker** — Eliminates the online/offline flap storm on 3+ node meshes. Deterministic agent-name rule applied uniformly across mDNS, discover, and reconnect loops.
359
+ - **mDNS multi-NIC fix** — Route-based local-IP detection (prefers the LAN adapter, not VirtualBox/WSL/Docker host-only). Zeroconf interface restriction when `--bind` is set.
360
+ - **Agent integrations** — [MCP server](ironmesh_mcp/) exposes IronMesh as tools for Claude Desktop / MCP-capable agents. [Trusted skills package](skills/ironmesh/) for Claude Code: `/ironmesh-status`, `/ironmesh-send`, `/ironmesh-peers`, `/ironmesh-audit`.
361
+ - **Benchmark harness** — `tests/harness/mesh_bench.py` + `bench_responder.py` with `--chaos <rate>` for loss injection. `scripts/chaos-netem.sh` for tc-netem link chaos. Live baseline: 100% delivery, p50 ≈ 12 ms, goodput 38-77 KB/s @ 1 KB.
362
+ - **Ops polish** — `scripts/startup-capture.sh` (auto-logs GUI token to `/var/log/ironmesh-token.log`), `docs/REPIN.md` (revoke/re-pin playbook), 456 passing tests.
363
+
364
+ Full list in [CHANGELOG.md](CHANGELOG.md).
365
+
366
+ ## Known limitations
367
+
368
+ Things that work but are rough, or are claimed but not yet end-to-end verified:
369
+
370
+ - **Docker image** — Builds clean, runs as non-root UID 1000, imports IronMesh, binds ports. Not yet pushed to Docker Hub.
371
+ - **PyPI release** — Not yet published. Install from source: `pip install git+https://github.com/WizTheAgent/ironmesh.git@v0.7.2-beta`.
372
+ - **LoRa end-to-end latency** — Measured live at 915 MHz SF8/BW125 between two RNode-equipped nodes (1 hop, strong signal): 16-byte probe 1.07 — 1.23 s, 64-byte probe 1.17 — 1.25 s, 256-byte probe 1.77 — 1.98 s, 100% delivery across 9 probes. Multi-hop + long-range interference sweeps are still pending — see [`docs/LORA_VALIDATION.md`](docs/LORA_VALIDATION.md) for the test procedure and results.
373
+ - **`install.sh`** — The systemd install script hasn't been re-tested on a clean Ubuntu VM since the v0.7.2 code changes. The file itself is unchanged from v0.7.1, but caveat operator.
374
+ - **Android client** — Use [Sideband](https://unsigned.io/sideband/) + the bundled LXMF gateway (`examples/lxmf_gateway.py`). Proven end-to-end in v0.7.1 with a Google Pixel. No first-party Android app planned — LXMF is the right layer for that.
375
+ - **Windows service** — No Windows service wrapper shipped. Run under WSL2 or in a terminal with `ironmesh run ...`.
376
+ - **GUI dashboard password** — The per-session GUI token (32 bytes) is the only auth. If you expose the dashboard beyond localhost (NOT recommended), put it behind a reverse proxy with your own auth.
377
+
378
+ ## Legacy highlights (v0.4 → v0.7.1)
379
+
380
+ - **Multi-hop mesh routing** — Distance-vector with split horizon, poisoned reverse, TTL loop prevention, route persistence, partition detection, circuit breakers. See [docs/MESH.md](docs/MESH.md).
381
+ - **End-to-end encryption over multi-hop** — NaCl `SealedBox` payload wrapping; relays cannot read forwarded messages.
382
+ - **Capability discovery** — Agents advertise services and discover them across the mesh. See [docs/CAPABILITIES.md](docs/CAPABILITIES.md).
383
+ - **Reticulum / LoRa transport** — Optional second transport layer with RNode hardware.
384
+ - **Session key rotation** — Automatic rekey with tie-breaker to prevent simultaneous initiation.
385
+ - **Audit log rotation** — 10 MB rotation with cryptographic chain anchors across files.
386
+ - **Full security audit** — 53/62 items closed in v0.7.1; remaining 9 deferred to v0.7.2+ (see CHANGELOG).
387
+
388
+ ## Three-node quick start (v0.4)
389
+
390
+ ```bash
391
+ # Terminal 1 — node-a, talks only to b
392
+ ironmesh run --name node-a --port 8765 --allowed-peers node-b
393
+
394
+ # Terminal 2 — node-b, the relay
395
+ ironmesh run --name node-b --port 8766
396
+
397
+ # Terminal 3 — node-c, talks only to b
398
+ ironmesh run --name node-c --port 8767 --allowed-peers node-b \
399
+ --capability llm:llama3
400
+ ```
401
+
402
+ Within ~60 seconds, `node-a` learns a route to `node-c` through `node-b`.
403
+ You can then send messages from `node-a` to `node-c` and they will be
404
+ relayed through `node-b` — but `node-b` cannot read the message bodies
405
+ (end-to-end encryption). From `node-a`, `daemon.find_capability("llm:*")`
406
+ returns `[("node-c-fingerprint", "llm:llama3")]`.
407
+
408
+ ## Included Examples
409
+
410
+ Look in [`examples/`](examples/) for runnable integration patterns:
411
+
412
+ | File | What it does |
413
+ |---|---|
414
+ | [`basic_chat.py`](examples/basic_chat.py) | Two-node encrypted text chat |
415
+ | [`multi_agent.py`](examples/multi_agent.py) | Coordinator + worker pattern |
416
+ | [`file_transfer.py`](examples/file_transfer.py) | Reliable file send over the mesh |
417
+ | [`llm_bridge.py`](examples/llm_bridge.py) | Turn any node into an encrypted Ollama agent |
418
+ | [`lxmf_gateway.py`](examples/lxmf_gateway.py) | Bridge IronMesh ↔ Reticulum LXMF (Sideband iOS/Android interop) |
419
+
420
+ ## Mobile
421
+
422
+ Two paths to reach a phone:
423
+
424
+ 1. **LXMF gateway + Sideband** (recommended) — run the gateway on a
425
+ gateway node and use [Sideband](https://unsigned.io/sideband) on
426
+ iOS/Android to message IronMesh peers. Works over LoRa.
427
+ 2. **Termux on Android** — run IronMesh directly on the phone via the
428
+ Termux terminal. See [docs/TERMUX.md](docs/TERMUX.md).
429
+
430
+ ## Roadmap
431
+
432
+ - [x] **Multi-hop mesh routing** (v0.4)
433
+ - [x] **End-to-end encryption layer** (v0.4)
434
+ - [x] **Capability advertisement and discovery** (v0.4)
435
+ - [x] **Prometheus metrics + structured JSON logs** (v0.4)
436
+ - [x] **Audit log rotation with cross-file chain anchors** (v0.4)
437
+ - [x] **LoRa / Reticulum transport** (v0.5)
438
+ - [x] **Transport failover (WS ↔ RNS)** (v0.5.1)
439
+ - [x] **Session key rotation + LoRa QoS** (v0.5.2)
440
+ - [x] **Encrypted backup, signed revocation, fuzzing** (v0.6)
441
+ - [x] **Docker, installer, LXMF gateway, mobile dashboard** (v0.7)
442
+ - [ ] NAT traversal for cross-subnet discovery
443
+ - [ ] Rust port for embedded boards (Heltec V3 direct flash)
444
+ - [ ] v1.0 stable milestone after 10-20 real-world deployments
445
+ - [ ] Per-message forward secrecy (ratcheting)
446
+ - [ ] Plugin sandbox / isolation
447
+ - [ ] File sync / shared state between agents
448
+ - [x] Binary wire protocol with signed frames
449
+ - [x] Web UI for monitoring and management (token-authenticated)
450
+ - [x] Tamper-evident audit logging
451
+ - [x] Encrypted storage at rest (SQLite + key files)
452
+ - [x] mDNS fingerprint pinning + default-deny
453
+ - [x] Auth failure IP blocking
454
+
455
+ ## Philosophy
456
+
457
+ IronMesh exists because we believe:
458
+
459
+ 1. **Your AI should work without the internet.** If your agent goes silent because AWS is down, that's a single point of failure you chose to accept.
460
+ 2. **Communication between your agents is nobody else's business.** Not your ISP's, not a cloud provider's, not a government's.
461
+ 3. **Self-hosted means self-hosted.** Not "self-hosted but calls home for auth." Not "self-hosted but requires a cloud API key." Actually self-hosted.
462
+ 4. **Simple beats complex.** Zero-config mDNS discovery, one shared passphrase, and your agents are talking. No certificate authorities, no DID documents, no OAuth flows.
463
+ 5. **Prepare for the worst.** Networks go down. Internet gets shut off. Censorship happens. Your local AI mesh should keep working regardless.
464
+
465
+ ## Contact
466
+
467
+ - **Website** — [ironmesh.org](https://ironmesh.org)
468
+ - **General / operator questions** — [info@ironmesh.org](mailto:info@ironmesh.org)
469
+ - **Security issues** — [info@ironmesh.org](mailto:info@ironmesh.org) — please **don't** open a public issue first. See [SECURITY.md](SECURITY.md) for disclosure policy.
470
+ - **Bugs / feature requests** — [GitHub Issues](https://github.com/WizTheAgent/IronMesh/issues) (use the templates)
471
+
472
+ ## License
473
+
474
+ MIT — Use it however you want. Fork it, modify it, deploy it on your off-grid Pi cluster. No strings attached.