Beginner May 19, 2026 · 2 min read

What it does

Operators provision the OrbWeaver Softphone Android app by scanning a one-shot QR code from the Extension form. The app exchanges the OTP for a Twilio Voice JWT + long-lived refresh token, registers with the Twilio Voice SDK over WebRTC, and rings on inbound calls via FCM push.

Stack

  • App: orbweaver_pbx v1.7.0 backend + orbweaver_pbx_android scaffold + phases 3-4
  • DocTypes: Mobile Provisioning Token, Mobile Device
  • API (orbweaver_pbx.api.mobile): create_provisioning_qr, redeem_provisioning_token, refresh_voice_token, mobile_heartbeat, list_phonebook, list_recents, set_presence
  • Settings: PBX Settings → Twilio Push Credential SID (FCM)

Android features shipped (phases 1-4)

  • QR-scan provisioning (CameraX + ML Kit)
  • Outbound dial via Twilio Voice SDK
  • Inbound calls via FCM with full-screen ringing UI
  • InCall controls: mute, speaker, hangup
  • JWT auto-refresh worker (WorkManager, 30 min)
  • Phonebook search + tap-to-dial
  • Recents (Phone Message rows scoped to extension) + tap-to-dial
  • Presence/DND toggle
  • Sign-out + re-pair

CI

Both repos run on the beast64 self-hosted runner (no GitHub-hosted compute). Latest green: orbweaver_pbx run 26111008854; orbweaver_pbx_android run 26111577525 (signed-debug APK artifact).

Tracking

Project: PROJ-0002. Closed issues: orbweaver_pbx#9 #11, orbweaver_pbx_android#2 #3. Task: TASK-2026-00524.

Phase 5 — signed release + distribution

  • v0.1.0 tagged and published on the beast64 self-hosted runner.
  • Signing keystore lives in the runner home (0600); release builds fail fast without it so we never ship unsigned.
  • Tag-triggered .github/workflows/release.yml builds, apksigner-verifies, uploads to GitHub Releases, and rsyncs the APK to orbweaver.dev via a restricted softphone-publish user on wh1 (rrsync-jailed).
  • Public download: /softphone-downloadhttps://orbweaver.dev/files/softphone/orbweaver-softphone.apk

ESA 3CX cutover (2026-05-19)

The Emerald Shield Academy site (emeraldshield.org) was migrated off 3CX (ps1old.zonkhost.net) onto orbweaver_pbx. The number +19563688067 now routes through orbweaver_pbx's Default ESA IVR.

  • Importer: orbweaver_pbx.imports.threecx.run(source_dir, dry_run, target_did) — idempotent, two-pass.
  • Mapped: 20 extensions, 7 IVR menus + 16 options, 239 voicemails with audio, 94 phonebook entries.
  • Audio handling: all wav files saved via frappe.utils.file_manager.save_file per the file-upload feedback memory.
  • ps1old.zonkhost.net is now ready for decommissioning.

ESA 3CX cleanup (2026-05-19)

The initial multi-tenant import was scrubbed and re-applied with explicit ESA-only filters + the 1xxx/9xxx numbering convention. emeraldshield.org now has exactly 1 imported Extension (1000), 1 IVR Menu (9000, Default ESA IVR), and 72 voicemails on Extension 1000. Adrian's Extension 1001 was preserved.

Pattern for future tenant migrations off the same 3CX bundle:

run(source_dir, extensions=[''], ivrs=[''], renumber_extensions={'': '<1xxx>'}, renumber_ivrs={'': '<9xxx>'}, target_did='')
Was this article helpful?