neuron-explorer
Steering-first neuron explorer for Qwen3.5-4B: push every MLP neuron, keep every rollout, browse the results
Open-source, self-hosted SMS gateway. Turn Android phones into an API for OTP, transactional SMS, USSD, and fleet automation. Apache 2.0.
Developer toolsPython · TypeScriptGitHub @behzad-njfOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Self-hosted SMS gateway & device automation platform. Turn Android phones into an API-driven fleet for OTP, transactional SMS, USSD, webhooks, and live monitoring.
Taken from the repository README
Loading the full README.
Read the full README ↗Turn Android phones into an API-driven SMS gateway and device automation fleet.
DeviceFlow pairs real phones with a multi-tenant control plane. Operators manage devices from a web dashboard; apps and backends send SMS, run USSD, import contacts, and issue remote commands through a REST API and live WebSocket updates.
| Component | Path | Role |
|---|---|---|
| Backend | backend/ | Django REST API, WebSockets (Channels), Celery tasks, multi-tenant auth |
| Dashboard | dashboard/ | React + Vite ops UI (EN / FA), live device monitor |
| Android agent | android-agent/ | Kotlin app: pair, heartbeat, send/receive SMS, remote commands |
| Deploy | deploy/ | Docker Compose, optional Kubernetes / Grafana assets |
| Docs | docs/ | Architecture, API, security, ops |
route_key (e.g. PizoGold customer UUID) → phone/SIM on a shared project; omit route_key to keep classic pool routing*#*#CODE#*#*)┌─────────────┐ JWT / WS ┌──────────────────┐
│ Dashboard │◄─────────────────►│ Django API │
└─────────────┘ │ (+ Redis/Celery)│
└────────┬─────────┘
│
Device token · HTTP poll (~3s) · heartbeat (~15s)
│
┌────────▼─────────┐
│ Android agent │
│ (foreground svc)│
└──────────────────┘
POST /agent/pair/ → receives device_token.GET /agent/commands/pending/ for SMS + control commands; acks results.device.updated, command.acked, SMS events, etc.The agent does not require a persistent WebSocket; polling is the source of truth for device work.
From the repo root:
cp .env.example .env
Defaults use SQLite (backend/db.sqlite3) when DATABASE_URL is unset. Point VITE_API_BASE_URL at your API (see .env.example).
cd backend
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements/base.txt
python manage.py migrate
python manage.py seed_operator_prefixes # IR operator prefix map (optional but useful)
python manage.py collectstatic --noinput
python manage.py createsuperuser # first login for Django admin / bootstrap
# HTTP + WebSockets
uvicorn config.asgi:application --host 0.0.0.0 --port 8000 --reload
/api/v1/schema//api/v1/docs//admin/config.settings.local runs Celery tasks eagerly (in-process), so you can develop without starting a worker. For production-like offline detection and async jobs, run Redis + Celery beat/worker (see Docker below).
cd dashboard
npm install
npm run dev
Open http://localhost:5173.
Typical first flow:
Set API URL via dashboard/.env or root .env:
VITE_API_BASE_URL=http://localhost:8000/api/v1
Useful to verify pairing and outbound SMS without Android:
# API must be running; use a pairing code from the dashboard
python scripts/mock_agent.py --code YOURCODE --base-url http://127.0.0.1:8000/api/v1
android-agent/ in Android Studio and sync Gradle.cd android-agent
./gradlew assembleDebug
# APK: app/build/outputs/apk/debug/app-debug.apk
localhost) and the pairing code.More detail: android-agent/README.md.
Networking tip: On a physical phone, http://10.0.2.2:8000 is the emulator alias only. Use your PC’s LAN IP (e.g. http://192.168.x.x:8000/api/v1) and ensure ALLOWED_HOSTS / firewall allow it.
cp .env.example .env
docker compose -f deploy/docker-compose.yml up --build
This starts Postgres, Redis, and the API on port 8000. Run the dashboard locally against that API, or extend Compose for your own frontend image.
| Variable | Purpose |
|---|---|
SECRET_KEY | Django secret (change in production) |
DEBUG | Debug mode |
ALLOWED_HOSTS | Comma-separated hosts |
DATABASE_URL | Postgres URL; omit for SQLite |
REDIS_URL | Redis for Celery / Channels |
CHANNELS_USE_REDIS | true to use Redis channel layer (Docker sets this) |
CORS_ALLOWED_ORIGINS | Dashboard origins |
JWT_ACCESS_MINUTES / JWT_REFRESH_DAYS | Auth token lifetimes |
DEVICE_OFFLINE_AFTER_SECONDS | Mark devices offline without heartbeat |
VITE_API_BASE_URL | Dashboard → API base path |
Never commit real .env files; .env.example is the template.
Base path: /api/v1/
| Area | Examples |
|---|---|
| Auth | JWT login / refresh |
| Devices | Pairing codes, list/detail, remote commands, OTA |
| Agent | pair, heartbeat, commands/pending, message status, inbound SMS |
| Messaging | Send, bulk, templates, conversations |
| Projects | API keys, webhooks, project–device links |
| Routing | Rules, operators, load-balancing config |
| Ops | Analytics, queues, map, audit logs, metrics (/metrics) |
Authenticate dashboard users with JWT. Integrate server-side with project API keys (Authorization: ApiKey …) and optional Idempotency-Key on send.
Interactive docs: /api/v1/docs/.
Typical setup:
mode: "otp" so the message is prioritized over marketing traffic.export DEVICEFLOW_URL="https://your-api.example.com/api/v1"
export DEVICEFLOW_API_KEY="df_live_xxxxxxxx" # project API key
export USER_PHONE="+989121234567"
export OTP_CODE="482915"
curl -sS -X POST "$DEVICEFLOW_URL/messages/send/" \
-H "Authorization: ApiKey $DEVICEFLOW_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: otp-$USER_PHONE-$(date +%s)" \
-d "{
\"to\": \"$USER_PHONE\",
\"text\": \"Your verification code is $OTP_CODE. It expires in 5 minutes.\",
\"mode\": \"otp\",
\"client_ref\": \"signup-$USER_PHONE\"
}"
Successful enqueue returns 202 with a message id, for example:
{
"id": "a1b2c3d4-....",
"status": "queued",
"mode": "otp",
"to_number": "+989121234567",
"created_at": "2026-07-24T15:00:00Z"
}
The online agent picks the job up within a few seconds, sends the SMS, then reports sent / failed back to the API (visible in the dashboard Messages / Live views).
import os
import secrets
import httpx
API = os.environ["DEVICEFLOW_URL"].rstrip("/") # e.g. https://api.example.com/api/v1
KEY = os.environ["DEVICEFLOW_API_KEY"]
def send_otp(phone: str) -> tuple[str, str]:
code = f"{secrets.randbelow(1_000_000):06d}"
r = httpx.post(
f"{API}/messages/send/",
headers={
"Authorization": f"ApiKey {KEY}",
"Idempotency-Key": f"otp-{phone}-{code}",
},
json={
"to": phone,
"text": f"Your verification code is {code}. It expires in 5 minutes.",
"mode": "otp",
"client_ref": f"login-{phone}",
},
timeout=15.0,
)
r.raise_for_status()
return code, r.json()["id"]
# Store `code` (hashed) in your session/DB, then verify what the user types.
otp, message_id = send_otp("+989121234567")
"mode": "otp" — OTP traffic is queued ahead of marketing / general messages.Idempotency-Key per send so retries from your backend do not duplicate SMS.+98…, +1…)."device": "<device-id-or-name>", or let project routing pick a healthy phone.GET /messages/{id}/.From the device detail page (or POST /devices/{id}/commands/), you can:
| Command | Effect |
|---|---|
sync, refresh_sim, clear_queue, restart_agent, enable_logs | Ops / diagnostics |
ussd_run | Dial USSD on the phone |
contacts_read | Import contacts into the platform |
hide_launcher / show_launcher | Remove / restore app icon |
lock_ui / unlock_ui / set_passcode / clear_passcode | Soft UI lock |
set_dialer_code | Customize reopen code (dial *#*#CODE#*#*) |
Security state (launcher_hidden, ui_locked, passcode_set, dialer_code) is reported on heartbeat and shown in the dashboard.
SMSGateway/
├── backend/ # Django project (apps, config, plugins)
├── dashboard/ # React SPA
├── android-agent/ # Kotlin Compose agent
├── Images/ # Brand assets (icon, banner, dashboard screenshot)
├── deploy/ # docker-compose, k8s, grafana
├── docs/ # Product & engineering docs
├── scripts/ # mock_agent, load_test, helpers
└── .env.example
SECRET_KEY, DEBUG=false, and tight ALLOWED_HOSTS / CORS.deploy/k8s/.| Topic | Link |
|---|---|
| Docs index | docs/00-INDEX.md |
| Vision | docs/01-VISION-AND-PRODUCT-STRATEGY.md |
| REST API | docs/api/01-REST-API.md |
| Device protocol | docs/api/03-DEVICE-COMMUNICATION-PROTOCOL.md |
| Plugin SDK | docs/plugins/01-PLUGIN-SDK.md |
| Android agent | android-agent/README.md |
Licensed under the Apache License, Version 2.0. See LICENSE.
Attribution is required via NOTICE: if you use or redistribute DeviceFlow (including forks and deployments), you must credit the original project and link the source repository:
https://github.com/behzad-njf/DeviceFlow
Suggested credit line:
Based on DeviceFlow — https://github.com/behzad-njf/DeviceFlow