meshmap 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.
- meshmap-0.1.0/.claude/settings.local.json +16 -0
- meshmap-0.1.0/.github/workflows/README.md +152 -0
- meshmap-0.1.0/.github/workflows/tests.yml +118 -0
- meshmap-0.1.0/.gitignore +22 -0
- meshmap-0.1.0/CLAUDE.md +31 -0
- meshmap-0.1.0/Makefile +45 -0
- meshmap-0.1.0/PKG-INFO +144 -0
- meshmap-0.1.0/README.md +131 -0
- meshmap-0.1.0/meshmap/__init__.py +15 -0
- meshmap-0.1.0/meshmap/cli/__init__.py +128 -0
- meshmap-0.1.0/meshmap/cli/contacts.py +117 -0
- meshmap-0.1.0/meshmap/cli/discover_repeaters.py +137 -0
- meshmap-0.1.0/meshmap/cli/explore.py +397 -0
- meshmap-0.1.0/meshmap/cli/export_key.py +92 -0
- meshmap-0.1.0/meshmap/cli/get_neighbours.py +205 -0
- meshmap-0.1.0/meshmap/cli/guest_login.py +77 -0
- meshmap-0.1.0/meshmap/cli/rf_discover.py +38 -0
- meshmap-0.1.0/meshmap/cli/scan.py +71 -0
- meshmap-0.1.0/meshmap/cli/sniff.py +132 -0
- meshmap-0.1.0/meshmap/crypto.py +737 -0
- meshmap-0.1.0/meshmap/decoder.py +590 -0
- meshmap-0.1.0/meshmap/graph.py +242 -0
- meshmap-0.1.0/meshmap/models.py +215 -0
- meshmap-0.1.0/meshmap/neighbours.py +180 -0
- meshmap-0.1.0/meshmap/scanner.py +391 -0
- meshmap-0.1.0/meshmap/sniffer.py +604 -0
- meshmap-0.1.0/meshmap/web/__init__.py +1 -0
- meshmap-0.1.0/meshmap/web/server.py +83 -0
- meshmap-0.1.0/meshmap/web/static/index.html +852 -0
- meshmap-0.1.0/meshmap-graph.json +1731 -0
- meshmap-0.1.0/pyproject.toml +55 -0
- meshmap-0.1.0/tests/README.md +172 -0
- meshmap-0.1.0/tests/test_crypto.py +785 -0
- meshmap-0.1.0/tests/test_decoder.py +475 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(uv run:*)",
|
|
5
|
+
"Bash(python -m py_compile:*)",
|
|
6
|
+
"Bash(python3 -m py_compile:*)",
|
|
7
|
+
"Bash(python3:*)",
|
|
8
|
+
"Bash(grep:*)",
|
|
9
|
+
"Bash(find:*)",
|
|
10
|
+
"Bash(uv pip install:*)",
|
|
11
|
+
"Bash(/Users/ajo/work/meshmap/tests/test_crypto.py:*)",
|
|
12
|
+
"Bash(python -m pytest:*)",
|
|
13
|
+
"Bash(make test:*)"
|
|
14
|
+
]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# GitHub Actions Workflows
|
|
2
|
+
|
|
3
|
+
This directory contains CI/CD workflows for meshmap.
|
|
4
|
+
|
|
5
|
+
## Workflows
|
|
6
|
+
|
|
7
|
+
### ๐งช [tests.yml](tests.yml)
|
|
8
|
+
**Complete test suite** - Runs on every push and PR
|
|
9
|
+
|
|
10
|
+
- **Triggers**: Push to main/develop, all PRs, manual dispatch
|
|
11
|
+
- **What it does**:
|
|
12
|
+
- **Always runs the full test suite** (all 16+ tests)
|
|
13
|
+
- Tests on Python 3.12 and 3.13
|
|
14
|
+
- Tests on Ubuntu and macOS
|
|
15
|
+
- Runs linting (ruff)
|
|
16
|
+
- Runs type checking (mypy)
|
|
17
|
+
- Generates coverage reports
|
|
18
|
+
- Uploads to Codecov (optional)
|
|
19
|
+
|
|
20
|
+
**Jobs**:
|
|
21
|
+
- `test` - Linux tests with full coverage
|
|
22
|
+
- `test-macos` - macOS compatibility tests (full suite)
|
|
23
|
+
- `check-formatting` - Code style checks
|
|
24
|
+
|
|
25
|
+
**Philosophy**: Always run everything. No shortcuts, no quick checks. Every commit gets the full treatment.
|
|
26
|
+
|
|
27
|
+
## Status Badges
|
|
28
|
+
|
|
29
|
+
Add these badges to your README.md:
|
|
30
|
+
|
|
31
|
+
```markdown
|
|
32
|
+

|
|
33
|
+

|
|
34
|
+

|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Running Workflows Locally
|
|
38
|
+
|
|
39
|
+
### Act (GitHub Actions locally)
|
|
40
|
+
```bash
|
|
41
|
+
# Install act
|
|
42
|
+
brew install act # macOS
|
|
43
|
+
# or download from: https://github.com/nektos/act
|
|
44
|
+
|
|
45
|
+
# Run full test suite
|
|
46
|
+
act -j test
|
|
47
|
+
|
|
48
|
+
# Run macOS tests
|
|
49
|
+
act -j test-macos
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Manual test run (same as CI)
|
|
53
|
+
```bash
|
|
54
|
+
# Exactly what CI runs:
|
|
55
|
+
uv pip install -e ".[dev]"
|
|
56
|
+
uv run pytest tests/ -v --cov=meshmap --cov-report=term
|
|
57
|
+
uv run ruff check meshmap/ tests/
|
|
58
|
+
uv run mypy meshmap/
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Configuration
|
|
62
|
+
|
|
63
|
+
### Python Versions
|
|
64
|
+
Currently testing on:
|
|
65
|
+
- Python 3.12 (primary)
|
|
66
|
+
- Python 3.13 (compatibility)
|
|
67
|
+
|
|
68
|
+
To add more versions, edit `matrix.python-version` in [tests.yml](tests.yml).
|
|
69
|
+
|
|
70
|
+
### Operating Systems
|
|
71
|
+
Currently testing on:
|
|
72
|
+
- Ubuntu Latest (primary)
|
|
73
|
+
- macOS Latest (compatibility)
|
|
74
|
+
|
|
75
|
+
To add Windows, add `windows-latest` to `matrix.os`.
|
|
76
|
+
|
|
77
|
+
### Coverage Reporting
|
|
78
|
+
|
|
79
|
+
Coverage is uploaded to Codecov automatically if you:
|
|
80
|
+
1. Create account at https://codecov.io
|
|
81
|
+
2. Connect your GitHub repo
|
|
82
|
+
3. Add `CODECOV_TOKEN` to GitHub secrets (optional)
|
|
83
|
+
|
|
84
|
+
Disable by removing the "Upload coverage" step.
|
|
85
|
+
|
|
86
|
+
## Troubleshooting
|
|
87
|
+
|
|
88
|
+
### "uv command not found"
|
|
89
|
+
The `astral-sh/setup-uv@v5` action installs uv. If it fails:
|
|
90
|
+
1. Check you're using the latest action version
|
|
91
|
+
2. Try with `uv` installed via pip as fallback
|
|
92
|
+
|
|
93
|
+
### Tests fail on Python 3.13
|
|
94
|
+
Check if dependencies are compatible:
|
|
95
|
+
```bash
|
|
96
|
+
uv pip install -e ".[dev]" --python 3.13
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### macOS tests fail
|
|
100
|
+
Some dependencies might have macOS-specific issues. Check:
|
|
101
|
+
- PyNaCl compilation (needs libsodium)
|
|
102
|
+
- Cryptography library (needs OpenSSL)
|
|
103
|
+
|
|
104
|
+
Add to workflow if needed:
|
|
105
|
+
```yaml
|
|
106
|
+
- name: Install system dependencies (macOS)
|
|
107
|
+
run: brew install libsodium
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Optimization Tips
|
|
111
|
+
|
|
112
|
+
### Cache Dependencies
|
|
113
|
+
Currently enabled via:
|
|
114
|
+
```yaml
|
|
115
|
+
- uses: astral-sh/setup-uv@v5
|
|
116
|
+
with:
|
|
117
|
+
enable-cache: true
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Parallel Jobs
|
|
121
|
+
Tests run in parallel by default:
|
|
122
|
+
- Multiple Python versions run concurrently
|
|
123
|
+
- Linux and macOS jobs run concurrently
|
|
124
|
+
|
|
125
|
+
### Skip CI
|
|
126
|
+
Add to commit message to skip CI:
|
|
127
|
+
```
|
|
128
|
+
git commit -m "docs: update README [skip ci]"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Security
|
|
132
|
+
|
|
133
|
+
### Secrets
|
|
134
|
+
If tests need secrets (e.g., API keys):
|
|
135
|
+
1. Add to GitHub repository secrets
|
|
136
|
+
2. Reference in workflow:
|
|
137
|
+
```yaml
|
|
138
|
+
env:
|
|
139
|
+
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Dependabot
|
|
143
|
+
Enable Dependabot to keep actions updated:
|
|
144
|
+
```yaml
|
|
145
|
+
# .github/dependabot.yml
|
|
146
|
+
version: 2
|
|
147
|
+
updates:
|
|
148
|
+
- package-ecosystem: "github-actions"
|
|
149
|
+
directory: "/"
|
|
150
|
+
schedule:
|
|
151
|
+
interval: "weekly"
|
|
152
|
+
```
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Test on Python ${{ matrix.python-version }}
|
|
12
|
+
runs-on: ${{ matrix.os }}
|
|
13
|
+
|
|
14
|
+
strategy:
|
|
15
|
+
fail-fast: false
|
|
16
|
+
matrix:
|
|
17
|
+
os: [ubuntu-latest]
|
|
18
|
+
python-version: ["3.12", "3.13"]
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout code
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
25
|
+
uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python-version }}
|
|
28
|
+
|
|
29
|
+
- name: Install uv
|
|
30
|
+
uses: astral-sh/setup-uv@v5
|
|
31
|
+
with:
|
|
32
|
+
enable-cache: true
|
|
33
|
+
cache-dependency-glob: "pyproject.toml"
|
|
34
|
+
|
|
35
|
+
- name: Install dependencies
|
|
36
|
+
run: |
|
|
37
|
+
uv pip install --system -e ".[dev]"
|
|
38
|
+
|
|
39
|
+
- name: Run linting (ruff)
|
|
40
|
+
run: |
|
|
41
|
+
uv run ruff check meshmap/ tests/
|
|
42
|
+
continue-on-error: true
|
|
43
|
+
|
|
44
|
+
- name: Run type checking (mypy)
|
|
45
|
+
run: |
|
|
46
|
+
uv run mypy meshmap/
|
|
47
|
+
continue-on-error: true
|
|
48
|
+
|
|
49
|
+
- name: Run all tests with coverage
|
|
50
|
+
run: |
|
|
51
|
+
# Always run the full test suite
|
|
52
|
+
uv run pytest tests/ -v --cov=meshmap --cov-report=xml --cov-report=term
|
|
53
|
+
|
|
54
|
+
- name: Upload coverage to Codecov
|
|
55
|
+
uses: codecov/codecov-action@v4
|
|
56
|
+
with:
|
|
57
|
+
file: ./coverage.xml
|
|
58
|
+
flags: unittests
|
|
59
|
+
name: codecov-umbrella
|
|
60
|
+
fail_ci_if_error: false
|
|
61
|
+
continue-on-error: true
|
|
62
|
+
|
|
63
|
+
- name: Generate coverage badge
|
|
64
|
+
if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest'
|
|
65
|
+
run: |
|
|
66
|
+
echo "Coverage report generated"
|
|
67
|
+
uv run coverage report
|
|
68
|
+
|
|
69
|
+
test-macos:
|
|
70
|
+
name: Test on macOS (Python 3.12)
|
|
71
|
+
runs-on: macos-latest
|
|
72
|
+
|
|
73
|
+
steps:
|
|
74
|
+
- name: Checkout code
|
|
75
|
+
uses: actions/checkout@v4
|
|
76
|
+
|
|
77
|
+
- name: Set up Python 3.12
|
|
78
|
+
uses: actions/setup-python@v5
|
|
79
|
+
with:
|
|
80
|
+
python-version: "3.12"
|
|
81
|
+
|
|
82
|
+
- name: Install uv
|
|
83
|
+
uses: astral-sh/setup-uv@v5
|
|
84
|
+
with:
|
|
85
|
+
enable-cache: true
|
|
86
|
+
|
|
87
|
+
- name: Install dependencies
|
|
88
|
+
run: |
|
|
89
|
+
uv pip install --system -e ".[dev]"
|
|
90
|
+
|
|
91
|
+
- name: Run all tests
|
|
92
|
+
run: |
|
|
93
|
+
# Full test suite on macOS
|
|
94
|
+
uv run pytest tests/ -v --cov=meshmap --cov-report=term
|
|
95
|
+
|
|
96
|
+
check-formatting:
|
|
97
|
+
name: Check code formatting
|
|
98
|
+
runs-on: ubuntu-latest
|
|
99
|
+
|
|
100
|
+
steps:
|
|
101
|
+
- name: Checkout code
|
|
102
|
+
uses: actions/checkout@v4
|
|
103
|
+
|
|
104
|
+
- name: Set up Python
|
|
105
|
+
uses: actions/setup-python@v5
|
|
106
|
+
with:
|
|
107
|
+
python-version: "3.12"
|
|
108
|
+
|
|
109
|
+
- name: Install uv
|
|
110
|
+
uses: astral-sh/setup-uv@v5
|
|
111
|
+
|
|
112
|
+
- name: Install ruff
|
|
113
|
+
run: |
|
|
114
|
+
uv pip install --system ruff
|
|
115
|
+
|
|
116
|
+
- name: Check formatting
|
|
117
|
+
run: |
|
|
118
|
+
uv run ruff format --check meshmap/ tests/
|
meshmap-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.pyo
|
|
5
|
+
|
|
6
|
+
# Testing & coverage
|
|
7
|
+
.coverage
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
|
|
10
|
+
# Virtual environments
|
|
11
|
+
.venv/
|
|
12
|
+
|
|
13
|
+
# Keys / secrets
|
|
14
|
+
my_real_key.txt
|
|
15
|
+
mykey
|
|
16
|
+
myotherkey
|
|
17
|
+
|
|
18
|
+
# Local reference repos
|
|
19
|
+
ref/
|
|
20
|
+
|
|
21
|
+
# uv
|
|
22
|
+
uv.lock
|
meshmap-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# meshmap
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
meshmap is a Python tool for scanning and mapping meshcore networks. It builds a network graph by:
|
|
5
|
+
- Discovering nearby nodes
|
|
6
|
+
- Accessing guest pages of repeaters to find adjacent nodes
|
|
7
|
+
- Collecting map coordinates for each node
|
|
8
|
+
|
|
9
|
+
## Tech Stack
|
|
10
|
+
- Python 3.12+
|
|
11
|
+
- uv for package management and tooling
|
|
12
|
+
|
|
13
|
+
## Development Setup
|
|
14
|
+
- Use `uv` for dependency management
|
|
15
|
+
- Run commands via the Makefile for consistency
|
|
16
|
+
- Follow PEP 8 style guidelines
|
|
17
|
+
|
|
18
|
+
## Project Structure
|
|
19
|
+
```
|
|
20
|
+
meshmap/
|
|
21
|
+
โโโ meshmap/ # Main package directory
|
|
22
|
+
โโโ tests/ # Test files
|
|
23
|
+
โโโ pyproject.toml # Project configuration
|
|
24
|
+
โโโ Makefile # Development commands
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Key Considerations
|
|
28
|
+
- Network scanning should be done responsibly
|
|
29
|
+
- Handle connection errors gracefully
|
|
30
|
+
- Consider rate limiting when accessing guest pages
|
|
31
|
+
- Store graph data efficiently
|
meshmap-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
.PHONY: help install dev test lint format clean run
|
|
2
|
+
|
|
3
|
+
help: ## Show this help message
|
|
4
|
+
@echo 'Usage: make [target]'
|
|
5
|
+
@echo ''
|
|
6
|
+
@echo 'Available targets:'
|
|
7
|
+
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}'
|
|
8
|
+
|
|
9
|
+
install: ## Install production dependencies
|
|
10
|
+
uv pip install -e .
|
|
11
|
+
|
|
12
|
+
dev: ## Install development dependencies
|
|
13
|
+
uv pip install -e ".[dev]"
|
|
14
|
+
|
|
15
|
+
test: ## Run tests with coverage
|
|
16
|
+
uv run pytest
|
|
17
|
+
|
|
18
|
+
lint: ## Run linting checks
|
|
19
|
+
uv run ruff check
|
|
20
|
+
uv run mypy meshmap
|
|
21
|
+
|
|
22
|
+
lint-fix: ## Run linting fixes
|
|
23
|
+
uv run ruff check --fix .
|
|
24
|
+
|
|
25
|
+
format: ## Format code with ruff
|
|
26
|
+
uv run ruff format .
|
|
27
|
+
|
|
28
|
+
clean: ## Clean up generated files
|
|
29
|
+
rm -rf build/
|
|
30
|
+
rm -rf dist/
|
|
31
|
+
rm -rf *.egg-info
|
|
32
|
+
rm -rf .pytest_cache/
|
|
33
|
+
rm -rf .coverage
|
|
34
|
+
rm -rf htmlcov/
|
|
35
|
+
find . -type d -name __pycache__ -exec rm -rf {} +
|
|
36
|
+
find . -type f -name '*.pyc' -delete
|
|
37
|
+
|
|
38
|
+
run: ## Run meshmap
|
|
39
|
+
uv run meshmap
|
|
40
|
+
|
|
41
|
+
sync: ## Sync dependencies
|
|
42
|
+
uv pip sync
|
|
43
|
+
|
|
44
|
+
lock: ## Update lock file
|
|
45
|
+
uv pip compile pyproject.toml -o requirements.txt
|
meshmap-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meshmap
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A tool to scan and map meshcore network graphs
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: click>=8.0.0
|
|
7
|
+
Requires-Dist: cryptography>=42.0.0
|
|
8
|
+
Requires-Dist: meshcore
|
|
9
|
+
Requires-Dist: pynacl>=1.5.0
|
|
10
|
+
Requires-Dist: pyyaml>=6.0.0
|
|
11
|
+
Requires-Dist: rich>=13.0.0
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# meshmap
|
|
15
|
+
|
|
16
|
+
A tool for scanning and mapping [meshcore](https://github.com/ripplebiz/MeshCore) radio networks. It discovers nearby nodes, logs in to each reachable repeater to enumerate its neighbours, and builds a graph of the network โ including GPS coordinates where available.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
Requires Python 3.12+ and [uv](https://docs.astral.sh/uv/).
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
# Install uv (if not already installed)
|
|
24
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
25
|
+
|
|
26
|
+
# Clone and install
|
|
27
|
+
git clone https://github.com/mangelajo/meshmap
|
|
28
|
+
cd meshmap
|
|
29
|
+
uv sync
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The `meshmap` command is then available via `uv run meshmap`.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
All commands require `-p` to specify the serial port of your connected device.
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
meshmap -p PORT [OPTIONS] COMMAND
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
**Global options** (apply to all commands):
|
|
43
|
+
|
|
44
|
+
| Flag | Description |
|
|
45
|
+
|------|-------------|
|
|
46
|
+
| `-p, --serial-port` | Serial port (e.g. `/dev/ttyUSB0`, `COM3`) |
|
|
47
|
+
| `--sniff` | Print decoded RF packets in real time |
|
|
48
|
+
| `--sniff-key PATH` | Private key file for decryption (repeatable) |
|
|
49
|
+
| `-d, --debug` | Low-level meshcore debug output |
|
|
50
|
+
|
|
51
|
+
## Commands
|
|
52
|
+
|
|
53
|
+
### `explore` โ map the network graph
|
|
54
|
+
|
|
55
|
+
The main command. Discovers 0-hop repeaters, then recursively logs in to each one to fetch its neighbour list, building a full network graph. Saves to a JSON file after each node so it can be paused and resumed.
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
# First scan
|
|
59
|
+
uv run meshmap -p /dev/ttyUSB0 explore
|
|
60
|
+
|
|
61
|
+
# Resume a previous scan, open live visualisation in browser
|
|
62
|
+
uv run meshmap -p /dev/ttyUSB0 explore --resume --serve
|
|
63
|
+
|
|
64
|
+
# Re-visit already-explored nodes to refresh SNR data
|
|
65
|
+
uv run meshmap -p /dev/ttyUSB0 explore --resume --refresh
|
|
66
|
+
|
|
67
|
+
# Retry nodes that failed on the last run
|
|
68
|
+
uv run meshmap -p /dev/ttyUSB0 explore --resume --retry
|
|
69
|
+
|
|
70
|
+
# Limit exploration to 3 hops, save to a custom file
|
|
71
|
+
uv run meshmap -p /dev/ttyUSB0 explore --depth 3 -o my-network.json
|
|
72
|
+
|
|
73
|
+
# Full run with live packet sniffing and web visualisation
|
|
74
|
+
uv run meshmap -p /dev/ttyUSB0 --sniff --sniff-key mykey.txt explore --serve --resume
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The web visualisation (`--serve`) is available at `http://localhost:8080` and auto-refreshes every 3 seconds while the scan runs.
|
|
78
|
+
|
|
79
|
+
| Option | Default | Description |
|
|
80
|
+
|--------|---------|-------------|
|
|
81
|
+
| `-o, --output` | `meshmap-graph.json` | Output file |
|
|
82
|
+
| `--resume` | off | Load existing graph, skip already-visited nodes |
|
|
83
|
+
| `--refresh` | off | Re-queue already-visited nodes (oldest first) |
|
|
84
|
+
| `--retry` | off | Re-queue nodes whose last visit failed |
|
|
85
|
+
| `--serve` | off | Start web visualisation server |
|
|
86
|
+
| `--port` | `8080` | Web server port |
|
|
87
|
+
| `--depth` | `5` | Max BFS hop depth |
|
|
88
|
+
| `-w, --wait-time` | `10.0` | Seconds to wait for 0-hop discovery responses |
|
|
89
|
+
|
|
90
|
+
### `discover-repeaters` โ list nearby repeaters
|
|
91
|
+
|
|
92
|
+
```sh
|
|
93
|
+
uv run meshmap -p /dev/ttyUSB0 discover-repeaters
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `get-neighbours` โ inspect a single repeater
|
|
97
|
+
|
|
98
|
+
Log in to one repeater and print its neighbour list.
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
uv run meshmap -p /dev/ttyUSB0 get-neighbours
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### `sniff` โ decode RF packets
|
|
105
|
+
|
|
106
|
+
Listen and decode raw RF traffic for a given duration.
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
uv run meshmap -p /dev/ttyUSB0 sniff 30
|
|
110
|
+
uv run meshmap -p /dev/ttyUSB0 sniff 60 --key mykey.txt
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### `contacts` โ list known contacts
|
|
114
|
+
|
|
115
|
+
```sh
|
|
116
|
+
uv run meshmap -p /dev/ttyUSB0 contacts
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### `scan` โ quick 0-hop node scan
|
|
120
|
+
|
|
121
|
+
```sh
|
|
122
|
+
uv run meshmap -p /dev/ttyUSB0 scan
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Graph file format
|
|
126
|
+
|
|
127
|
+
The graph is saved as JSON and can be loaded by external tools:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"version": 1,
|
|
132
|
+
"last_updated": "2026-02-17T12:00:00+00:00",
|
|
133
|
+
"nodes": {
|
|
134
|
+
"<pubkey>": {
|
|
135
|
+
"public_key": "...", "name": "My Repeater",
|
|
136
|
+
"node_type": "repeater", "lat": 40.4, "lon": -3.7,
|
|
137
|
+
"depth": 1, "last_visited": "...", "visit_failed": false
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
"edges": [
|
|
141
|
+
{ "node_a": "...", "node_b": "...", "snr_a_hears_b": 12.5, "snr_b_hears_a": 9.0, "last_seen": "..." }
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
```
|
meshmap-0.1.0/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# meshmap
|
|
2
|
+
|
|
3
|
+
A tool for scanning and mapping [meshcore](https://github.com/ripplebiz/MeshCore) radio networks. It discovers nearby nodes, logs in to each reachable repeater to enumerate its neighbours, and builds a graph of the network โ including GPS coordinates where available.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
Requires Python 3.12+ and [uv](https://docs.astral.sh/uv/).
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
# Install uv (if not already installed)
|
|
11
|
+
curl -LsSf https://astral.sh/uv/install.sh | sh
|
|
12
|
+
|
|
13
|
+
# Clone and install
|
|
14
|
+
git clone https://github.com/mangelajo/meshmap
|
|
15
|
+
cd meshmap
|
|
16
|
+
uv sync
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The `meshmap` command is then available via `uv run meshmap`.
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
All commands require `-p` to specify the serial port of your connected device.
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
meshmap -p PORT [OPTIONS] COMMAND
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Global options** (apply to all commands):
|
|
30
|
+
|
|
31
|
+
| Flag | Description |
|
|
32
|
+
|------|-------------|
|
|
33
|
+
| `-p, --serial-port` | Serial port (e.g. `/dev/ttyUSB0`, `COM3`) |
|
|
34
|
+
| `--sniff` | Print decoded RF packets in real time |
|
|
35
|
+
| `--sniff-key PATH` | Private key file for decryption (repeatable) |
|
|
36
|
+
| `-d, --debug` | Low-level meshcore debug output |
|
|
37
|
+
|
|
38
|
+
## Commands
|
|
39
|
+
|
|
40
|
+
### `explore` โ map the network graph
|
|
41
|
+
|
|
42
|
+
The main command. Discovers 0-hop repeaters, then recursively logs in to each one to fetch its neighbour list, building a full network graph. Saves to a JSON file after each node so it can be paused and resumed.
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
# First scan
|
|
46
|
+
uv run meshmap -p /dev/ttyUSB0 explore
|
|
47
|
+
|
|
48
|
+
# Resume a previous scan, open live visualisation in browser
|
|
49
|
+
uv run meshmap -p /dev/ttyUSB0 explore --resume --serve
|
|
50
|
+
|
|
51
|
+
# Re-visit already-explored nodes to refresh SNR data
|
|
52
|
+
uv run meshmap -p /dev/ttyUSB0 explore --resume --refresh
|
|
53
|
+
|
|
54
|
+
# Retry nodes that failed on the last run
|
|
55
|
+
uv run meshmap -p /dev/ttyUSB0 explore --resume --retry
|
|
56
|
+
|
|
57
|
+
# Limit exploration to 3 hops, save to a custom file
|
|
58
|
+
uv run meshmap -p /dev/ttyUSB0 explore --depth 3 -o my-network.json
|
|
59
|
+
|
|
60
|
+
# Full run with live packet sniffing and web visualisation
|
|
61
|
+
uv run meshmap -p /dev/ttyUSB0 --sniff --sniff-key mykey.txt explore --serve --resume
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
The web visualisation (`--serve`) is available at `http://localhost:8080` and auto-refreshes every 3 seconds while the scan runs.
|
|
65
|
+
|
|
66
|
+
| Option | Default | Description |
|
|
67
|
+
|--------|---------|-------------|
|
|
68
|
+
| `-o, --output` | `meshmap-graph.json` | Output file |
|
|
69
|
+
| `--resume` | off | Load existing graph, skip already-visited nodes |
|
|
70
|
+
| `--refresh` | off | Re-queue already-visited nodes (oldest first) |
|
|
71
|
+
| `--retry` | off | Re-queue nodes whose last visit failed |
|
|
72
|
+
| `--serve` | off | Start web visualisation server |
|
|
73
|
+
| `--port` | `8080` | Web server port |
|
|
74
|
+
| `--depth` | `5` | Max BFS hop depth |
|
|
75
|
+
| `-w, --wait-time` | `10.0` | Seconds to wait for 0-hop discovery responses |
|
|
76
|
+
|
|
77
|
+
### `discover-repeaters` โ list nearby repeaters
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
uv run meshmap -p /dev/ttyUSB0 discover-repeaters
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `get-neighbours` โ inspect a single repeater
|
|
84
|
+
|
|
85
|
+
Log in to one repeater and print its neighbour list.
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
uv run meshmap -p /dev/ttyUSB0 get-neighbours
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### `sniff` โ decode RF packets
|
|
92
|
+
|
|
93
|
+
Listen and decode raw RF traffic for a given duration.
|
|
94
|
+
|
|
95
|
+
```sh
|
|
96
|
+
uv run meshmap -p /dev/ttyUSB0 sniff 30
|
|
97
|
+
uv run meshmap -p /dev/ttyUSB0 sniff 60 --key mykey.txt
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `contacts` โ list known contacts
|
|
101
|
+
|
|
102
|
+
```sh
|
|
103
|
+
uv run meshmap -p /dev/ttyUSB0 contacts
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `scan` โ quick 0-hop node scan
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
uv run meshmap -p /dev/ttyUSB0 scan
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Graph file format
|
|
113
|
+
|
|
114
|
+
The graph is saved as JSON and can be loaded by external tools:
|
|
115
|
+
|
|
116
|
+
```json
|
|
117
|
+
{
|
|
118
|
+
"version": 1,
|
|
119
|
+
"last_updated": "2026-02-17T12:00:00+00:00",
|
|
120
|
+
"nodes": {
|
|
121
|
+
"<pubkey>": {
|
|
122
|
+
"public_key": "...", "name": "My Repeater",
|
|
123
|
+
"node_type": "repeater", "lat": 40.4, "lon": -3.7,
|
|
124
|
+
"depth": 1, "last_visited": "...", "visit_failed": false
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
"edges": [
|
|
128
|
+
{ "node_a": "...", "node_b": "...", "snr_a_hears_b": 12.5, "snr_b_hears_a": 9.0, "last_seen": "..." }
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""meshmap - A tool to scan and map meshcore network graphs."""
|
|
2
|
+
|
|
3
|
+
from meshmap import crypto, decoder, models
|
|
4
|
+
from meshmap.scanner import MeshScanner
|
|
5
|
+
from meshmap.sniffer import PacketSniffer
|
|
6
|
+
|
|
7
|
+
__version__ = "0.1.0"
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"MeshScanner",
|
|
11
|
+
"PacketSniffer",
|
|
12
|
+
"models",
|
|
13
|
+
"decoder",
|
|
14
|
+
"crypto",
|
|
15
|
+
]
|