A
8672b0de6b
Full working closed-loop eSIM provisioning stack (spec in claude.md / plan/): Flask API, SQLite subscriber store, credential/profile generator, Open5GS and free5gc core adapters, captive portal, operational scripts, systemd units, network configs, and a 44-test suite. Profile generation currently returns a mock JSON profile; real SGP.22 generation via pySim is the planned Phase-2 upgrade. Fixes two bugs found while getting the suite green: - generate_imsi hardcoded a 10-digit MSIN, producing 16-digit IMSIs for a 3-digit MNC; MSIN length is now 15 - len(mcc) - len(mnc). - check_rate_limit compared timestamps as strings across mismatched formats (SQLite CURRENT_TIMESTAMP vs Python isoformat); both sides now normalized via SQLite datetime() so the window check is chronologically correct. Add .gitignore for venv, caches, runtime config.yaml, and databases. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
161 lines
5.2 KiB
Markdown
161 lines
5.2 KiB
Markdown
# Unit 11 — README.md
|
|
|
|
## Goal
|
|
Write the main `README.md` at the project root. This is the last unit — written after all code exists.
|
|
|
|
## File
|
|
- `README.md`
|
|
|
|
---
|
|
|
|
## Required Sections
|
|
|
|
### Header
|
|
- Project name + one-line description
|
|
- Badges (optional): Python version, license
|
|
|
|
### Architecture Diagram
|
|
Copy from `design.md`:
|
|
```
|
|
┌─────────────────────────────────────────┐
|
|
│ Server Host (srsRAN gNB) │
|
|
│ - srsRAN gNB │
|
|
│ - Open5GS Core (or free5gc) │
|
|
│ - Subscriber database │
|
|
└─────────────┬───────────────────────────┘
|
|
│ 5G NR
|
|
▼
|
|
┌─────────────────────────────────────────┐
|
|
│ UE WiFi AP Drop │
|
|
│ (Android device or Raspberry Pi) │
|
|
│ - WiFi AP (open network) │
|
|
│ - eSIM Provisioning Server │
|
|
│ - Captive Portal │
|
|
└─────────────┬───────────────────────────┘
|
|
│ WiFi
|
|
▼
|
|
End User Devices
|
|
```
|
|
|
|
### User Flow (numbered list)
|
|
1. User connects to open WiFi `Emergency-5G`
|
|
2. Captive portal auto-redirects to activation page
|
|
3. User taps "Activate eSIM"
|
|
4. Server generates IMSI/Ki/OPc/ICCID
|
|
5. eSIM profile downloaded to device
|
|
6. User installs profile via Settings
|
|
7. Device connects to 5G network automatically
|
|
|
|
### System Requirements
|
|
|
|
**Raspberry Pi:**
|
|
- Raspberry Pi 4 (4GB+ RAM)
|
|
- Quectel RM500Q 5G modem (or compatible)
|
|
- Raspberry Pi OS Lite 64-bit
|
|
- Python 3.11+
|
|
|
|
**Android:**
|
|
- Android 9+ with Termux (F-Droid)
|
|
- 5G-capable device
|
|
- Pre-provisioned SIM for gNB backhaul
|
|
|
|
**End-user devices:**
|
|
- eSIM-capable device (eUICC support)
|
|
- Any OS (Android, iOS, Windows)
|
|
|
|
### Quick Start (5 commands)
|
|
```bash
|
|
git clone <repo>
|
|
cd esim-provisioning
|
|
python3 -m venv venv && source venv/bin/activate
|
|
pip install -r requirements.txt
|
|
cp config.yaml.example config.yaml # edit as needed
|
|
python scripts/init_db.py
|
|
python -m server.app
|
|
# → Server running at http://localhost:5000
|
|
```
|
|
|
|
### Configuration Guide
|
|
|
|
Key `config.yaml` fields:
|
|
|
|
| Field | Description | Example |
|
|
|-------|-------------|---------|
|
|
| `network.mcc` | Mobile Country Code | `"302"` |
|
|
| `network.mnc` | Mobile Network Code | `"720"` |
|
|
| `network.op_key` | Operator key (hex, 16 bytes) | `"63bfa..."` |
|
|
| `wifi.ip` | WiFi AP IP address | `"192.168.4.1"` |
|
|
| `database.path` | SQLite database path | `"/opt/.../subscribers.db"` |
|
|
| `rate_limiting.window_hours` | Rate limit window | `1` |
|
|
| `core.type` | Core adapter | `"open5gs"` / `"none"` |
|
|
| `core.mongodb_uri` | MongoDB connection | `"mongodb://localhost:27017"` |
|
|
|
|
### Raspberry Pi Installation
|
|
Reference `scripts/setup_wifi_ap.sh` for automated setup, or follow manual steps from `configs/` directory.
|
|
|
|
### Android / Termux Installation
|
|
```bash
|
|
bash android/termux_setup.sh
|
|
~/esim-provisioning/start.sh
|
|
```
|
|
See `android/README.md` for detailed instructions and limitations.
|
|
|
|
### API Reference
|
|
| Endpoint | Method | Description |
|
|
|----------|--------|-------------|
|
|
| `/activate` | GET | Captive portal page |
|
|
| `/api/provision` | POST | Generate eSIM profile |
|
|
| `/api/profile/<id>` | GET | Download profile |
|
|
| `/api/export` | GET | Export all subscribers (JSON) |
|
|
| `/api/status` | GET | Health check |
|
|
|
|
### Troubleshooting
|
|
|
|
**Profile generation fails:**
|
|
```
|
|
Check: python -c "from server.profile_generator import generate_ki; print(generate_ki().hex())"
|
|
Check: OP key is 32 hex chars (16 bytes) in config.yaml
|
|
```
|
|
|
|
**Captive portal not showing:**
|
|
```
|
|
Check: nslookup google.com 192.168.4.1 → should return 192.168.4.1
|
|
Check: systemctl status dnsmasq nginx
|
|
Check: iptables -t nat -L PREROUTING
|
|
```
|
|
|
|
**5G authentication fails after eSIM install:**
|
|
```
|
|
Check: IMSI/Ki/OPc in subscriber database matches what's in Open5GS
|
|
Check: PLMN (MCC+MNC) matches gNB configuration
|
|
Check: python scripts/sync_subscribers.py
|
|
Check: Open5GS UDM/AMF logs: journalctl -u open5gs-udmd
|
|
```
|
|
|
|
**Rate limit blocking legitimate user:**
|
|
```sql
|
|
-- Find and clear rate limit for a MAC:
|
|
sqlite3 /path/to/subscribers.db \
|
|
"DELETE FROM subscribers WHERE mac_address='aa:bb:cc:dd:ee:ff';"
|
|
```
|
|
|
|
### Architecture Notes
|
|
- **Adapter pattern**: Adding a new core (e.g., srsRAN-CN) requires only a new class in `server/core_adapters/`
|
|
- **IMSI space**: 10^10 possible MSINs per MCC+MNC — effectively unlimited for field use
|
|
- **Rate limiting**: Per MAC address, 1 profile per hour window. SQLite-backed, survives restarts
|
|
- **Profile format**: Phase 1 mock (JSON bytes). Phase 2: real SGP.22 via pySim for eUICC compatibility
|
|
|
|
### License
|
|
MIT (or as appropriate for deployment context)
|
|
|
|
---
|
|
|
|
## Writing Notes
|
|
|
|
When writing the actual README.md:
|
|
- Keep it scannable — use tables, code blocks, headers
|
|
- Lead with the architecture diagram
|
|
- Quick Start must work in <5 minutes on a fresh Pi
|
|
- Troubleshooting section is critical — include it prominently
|
|
- Do not duplicate design.md — link to it for deep dives
|