A
2026-07-01 01:40:49 -04:00
|
|
|
|
# ria-ran--sim-drop
|
|
|
|
|
|
|
A
Write operational README
Replace the placeholder README with a full operational guide: architecture,
user flow, quick start, config table, API reference (token profile URLs,
admin-token-gated export), profile-package-v2 format, Pi/Android deployment,
core sync, security notes, testing, and project layout.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 22:16:49 -04:00
|
|
|
|
**Closed-loop eSIM provisioning for emergency / NTN 5G deployments.**
|
|
|
|
|
|
|
|
|
|
|
|
A self-contained appliance (Raspberry Pi or Android) that connects to a 5G gNB
|
|
|
|
|
|
for backhaul, broadcasts an open WiFi AP, and hands out eSIM credentials to
|
|
|
|
|
|
connecting devices through a captive portal. Users go from "no signal" to
|
|
|
|
|
|
"connected to the ad-hoc 5G network" in under a minute, with no operator in the
|
|
|
|
|
|
loop.
|
|
|
|
|
|
|
|
|
|
|
|
> Deep design rationale lives in [`plan/design.md`](plan/design.md). This README
|
|
|
|
|
|
> is the operational guide. Notable changes are tracked in
|
|
|
|
|
|
> [`CHANGELOG.md`](CHANGELOG.md).
|
|
|
|
|
|
|
|
|
|
|
|
## Architecture
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌─────────────────────────────────────────┐
|
|
|
|
|
|
│ 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 (Flask) │
|
|
|
|
|
|
│ - Captive Portal (nginx + dnsmasq) │
|
|
|
|
|
|
└─────────────┬───────────────────────────┘
|
|
|
|
|
|
│ WiFi
|
|
|
|
|
|
▼
|
|
|
|
|
|
End User Devices
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## User flow
|
|
|
|
|
|
|
|
|
|
|
|
1. User connects to the open WiFi network `Emergency-5G`.
|
|
|
|
|
|
2. The captive portal auto-redirects to the activation page.
|
|
|
|
|
|
3. User taps **Activate eSIM**.
|
|
|
|
|
|
4. The server generates a unique IMSI / Ki / OPc / ICCID.
|
|
|
|
|
|
5. An eSIM profile is generated and offered as a one-time download.
|
|
|
|
|
|
6. User installs the profile via device Settings.
|
|
|
|
|
|
7. The device connects to the 5G network automatically.
|
|
|
|
|
|
|
|
|
|
|
|
## Quick start (local dev)
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
git clone https://riahub.ai/qoherent/ria-ran--sim-drop.git
|
|
|
|
|
|
cd ria-ran--sim-drop
|
|
|
|
|
|
python3 -m venv venv && source venv/bin/activate
|
|
|
|
|
|
pip install -r requirements.txt
|
|
|
|
|
|
cp config.yaml.example config.yaml # edit MCC/MNC/op_key as needed
|
|
|
|
|
|
python scripts/init_db.py
|
|
|
|
|
|
python -m server.app # → http://localhost:5000
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Open <http://localhost:5000/activate> and click **Activate eSIM**.
|
|
|
|
|
|
|
|
|
|
|
|
## Configuration
|
|
|
|
|
|
|
|
|
|
|
|
All configuration is in `config.yaml` (start from `config.yaml.example`).
|
|
|
|
|
|
|
|
|
|
|
|
| Field | Description | Example |
|
|
|
|
|
|
|-------|-------------|---------|
|
|
|
|
|
|
| `network.mcc` | Mobile Country Code (3 digits) | `"302"` |
|
|
|
|
|
|
| `network.mnc` | Mobile Network Code (2–3 digits) | `"720"` |
|
|
|
|
|
|
| `network.op_key` | Operator key, 16-byte hex (32 chars) | `"63bfa50e…"` |
|
|
|
|
|
|
| `wifi.ip` | WiFi AP IP address | `"192.168.4.1"` |
|
|
|
|
|
|
| `server.host` / `server.port` | Flask bind address | `"0.0.0.0"` / `5000` |
|
|
|
|
|
|
| `server.admin_token` | Token guarding `/api/export`; empty = export disabled | `""` |
|
|
|
|
|
|
| `database.path` | SQLite database path | `"/opt/…/subscribers.db"` |
|
|
|
|
|
|
| `rate_limiting.window_hours` | One profile per MAC per window | `1` |
|
|
|
|
|
|
| `profile.apn` | APN / DNN used in the profile and core session | `"internet"` |
|
|
|
|
|
|
| `profile.smdp_address` | SM-DP+ address embedded in the LPA activation code | `"esim.local"` |
|
|
|
|
|
|
| `core.type` | Core adapter: `open5gs` / `free5gc` / `none` | `"open5gs"` |
|
|
|
|
|
|
| `core.mongodb_uri` | MongoDB connection for the core | `"mongodb://localhost:27017"` |
|
|
|
|
|
|
|
|
|
|
|
|
`op_key` is validated at startup (must be 16-byte hex) and the config file is
|
|
|
|
|
|
rejected if it is empty or missing a required section.
|
|
|
|
|
|
|
|
|
|
|
|
## API reference
|
|
|
|
|
|
|
|
|
|
|
|
| Endpoint | Method | Description |
|
|
|
|
|
|
|----------|--------|-------------|
|
|
|
|
|
|
| `/activate` | GET | Captive portal page |
|
|
|
|
|
|
| `/api/provision` | POST | Generate credentials + profile; returns a one-time `profile_url` |
|
|
|
|
|
|
| `/api/profile/<token>` | GET | Download the profile via its unguessable capability token |
|
|
|
|
|
|
| `/api/export` | GET | Export all subscribers (JSON). Requires `X-Admin-Token`; disabled unless `server.admin_token` is set |
|
|
|
|
|
|
| `/api/status` | GET | Health check (subscriber count, uptime, core adapter) |
|
|
|
|
|
|
|
|
|
|
|
|
`POST /api/provision` accepts `{"mac_address": "aa:bb:cc:dd:ee:ff"}`; if omitted,
|
|
|
|
|
|
the server resolves the client MAC from the ARP table. Rate limiting is per MAC.
|
|
|
|
|
|
|
|
|
|
|
|
## Profile format
|
|
|
|
|
|
|
A
2026-07-01 22:35:54 -04:00
|
|
|
|
Two formats are selectable via `profile.format`:
|
|
|
|
|
|
|
|
|
|
|
|
- **`v2`** (default, always available) — a JSON `profile-package-v2` bundle with
|
|
|
|
|
|
the raw credentials plus **real 3GPP field encodings** (`EF_ICCID`, `EF_IMSI`,
|
|
|
|
|
|
nibble-swapped BCD per 3GPP TS 31.102) and an SGP.22 **LPA activation code**
|
|
|
|
|
|
(`LPA:1$<smdp>$<matching-id>`).
|
|
|
|
|
|
- **`saip`** — a real DER-encoded **SGP.22 SAIP `ProfileElement` sequence**
|
|
|
|
|
|
(ProfileHeader with ICCID + Milenage AKA parameters carrying Ki/OPc), built
|
|
|
|
|
|
with `asn1tools` against the official `PE_Definitions` ASN.1 schema. Enable by
|
|
|
|
|
|
installing `requirements-saip.txt` (see that file for the schema source) and
|
|
|
|
|
|
setting `profile.format: saip`. If prerequisites are missing the server logs a
|
|
|
|
|
|
warning and falls back to `v2`.
|
|
|
|
|
|
|
|
|
|
|
|
The SAIP package currently carries ICCID and the AKA credentials; emitting IMSI
|
|
|
|
|
|
and other EFs (USIM-template / GenericFileManagement PEs with File Control
|
|
|
|
|
|
Parameters) is the next layer. Note that consumer over-the-air install still
|
|
|
|
|
|
requires an SM-DP+ / LPA; these packages target programmable SIMs and
|
A
Write operational README
Replace the placeholder README with a full operational guide: architecture,
user flow, quick start, config table, API reference (token profile URLs,
admin-token-gated export), profile-package-v2 format, Pi/Android deployment,
core sync, security notes, testing, and project layout.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 22:16:49 -04:00
|
|
|
|
core-network sync. See [`CHANGELOG.md`](CHANGELOG.md).
|
|
|
|
|
|
|
|
|
|
|
|
## Deployment
|
|
|
|
|
|
|
|
|
|
|
|
### Raspberry Pi
|
|
|
|
|
|
|
|
|
|
|
|
Requirements: Pi 4 (4GB+), a compatible 5G modem (e.g. Quectel RM500Q), Raspberry
|
|
|
|
|
|
Pi OS Lite 64-bit, Python 3.11+, and a pre-provisioned SIM for gNB backhaul.
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
sudo bash scripts/setup_wifi_ap.sh
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
This installs hostapd/dnsmasq/nginx, configures the AP and captive-portal
|
|
|
|
|
|
redirect, deploys the server under `/opt/esim-provisioning`, creates the `esim`
|
|
|
|
|
|
service user, restricts `config.yaml` to `600`, initialises the DB, and enables
|
|
|
|
|
|
the systemd services. Then edit `/etc/esim-provisioning/config.yaml` and
|
|
|
|
|
|
`systemctl restart esim-provisioning`.
|
|
|
|
|
|
|
|
|
|
|
|
### Android / Termux
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
bash android/termux_setup.sh
|
|
|
|
|
|
~/esim-provisioning/start.sh
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
See [`android/README.md`](android/README.md) for details and limitations.
|
|
|
|
|
|
|
|
|
|
|
|
## Core network sync
|
|
|
|
|
|
|
|
|
|
|
|
With `core.type: none`, credentials are stored locally only. To push them to a
|
|
|
|
|
|
core network:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python scripts/sync_subscribers.py # sync unsynced subscribers to Open5GS/free5gc
|
|
|
|
|
|
python scripts/export_subscribers.py # dump all subscribers as JSON (reads the DB directly)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Adding a new core is a single class in `server/core_adapters/` implementing the
|
|
|
|
|
|
`CoreAdapter` interface.
|
|
|
|
|
|
|
|
|
|
|
|
## Security notes
|
|
|
|
|
|
|
|
|
|
|
|
- Profiles are served at an unguessable one-time token URL, never an enumerable
|
|
|
|
|
|
id — the profile body contains Ki/OPc.
|
|
|
|
|
|
- `/api/export` returns all key material and is **disabled by default**; set
|
|
|
|
|
|
`server.admin_token` and send it as `X-Admin-Token` to enable.
|
|
|
|
|
|
- The deployed `config.yaml` (holds `op_key`) is `chmod 600`, owned by the
|
|
|
|
|
|
service user; the systemd unit runs with `NoNewPrivileges`, `PrivateTmp`, and
|
|
|
|
|
|
`ProtectSystem=strict`.
|
|
|
|
|
|
- The WiFi network is intentionally open (emergency access); there is no
|
|
|
|
|
|
transport encryption on the local link.
|
|
|
|
|
|
|
|
|
|
|
|
## Testing
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
pip install -r requirements.txt
|
|
|
|
|
|
python -m pytest tests/ -q
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
The suite covers credential generation and 3GPP encodings, the database and rate
|
|
|
|
|
|
limiting, the core adapters, config/utility validation, and the Flask API
|
|
|
|
|
|
(provisioning, token download, rate-limit and export auth).
|
|
|
|
|
|
|
|
|
|
|
|
## Project layout
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
server/ Flask app, profile generator, database, core adapters
|
|
|
|
|
|
portal/ Captive-portal frontend (HTML/CSS/JS)
|
|
|
|
|
|
scripts/ init_db, sync/export subscribers, Pi AP setup
|
|
|
|
|
|
configs/ nginx / dnsmasq / hostapd reference configs
|
|
|
|
|
|
systemd/ Service units
|
|
|
|
|
|
android/ Termux deployment
|
|
|
|
|
|
tests/ Test suite
|
|
|
|
|
|
plan/ Design doc and per-unit implementation notes
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
|
|
|
|
MIT (or as appropriate for the deployment context).
|