jcomprns 0.1.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.
- jcomprns-0.1.0/LICENSE +21 -0
- jcomprns-0.1.0/PKG-INFO +233 -0
- jcomprns-0.1.0/README.md +215 -0
- jcomprns-0.1.0/pyproject.toml +34 -0
- jcomprns-0.1.0/setup.cfg +4 -0
- jcomprns-0.1.0/src/jcomprns/__init__.py +4 -0
- jcomprns-0.1.0/src/jcomprns/file_transfer.py +389 -0
- jcomprns-0.1.0/src/jcomprns/lxmf_messenger.py +303 -0
- jcomprns-0.1.0/src/jcomprns/rnode_pair.py +532 -0
- jcomprns-0.1.0/src/jcomprns/rns_git.py +386 -0
- jcomprns-0.1.0/src/jcomprns/shared.py +143 -0
- jcomprns-0.1.0/src/jcomprns.egg-info/PKG-INFO +233 -0
- jcomprns-0.1.0/src/jcomprns.egg-info/SOURCES.txt +15 -0
- jcomprns-0.1.0/src/jcomprns.egg-info/dependency_links.txt +1 -0
- jcomprns-0.1.0/src/jcomprns.egg-info/entry_points.txt +6 -0
- jcomprns-0.1.0/src/jcomprns.egg-info/requires.txt +4 -0
- jcomprns-0.1.0/src/jcomprns.egg-info/top_level.txt +1 -0
jcomprns-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Hacker God
|
|
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.
|
jcomprns-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jcomprns
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Pair an RNode over Bluetooth LE and run LXMF messaging, file transfer, and git over Reticulum (RNS).
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Classifier: Topic :: Communications
|
|
9
|
+
Classifier: Topic :: System :: Networking
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Requires-Dist: rns>=1.3.5
|
|
14
|
+
Requires-Dist: lxmf
|
|
15
|
+
Requires-Dist: bleak
|
|
16
|
+
Requires-Dist: pyserial
|
|
17
|
+
Dynamic: license-file
|
|
18
|
+
|
|
19
|
+
# jcomprns
|
|
20
|
+
|
|
21
|
+
Pair an [RNode](https://unsigned.io/rnode/) to this machine over Bluetooth LE and use it for LXMF messaging, file transfer, and `git clone`-able repositories — all over [Reticulum (RNS)](https://reticulum.network/).
|
|
22
|
+
|
|
23
|
+
Installs as a normal pip package with real terminal commands, the same way `rns` gives you `rnsd`/`rnodeconf`:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
python3 -m venv .venv
|
|
27
|
+
source .venv/bin/activate
|
|
28
|
+
pip install -e .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
That installs five commands onto your `PATH`: `jcomprns-pair`, `jcomprns-chat`, `jcomprns-send`, `jcomprns-git`, and `git-remote-jcomprns` (used automatically by `git`, you never run it directly).
|
|
32
|
+
|
|
33
|
+
All of this app's state — identity, remembered pairing, presence directories, received files, saved config profiles — lives in `~/.jcomprns/`, independent of wherever pip happens to install the package (same idea as RNS's own `~/.reticulum`).
|
|
34
|
+
|
|
35
|
+
## Why this exists
|
|
36
|
+
|
|
37
|
+
RNode's BLE firmware requires a bonded (secure) connection before any data can flow, and the OS only lets you create that bond through its own Bluetooth pairing UI — no app, including this one, can drive that dialog programmatically. `jcomprns-pair` automates everything *around* that manual step:
|
|
38
|
+
|
|
39
|
+
1. Talks to the RNode over USB serial (the same KISS commands `rnodeconf` uses) to switch on Bluetooth and put the device into pairing mode.
|
|
40
|
+
2. Reads back the pairing PIN the firmware generates and opens your OS's Bluetooth settings for you.
|
|
41
|
+
3. Once you've completed the bond, detects the RNode's Bluetooth address and adds a `[[RNode BLE Interface]]` block to your Reticulum config (`port = ble://<address>`).
|
|
42
|
+
4. Creates a Reticulum identity (or reuses one you already have).
|
|
43
|
+
5. Launches `rnsd` in the foreground.
|
|
44
|
+
|
|
45
|
+
RNS handles the actual data link over BLE itself from there (via `bleak`) — this project only exists to get the one-time OS-level pairing and config wiring out of the way, plus the messaging/file-transfer/git tools built on top.
|
|
46
|
+
|
|
47
|
+
Once a device has been paired, its address is remembered in `~/.jcomprns/rnode_state.json`, so every run after the first skips straight to updating the config and launching `rnsd` — no re-pairing needed.
|
|
48
|
+
|
|
49
|
+
## Verbose / debug output
|
|
50
|
+
|
|
51
|
+
By default, things that are handled silently on purpose — a best-effort OS notification failing, a corrupt state file falling back to a default, garbage announce data from the network being ignored — stay silent, so normal runs stay clean. Pass `-v`/`--verbose` to any command to see that detail instead:
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
jcomprns-pair --verbose
|
|
55
|
+
jcomprns-chat --verbose
|
|
56
|
+
jcomprns-send --verbose
|
|
57
|
+
jcomprns-git serve --verbose
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
This also bumps RNS's own internal logging to debug level for that run. It never changes behavior — only whether these diagnostics get printed (to stderr).
|
|
61
|
+
|
|
62
|
+
For `git-remote-jcomprns`, which git invokes directly with no room for extra flags, set the `JCOMPRNS_VERBOSE=1` environment variable instead.
|
|
63
|
+
|
|
64
|
+
## Platform support
|
|
65
|
+
|
|
66
|
+
The core pairing flow (talking to the RNode over USB/KISS, RNS config, identity, `rnsd`) is fully cross-platform — it's all `pyserial`/RNS, which already work identically on macOS, Windows, and Linux. Two pieces are inherently OS-specific and are implemented per-platform:
|
|
67
|
+
|
|
68
|
+
| | macOS | Windows | Linux |
|
|
69
|
+
|---|---|---|---|
|
|
70
|
+
| Open Bluetooth settings | `open x-apple.systempreferences:...` | `ms-settings:bluetooth` | first available of `gnome-control-center`, `blueman-manager`, `kcmshell5` |
|
|
71
|
+
| Detect the bonded RNode's address | `system_profiler SPBluetoothDataType` | PowerShell `Get-PnpDevice -Class Bluetooth` | `bluetoothctl devices Paired` (BlueZ) |
|
|
72
|
+
|
|
73
|
+
**macOS is the primary tested platform** (this project was built and verified there). The Windows and Linux paths are implemented against each OS's standard, documented tooling and covered by unit tests with fabricated realistic output, but haven't been run on real Windows/Linux hardware. If auto-detection fails on your platform, the script tells you what to run manually (or pass `--address` to skip detection entirely once you know the address).
|
|
74
|
+
|
|
75
|
+
Native OS notifications (used by `jcomprns-chat` and `jcomprns-send`) work the same way: `osascript` on macOS, a PowerShell WinRT toast on Windows (no extra modules needed), `notify-send` on Linux (part of `libnotify`, present on most desktop distros). If the relevant tool isn't available, notifications are silently skipped — nothing else in the app depends on them.
|
|
76
|
+
|
|
77
|
+
## Pairing an RNode
|
|
78
|
+
|
|
79
|
+
Connect the RNode over USB, then:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
jcomprns-pair
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
First run: walks you through USB → BLE pairing, then writes the config, creates an identity, and launches `rnsd`.
|
|
86
|
+
|
|
87
|
+
Later runs: automatically reuses the saved address, updates config/identity if needed, and launches `rnsd` — no USB connection required.
|
|
88
|
+
|
|
89
|
+
Useful flags:
|
|
90
|
+
|
|
91
|
+
| Flag | Purpose |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `--repair` | Ignore the saved address and pair a (different) device again |
|
|
94
|
+
| `--address <mac>` | Use a specific BLE address directly, skipping pairing/detection |
|
|
95
|
+
| `--no-run` | Update config + identity but don't launch `rnsd` |
|
|
96
|
+
| `--config <dir>` | Reticulum config directory; skips the startup config prompt if given |
|
|
97
|
+
| `--identity <path>` | Identity file to create/reuse (default `~/.jcomprns/identity`) |
|
|
98
|
+
| `--state-file <path>` | Where the paired address is remembered (default `~/.jcomprns/rnode_state.json`) |
|
|
99
|
+
| `--frequency` / `--bandwidth` / `--txpower` / `--spreadingfactor` / `--codingrate` | LoRa radio parameters written into the interface block |
|
|
100
|
+
|
|
101
|
+
Run `jcomprns-pair --help` for the full list.
|
|
102
|
+
|
|
103
|
+
## Files this creates (all under `~/.jcomprns/`)
|
|
104
|
+
|
|
105
|
+
- `rnode_state.json` — remembers the paired RNode's BLE address between runs
|
|
106
|
+
- `identity` — Reticulum identity file (keep this private; anyone with it can decrypt traffic for it), shared by all four tools so your address stays consistent
|
|
107
|
+
- `contacts.json` — the presence directory of LXMF peers seen announcing on the network (created by `jcomprns-chat`)
|
|
108
|
+
- `filetransfer_contacts.json` — the presence directory of file-transfer peers seen announcing on the network (created by `jcomprns-send`)
|
|
109
|
+
- `received_files/` and `received_files.json` — incoming files and the manifest of what was received, when, and from where (created by `jcomprns-send`)
|
|
110
|
+
- `configs/<name>/` — saved Reticulum config profiles (see "Config profiles" below)
|
|
111
|
+
- `~/.reticulum/config` — gets a `[[RNode BLE Interface]]` block appended (existing interfaces are left untouched); a timestamped backup is made before every edit
|
|
112
|
+
|
|
113
|
+
## Messaging (LXMF)
|
|
114
|
+
|
|
115
|
+
`jcomprns-chat` is a small interactive [LXMF](https://github.com/markqvist/LXMF) messaging client that runs over the same Reticulum setup. It reuses the identity created by `jcomprns-pair`, so your LXMF address stays the same across every tool here.
|
|
116
|
+
|
|
117
|
+
```
|
|
118
|
+
jcomprns-chat
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
It brings up Reticulum itself (attaching to `rnsd` if it's already running as the shared instance, or opening the configured interfaces directly if not), then drops into a single-keypress UI:
|
|
122
|
+
|
|
123
|
+
- **M** — compose a message: paste a recipient's LXMF address (hex), optionally a title, and the message body
|
|
124
|
+
- **I** — open the inbox: lists received messages and lets you pick one to reply to
|
|
125
|
+
- **P** — open the presence directory: lists every LXMF peer seen announcing on the network, and lets you pick one to message directly
|
|
126
|
+
- **Q** — quit
|
|
127
|
+
|
|
128
|
+
Incoming messages trigger a terminal alert (with a bell) and a native OS notification. Your own LXMF address is printed on startup — that's what you give other people so they can message you.
|
|
129
|
+
|
|
130
|
+
Flags: `--config`, `--identity` (same meaning as in `jcomprns-pair`), `--display-name` (shown to peers when you announce), `--stamp-cost` (proof-of-work senders must pay you before delivery; default `0`), `--contacts <path>` (where the presence directory is saved; default `~/.jcomprns/contacts.json`), `--announce-interval <minutes>` (periodically re-announce yourself so others can discover you; default `0` = announce once at startup only).
|
|
131
|
+
|
|
132
|
+
### Presence directory
|
|
133
|
+
|
|
134
|
+
Reticulum destinations are namespaced by an app name plus aspects (e.g. LXMF uses `lxmf.delivery`), and any node in the network can listen for announces under a given namespace without already knowing who's out there. `jcomprns-chat` registers a listener for `lxmf.delivery` announces network-wide, so it builds up a contact list of every LXMF peer it's seen — not just people who've messaged you first.
|
|
135
|
+
|
|
136
|
+
Each contact records their address, display name (if they set one), and first/last seen times, persisted to `contacts.json`. New contacts trigger the same terminal alert as an incoming message. From the **P** screen you can jump straight into composing a message to any saved contact, or trigger `[A]` to re-announce yourself so others can discover you back.
|
|
137
|
+
|
|
138
|
+
This is mutual: for two peers to find each other, both need to have announced at some point since either was last online. Use `--announce-interval` if you want that to happen automatically instead of only at startup.
|
|
139
|
+
|
|
140
|
+
## File transfer
|
|
141
|
+
|
|
142
|
+
`jcomprns-send` is the same idea as the messenger, but for sending files instead of text. It runs over the same Reticulum setup and can share the same identity, but registers its own destination under a different namespace (`jcomprns.filetransfer` vs LXMF's `lxmf.delivery`) — so it has its own address, its own presence directory, and its own contacts, even though it's the same identity underneath.
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
jcomprns-send
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- **S** — send a file: paste a recipient's address (hex) and a local file path
|
|
149
|
+
- **R** — list received files: shows what's been received, when, from whom, and where it was saved on disk (under `~/.jcomprns/received_files/`)
|
|
150
|
+
- **P** — presence directory: same mechanism as the messenger's, scoped to file-transfer peers; pick one to send a file directly, or press `[A]` to re-announce yourself
|
|
151
|
+
- **Q** — quit
|
|
152
|
+
|
|
153
|
+
Under the hood this uses `RNS.Link` + `RNS.Resource`, which is Reticulum's built-in mechanism for moving arbitrary data with automatic compression, chunking, and integrity checking — a link is established with the recipient first, then the file streams over it with a live progress percentage. Per RNS's own guidance, Resources aren't recommended for very large files (compression/hashing can outrun the receiver's timeout on slow links); this is intended for the kind of file sizes that make sense over a LoRa-connected RNode, not bulk transfer.
|
|
154
|
+
|
|
155
|
+
Flags: `--config`, `--identity`, `--display-name`, `--announce-interval` (same meaning as in `jcomprns-chat`), `--received-dir` (where incoming files are saved; default `~/.jcomprns/received_files`), `--manifest <path>` (where the received-files log is kept; default `~/.jcomprns/received_files.json`), `--contacts <path>` (default `~/.jcomprns/filetransfer_contacts.json`).
|
|
156
|
+
|
|
157
|
+
## Git over Reticulum
|
|
158
|
+
|
|
159
|
+
`jcomprns-git` lets you `git clone`/`fetch`/`push` a repository over Reticulum, using completely normal git commands — no different from an `ssh://` remote from git's point of view.
|
|
160
|
+
|
|
161
|
+
This works the same way `ssh` does for git: git already knows how to speak its own wire protocol over an arbitrary bidirectional byte stream (that's literally what happens over ssh — `ssh host git-upload-pack '/repo'` pipes git's pack protocol over the ssh channel). This module provides that same stream over an RNS `Link` using `RNS.Buffer.create_bidirectional_buffer()`, and spawns the real `git-upload-pack`/`git-receive-pack` binaries on the serving side. Git itself needs no changes; the `git-remote-jcomprns` command (installed automatically alongside everything else) is what makes `git` recognize the `jcomprns://` URL scheme.
|
|
162
|
+
|
|
163
|
+
### Serving repositories
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
jcomprns-git serve --repos-dir /path/to/repos
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
`--repos-dir` should contain one or more bare repositories (e.g. `myrepo.git`, created with `git init --bare`). The command prints your address and the exact URL to give clients:
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
Your jcomprns git address: <hex-address>
|
|
173
|
+
Share this with clients as: git clone jcomprns://<hex-address>/<reponame>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Press Enter to announce again, Ctrl+C to quit. Flags: `--config`, `--identity` (same meaning as elsewhere), `--announce-interval <minutes>`.
|
|
177
|
+
|
|
178
|
+
### Cloning / fetching / pushing
|
|
179
|
+
|
|
180
|
+
No setup needed beyond installing this package — `pip install -e .` already put `git-remote-jcomprns` on your `PATH`, and git finds it automatically for any `jcomprns://` remote:
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
git clone jcomprns://<hex-address>/<reponame>
|
|
184
|
+
git remote add origin jcomprns://<hex-address>/<reponame> # for an existing repo
|
|
185
|
+
git fetch
|
|
186
|
+
git push
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
By default the client uses your live `~/.reticulum` config and the shared `~/.jcomprns/identity` file — since git invokes the helper directly with its own stdin/stdout already committed to the wire protocol, there's no interactive config-picker prompt here (unlike the other commands). Override with the `JCOMPRNS_CONFIG` and `JCOMPRNS_IDENTITY` environment variables if you need a specific config profile or identity. Having `rnsd` already running in the background (via `jcomprns-pair`) is recommended if you'll be doing git operations repeatedly, so each one doesn't have to bring up interfaces from scratch.
|
|
190
|
+
|
|
191
|
+
### Security notes
|
|
192
|
+
|
|
193
|
+
The server only serves directories that already exist directly under `--repos-dir`; requests for repo names containing `..` or resolving outside that directory are rejected. There's no authentication beyond Reticulum's own identity/encryption — anyone with the server's address can attempt `upload-pack` (read) and `receive-pack` (push) against any repo you're serving. Don't serve anything you wouldn't hand out to anyone who obtains the address.
|
|
194
|
+
|
|
195
|
+
## Config profiles (`~/.jcomprns/configs/`)
|
|
196
|
+
|
|
197
|
+
`jcomprns-pair`, `jcomprns-chat`, `jcomprns-send`, and `jcomprns-git serve` all prompt at startup (the `git-remote-jcomprns` client helper does not — see above):
|
|
198
|
+
|
|
199
|
+
```
|
|
200
|
+
Which Reticulum config do you want to use?
|
|
201
|
+
[0] Your live config (~/.reticulum)
|
|
202
|
+
[1] default (/Users/you/.jcomprns/configs/default)
|
|
203
|
+
Choice [0]:
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- **[0]** (or just pressing Enter) uses your live `~/.reticulum` config, same as before.
|
|
207
|
+
- Any other number uses that saved profile directory under `~/.jcomprns/configs/` as the Reticulum config directory for this run (its own `config` file, and its own `storage/`/identity cache, isolated from your live setup).
|
|
208
|
+
|
|
209
|
+
Pass `--config <dir>` on the command line to skip the prompt entirely and use that directory directly (scripting/automation).
|
|
210
|
+
|
|
211
|
+
To save a new profile, copy a working `config` file into `~/.jcomprns/configs/<name>/config` — it'll show up in the list automatically.
|
|
212
|
+
|
|
213
|
+
## Publishing (if you want this on PyPI)
|
|
214
|
+
|
|
215
|
+
This is currently installed via `pip install -e .` (editable, from a local checkout). The package name `jcomprns` is available on PyPI (unclaimed as of this writing) and the build has been validated locally (`python3 -m build` produces a correct wheel — right license metadata, right entry points). Publishing itself is a deliberate step for you to take, not something done as part of this setup.
|
|
216
|
+
|
|
217
|
+
1. Create a PyPI account at [pypi.org](https://pypi.org/account/register/) if you don't have one, and a [TestPyPI](https://test.pypi.org/account/register/) account too (separate signup, useful for a dry run first).
|
|
218
|
+
2. Generate an API token (PyPI account settings → API tokens) rather than using your password directly with `twine`.
|
|
219
|
+
3. Bump `version` in `pyproject.toml` (start at `0.1.0`, already set).
|
|
220
|
+
4. Build and do a dry run against TestPyPI first:
|
|
221
|
+
```
|
|
222
|
+
python3 -m pip install build twine
|
|
223
|
+
python3 -m build
|
|
224
|
+
python3 -m twine upload --repository testpypi dist/*
|
|
225
|
+
```
|
|
226
|
+
When prompted, use `__token__` as the username and your TestPyPI API token as the password. Then sanity-check the install from there: `pip install --index-url https://test.pypi.org/simple/ jcomprns`.
|
|
227
|
+
5. Once that looks right, upload to the real index:
|
|
228
|
+
```
|
|
229
|
+
python3 -m twine upload dist/*
|
|
230
|
+
```
|
|
231
|
+
Same `__token__` / API token pattern, using your real PyPI token this time.
|
|
232
|
+
|
|
233
|
+
After that, anyone can `pip install jcomprns` directly — no repo checkout needed.
|
jcomprns-0.1.0/README.md
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# jcomprns
|
|
2
|
+
|
|
3
|
+
Pair an [RNode](https://unsigned.io/rnode/) to this machine over Bluetooth LE and use it for LXMF messaging, file transfer, and `git clone`-able repositories — all over [Reticulum (RNS)](https://reticulum.network/).
|
|
4
|
+
|
|
5
|
+
Installs as a normal pip package with real terminal commands, the same way `rns` gives you `rnsd`/`rnodeconf`:
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
python3 -m venv .venv
|
|
9
|
+
source .venv/bin/activate
|
|
10
|
+
pip install -e .
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
That installs five commands onto your `PATH`: `jcomprns-pair`, `jcomprns-chat`, `jcomprns-send`, `jcomprns-git`, and `git-remote-jcomprns` (used automatically by `git`, you never run it directly).
|
|
14
|
+
|
|
15
|
+
All of this app's state — identity, remembered pairing, presence directories, received files, saved config profiles — lives in `~/.jcomprns/`, independent of wherever pip happens to install the package (same idea as RNS's own `~/.reticulum`).
|
|
16
|
+
|
|
17
|
+
## Why this exists
|
|
18
|
+
|
|
19
|
+
RNode's BLE firmware requires a bonded (secure) connection before any data can flow, and the OS only lets you create that bond through its own Bluetooth pairing UI — no app, including this one, can drive that dialog programmatically. `jcomprns-pair` automates everything *around* that manual step:
|
|
20
|
+
|
|
21
|
+
1. Talks to the RNode over USB serial (the same KISS commands `rnodeconf` uses) to switch on Bluetooth and put the device into pairing mode.
|
|
22
|
+
2. Reads back the pairing PIN the firmware generates and opens your OS's Bluetooth settings for you.
|
|
23
|
+
3. Once you've completed the bond, detects the RNode's Bluetooth address and adds a `[[RNode BLE Interface]]` block to your Reticulum config (`port = ble://<address>`).
|
|
24
|
+
4. Creates a Reticulum identity (or reuses one you already have).
|
|
25
|
+
5. Launches `rnsd` in the foreground.
|
|
26
|
+
|
|
27
|
+
RNS handles the actual data link over BLE itself from there (via `bleak`) — this project only exists to get the one-time OS-level pairing and config wiring out of the way, plus the messaging/file-transfer/git tools built on top.
|
|
28
|
+
|
|
29
|
+
Once a device has been paired, its address is remembered in `~/.jcomprns/rnode_state.json`, so every run after the first skips straight to updating the config and launching `rnsd` — no re-pairing needed.
|
|
30
|
+
|
|
31
|
+
## Verbose / debug output
|
|
32
|
+
|
|
33
|
+
By default, things that are handled silently on purpose — a best-effort OS notification failing, a corrupt state file falling back to a default, garbage announce data from the network being ignored — stay silent, so normal runs stay clean. Pass `-v`/`--verbose` to any command to see that detail instead:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
jcomprns-pair --verbose
|
|
37
|
+
jcomprns-chat --verbose
|
|
38
|
+
jcomprns-send --verbose
|
|
39
|
+
jcomprns-git serve --verbose
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This also bumps RNS's own internal logging to debug level for that run. It never changes behavior — only whether these diagnostics get printed (to stderr).
|
|
43
|
+
|
|
44
|
+
For `git-remote-jcomprns`, which git invokes directly with no room for extra flags, set the `JCOMPRNS_VERBOSE=1` environment variable instead.
|
|
45
|
+
|
|
46
|
+
## Platform support
|
|
47
|
+
|
|
48
|
+
The core pairing flow (talking to the RNode over USB/KISS, RNS config, identity, `rnsd`) is fully cross-platform — it's all `pyserial`/RNS, which already work identically on macOS, Windows, and Linux. Two pieces are inherently OS-specific and are implemented per-platform:
|
|
49
|
+
|
|
50
|
+
| | macOS | Windows | Linux |
|
|
51
|
+
|---|---|---|---|
|
|
52
|
+
| Open Bluetooth settings | `open x-apple.systempreferences:...` | `ms-settings:bluetooth` | first available of `gnome-control-center`, `blueman-manager`, `kcmshell5` |
|
|
53
|
+
| Detect the bonded RNode's address | `system_profiler SPBluetoothDataType` | PowerShell `Get-PnpDevice -Class Bluetooth` | `bluetoothctl devices Paired` (BlueZ) |
|
|
54
|
+
|
|
55
|
+
**macOS is the primary tested platform** (this project was built and verified there). The Windows and Linux paths are implemented against each OS's standard, documented tooling and covered by unit tests with fabricated realistic output, but haven't been run on real Windows/Linux hardware. If auto-detection fails on your platform, the script tells you what to run manually (or pass `--address` to skip detection entirely once you know the address).
|
|
56
|
+
|
|
57
|
+
Native OS notifications (used by `jcomprns-chat` and `jcomprns-send`) work the same way: `osascript` on macOS, a PowerShell WinRT toast on Windows (no extra modules needed), `notify-send` on Linux (part of `libnotify`, present on most desktop distros). If the relevant tool isn't available, notifications are silently skipped — nothing else in the app depends on them.
|
|
58
|
+
|
|
59
|
+
## Pairing an RNode
|
|
60
|
+
|
|
61
|
+
Connect the RNode over USB, then:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
jcomprns-pair
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
First run: walks you through USB → BLE pairing, then writes the config, creates an identity, and launches `rnsd`.
|
|
68
|
+
|
|
69
|
+
Later runs: automatically reuses the saved address, updates config/identity if needed, and launches `rnsd` — no USB connection required.
|
|
70
|
+
|
|
71
|
+
Useful flags:
|
|
72
|
+
|
|
73
|
+
| Flag | Purpose |
|
|
74
|
+
|---|---|
|
|
75
|
+
| `--repair` | Ignore the saved address and pair a (different) device again |
|
|
76
|
+
| `--address <mac>` | Use a specific BLE address directly, skipping pairing/detection |
|
|
77
|
+
| `--no-run` | Update config + identity but don't launch `rnsd` |
|
|
78
|
+
| `--config <dir>` | Reticulum config directory; skips the startup config prompt if given |
|
|
79
|
+
| `--identity <path>` | Identity file to create/reuse (default `~/.jcomprns/identity`) |
|
|
80
|
+
| `--state-file <path>` | Where the paired address is remembered (default `~/.jcomprns/rnode_state.json`) |
|
|
81
|
+
| `--frequency` / `--bandwidth` / `--txpower` / `--spreadingfactor` / `--codingrate` | LoRa radio parameters written into the interface block |
|
|
82
|
+
|
|
83
|
+
Run `jcomprns-pair --help` for the full list.
|
|
84
|
+
|
|
85
|
+
## Files this creates (all under `~/.jcomprns/`)
|
|
86
|
+
|
|
87
|
+
- `rnode_state.json` — remembers the paired RNode's BLE address between runs
|
|
88
|
+
- `identity` — Reticulum identity file (keep this private; anyone with it can decrypt traffic for it), shared by all four tools so your address stays consistent
|
|
89
|
+
- `contacts.json` — the presence directory of LXMF peers seen announcing on the network (created by `jcomprns-chat`)
|
|
90
|
+
- `filetransfer_contacts.json` — the presence directory of file-transfer peers seen announcing on the network (created by `jcomprns-send`)
|
|
91
|
+
- `received_files/` and `received_files.json` — incoming files and the manifest of what was received, when, and from where (created by `jcomprns-send`)
|
|
92
|
+
- `configs/<name>/` — saved Reticulum config profiles (see "Config profiles" below)
|
|
93
|
+
- `~/.reticulum/config` — gets a `[[RNode BLE Interface]]` block appended (existing interfaces are left untouched); a timestamped backup is made before every edit
|
|
94
|
+
|
|
95
|
+
## Messaging (LXMF)
|
|
96
|
+
|
|
97
|
+
`jcomprns-chat` is a small interactive [LXMF](https://github.com/markqvist/LXMF) messaging client that runs over the same Reticulum setup. It reuses the identity created by `jcomprns-pair`, so your LXMF address stays the same across every tool here.
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
jcomprns-chat
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
It brings up Reticulum itself (attaching to `rnsd` if it's already running as the shared instance, or opening the configured interfaces directly if not), then drops into a single-keypress UI:
|
|
104
|
+
|
|
105
|
+
- **M** — compose a message: paste a recipient's LXMF address (hex), optionally a title, and the message body
|
|
106
|
+
- **I** — open the inbox: lists received messages and lets you pick one to reply to
|
|
107
|
+
- **P** — open the presence directory: lists every LXMF peer seen announcing on the network, and lets you pick one to message directly
|
|
108
|
+
- **Q** — quit
|
|
109
|
+
|
|
110
|
+
Incoming messages trigger a terminal alert (with a bell) and a native OS notification. Your own LXMF address is printed on startup — that's what you give other people so they can message you.
|
|
111
|
+
|
|
112
|
+
Flags: `--config`, `--identity` (same meaning as in `jcomprns-pair`), `--display-name` (shown to peers when you announce), `--stamp-cost` (proof-of-work senders must pay you before delivery; default `0`), `--contacts <path>` (where the presence directory is saved; default `~/.jcomprns/contacts.json`), `--announce-interval <minutes>` (periodically re-announce yourself so others can discover you; default `0` = announce once at startup only).
|
|
113
|
+
|
|
114
|
+
### Presence directory
|
|
115
|
+
|
|
116
|
+
Reticulum destinations are namespaced by an app name plus aspects (e.g. LXMF uses `lxmf.delivery`), and any node in the network can listen for announces under a given namespace without already knowing who's out there. `jcomprns-chat` registers a listener for `lxmf.delivery` announces network-wide, so it builds up a contact list of every LXMF peer it's seen — not just people who've messaged you first.
|
|
117
|
+
|
|
118
|
+
Each contact records their address, display name (if they set one), and first/last seen times, persisted to `contacts.json`. New contacts trigger the same terminal alert as an incoming message. From the **P** screen you can jump straight into composing a message to any saved contact, or trigger `[A]` to re-announce yourself so others can discover you back.
|
|
119
|
+
|
|
120
|
+
This is mutual: for two peers to find each other, both need to have announced at some point since either was last online. Use `--announce-interval` if you want that to happen automatically instead of only at startup.
|
|
121
|
+
|
|
122
|
+
## File transfer
|
|
123
|
+
|
|
124
|
+
`jcomprns-send` is the same idea as the messenger, but for sending files instead of text. It runs over the same Reticulum setup and can share the same identity, but registers its own destination under a different namespace (`jcomprns.filetransfer` vs LXMF's `lxmf.delivery`) — so it has its own address, its own presence directory, and its own contacts, even though it's the same identity underneath.
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
jcomprns-send
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
- **S** — send a file: paste a recipient's address (hex) and a local file path
|
|
131
|
+
- **R** — list received files: shows what's been received, when, from whom, and where it was saved on disk (under `~/.jcomprns/received_files/`)
|
|
132
|
+
- **P** — presence directory: same mechanism as the messenger's, scoped to file-transfer peers; pick one to send a file directly, or press `[A]` to re-announce yourself
|
|
133
|
+
- **Q** — quit
|
|
134
|
+
|
|
135
|
+
Under the hood this uses `RNS.Link` + `RNS.Resource`, which is Reticulum's built-in mechanism for moving arbitrary data with automatic compression, chunking, and integrity checking — a link is established with the recipient first, then the file streams over it with a live progress percentage. Per RNS's own guidance, Resources aren't recommended for very large files (compression/hashing can outrun the receiver's timeout on slow links); this is intended for the kind of file sizes that make sense over a LoRa-connected RNode, not bulk transfer.
|
|
136
|
+
|
|
137
|
+
Flags: `--config`, `--identity`, `--display-name`, `--announce-interval` (same meaning as in `jcomprns-chat`), `--received-dir` (where incoming files are saved; default `~/.jcomprns/received_files`), `--manifest <path>` (where the received-files log is kept; default `~/.jcomprns/received_files.json`), `--contacts <path>` (default `~/.jcomprns/filetransfer_contacts.json`).
|
|
138
|
+
|
|
139
|
+
## Git over Reticulum
|
|
140
|
+
|
|
141
|
+
`jcomprns-git` lets you `git clone`/`fetch`/`push` a repository over Reticulum, using completely normal git commands — no different from an `ssh://` remote from git's point of view.
|
|
142
|
+
|
|
143
|
+
This works the same way `ssh` does for git: git already knows how to speak its own wire protocol over an arbitrary bidirectional byte stream (that's literally what happens over ssh — `ssh host git-upload-pack '/repo'` pipes git's pack protocol over the ssh channel). This module provides that same stream over an RNS `Link` using `RNS.Buffer.create_bidirectional_buffer()`, and spawns the real `git-upload-pack`/`git-receive-pack` binaries on the serving side. Git itself needs no changes; the `git-remote-jcomprns` command (installed automatically alongside everything else) is what makes `git` recognize the `jcomprns://` URL scheme.
|
|
144
|
+
|
|
145
|
+
### Serving repositories
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
jcomprns-git serve --repos-dir /path/to/repos
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`--repos-dir` should contain one or more bare repositories (e.g. `myrepo.git`, created with `git init --bare`). The command prints your address and the exact URL to give clients:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
Your jcomprns git address: <hex-address>
|
|
155
|
+
Share this with clients as: git clone jcomprns://<hex-address>/<reponame>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Press Enter to announce again, Ctrl+C to quit. Flags: `--config`, `--identity` (same meaning as elsewhere), `--announce-interval <minutes>`.
|
|
159
|
+
|
|
160
|
+
### Cloning / fetching / pushing
|
|
161
|
+
|
|
162
|
+
No setup needed beyond installing this package — `pip install -e .` already put `git-remote-jcomprns` on your `PATH`, and git finds it automatically for any `jcomprns://` remote:
|
|
163
|
+
|
|
164
|
+
```
|
|
165
|
+
git clone jcomprns://<hex-address>/<reponame>
|
|
166
|
+
git remote add origin jcomprns://<hex-address>/<reponame> # for an existing repo
|
|
167
|
+
git fetch
|
|
168
|
+
git push
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
By default the client uses your live `~/.reticulum` config and the shared `~/.jcomprns/identity` file — since git invokes the helper directly with its own stdin/stdout already committed to the wire protocol, there's no interactive config-picker prompt here (unlike the other commands). Override with the `JCOMPRNS_CONFIG` and `JCOMPRNS_IDENTITY` environment variables if you need a specific config profile or identity. Having `rnsd` already running in the background (via `jcomprns-pair`) is recommended if you'll be doing git operations repeatedly, so each one doesn't have to bring up interfaces from scratch.
|
|
172
|
+
|
|
173
|
+
### Security notes
|
|
174
|
+
|
|
175
|
+
The server only serves directories that already exist directly under `--repos-dir`; requests for repo names containing `..` or resolving outside that directory are rejected. There's no authentication beyond Reticulum's own identity/encryption — anyone with the server's address can attempt `upload-pack` (read) and `receive-pack` (push) against any repo you're serving. Don't serve anything you wouldn't hand out to anyone who obtains the address.
|
|
176
|
+
|
|
177
|
+
## Config profiles (`~/.jcomprns/configs/`)
|
|
178
|
+
|
|
179
|
+
`jcomprns-pair`, `jcomprns-chat`, `jcomprns-send`, and `jcomprns-git serve` all prompt at startup (the `git-remote-jcomprns` client helper does not — see above):
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
Which Reticulum config do you want to use?
|
|
183
|
+
[0] Your live config (~/.reticulum)
|
|
184
|
+
[1] default (/Users/you/.jcomprns/configs/default)
|
|
185
|
+
Choice [0]:
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
- **[0]** (or just pressing Enter) uses your live `~/.reticulum` config, same as before.
|
|
189
|
+
- Any other number uses that saved profile directory under `~/.jcomprns/configs/` as the Reticulum config directory for this run (its own `config` file, and its own `storage/`/identity cache, isolated from your live setup).
|
|
190
|
+
|
|
191
|
+
Pass `--config <dir>` on the command line to skip the prompt entirely and use that directory directly (scripting/automation).
|
|
192
|
+
|
|
193
|
+
To save a new profile, copy a working `config` file into `~/.jcomprns/configs/<name>/config` — it'll show up in the list automatically.
|
|
194
|
+
|
|
195
|
+
## Publishing (if you want this on PyPI)
|
|
196
|
+
|
|
197
|
+
This is currently installed via `pip install -e .` (editable, from a local checkout). The package name `jcomprns` is available on PyPI (unclaimed as of this writing) and the build has been validated locally (`python3 -m build` produces a correct wheel — right license metadata, right entry points). Publishing itself is a deliberate step for you to take, not something done as part of this setup.
|
|
198
|
+
|
|
199
|
+
1. Create a PyPI account at [pypi.org](https://pypi.org/account/register/) if you don't have one, and a [TestPyPI](https://test.pypi.org/account/register/) account too (separate signup, useful for a dry run first).
|
|
200
|
+
2. Generate an API token (PyPI account settings → API tokens) rather than using your password directly with `twine`.
|
|
201
|
+
3. Bump `version` in `pyproject.toml` (start at `0.1.0`, already set).
|
|
202
|
+
4. Build and do a dry run against TestPyPI first:
|
|
203
|
+
```
|
|
204
|
+
python3 -m pip install build twine
|
|
205
|
+
python3 -m build
|
|
206
|
+
python3 -m twine upload --repository testpypi dist/*
|
|
207
|
+
```
|
|
208
|
+
When prompted, use `__token__` as the username and your TestPyPI API token as the password. Then sanity-check the install from there: `pip install --index-url https://test.pypi.org/simple/ jcomprns`.
|
|
209
|
+
5. Once that looks right, upload to the real index:
|
|
210
|
+
```
|
|
211
|
+
python3 -m twine upload dist/*
|
|
212
|
+
```
|
|
213
|
+
Same `__token__` / API token pattern, using your real PyPI token this time.
|
|
214
|
+
|
|
215
|
+
After that, anyone can `pip install jcomprns` directly — no repo checkout needed.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "jcomprns"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Pair an RNode over Bluetooth LE and run LXMF messaging, file transfer, and git over Reticulum (RNS)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Topic :: Communications",
|
|
17
|
+
"Topic :: System :: Networking",
|
|
18
|
+
]
|
|
19
|
+
dependencies = [
|
|
20
|
+
"rns>=1.3.5",
|
|
21
|
+
"lxmf",
|
|
22
|
+
"bleak",
|
|
23
|
+
"pyserial",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.scripts]
|
|
27
|
+
jcomprns-pair = "jcomprns.rnode_pair:main"
|
|
28
|
+
jcomprns-chat = "jcomprns.lxmf_messenger:main"
|
|
29
|
+
jcomprns-send = "jcomprns.file_transfer:main"
|
|
30
|
+
jcomprns-git = "jcomprns.rns_git:main"
|
|
31
|
+
git-remote-jcomprns = "jcomprns.rns_git:remote_helper_main"
|
|
32
|
+
|
|
33
|
+
[tool.setuptools.packages.find]
|
|
34
|
+
where = ["src"]
|
jcomprns-0.1.0/setup.cfg
ADDED