Files
BizGaze_Remote/TECHSTACK.md
T
SravanandClaude Opus 4.8 7cd3c3afff android(build): inject JitPack repo so LiveKit's audioswitch fork resolves
Codemagic build failed at :app:checkDebugAarMetadata — "Could not find
com.github.davidliu:audioswitch". LiveKit Android 2.29.2 pulls in that
JitPack-only fork (commit-hash version), and Gradle resolves the whole
:app transitive graph against the APP module's (+ allprojects) repos, not
the native-call plugin's. The plugin-level jitpack repo was therefore
ignored. android-patch.js now injects `maven { url 'https://jitpack.io' }`
into the generated root build.gradle's allprojects{} (and settings.gradle
dependencyResolutionManagement{} if a newer template uses it). Idempotent,
build-tolerant. TECHSTACK rev 3 note updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-30 19:11:23 +05:30

14 KiB

Tech Stack — BizGaze Connect

Living document. Last updated: 2026-10-30 (rev 3). Update this file whenever a dependency, platform, service, or tool is added/removed/version-bumped. See Maintaining this file and the Changelog at the bottom.

What this project is

BizGaze Connect is a self-hosted team chat + meetings + calling product (evolved from a remote screen-sharing/"remote-access" tool, whose agent console still lives in the repo). One backend serves a web app and is wrapped by native mobile (Capacitor) and desktop (Electron) shells. The UI loads live from the server, so web changes deploy without rebuilding the native apps.

Architecture style: thin, dependency-light Node backend (custom modules, no web framework) + a single-page vanilla-JS web UI + native shells that wrap the web and add native capabilities (push, calling, screen capture, file access). Real-time via WebSockets; meeting media via WebRTC (P2P mesh, or LiveKit SFU when enabled).


Backend (server)

  • Runtime: Node.js — engines requires >= 22.5; the Docker image is node:24-alpine.
  • HTTP/HTTPS: Node built-in http/https modules. No Express/Koa/Fastify — a small custom router (routes.js) maps "METHOD /path" to handlers; static files + authenticated downloads via static.js.
  • WebSockets: ws ^8.18 (WebSocketServer on /ws) — chat, presence, meeting signaling (SDP/ICE relay + consent).
  • Structure (server/): server.js (wiring), routes.js (HTTP JSON API), repos.js (all SQL / data access), signaling.js (WS), calls.js, chat.js, presence.js, session.js/auth.js, push.js, livekit.js, media.js, mailer.js, webhooks.js, bizgaze.js (BizGaze identity provider), directory.js, config.js.
  • Media tooling: ffmpeg (in the image) for server-side video poster thumbnails.

Data layer

  • Database: PostgreSQL 16 (pg ^8.13). This is the only backend — SQLite was retired 2026-08-12; one schema source of truth at server/db/schema.pg.sql. Selected via DB_BACKEND=pg / DATABASE_URL.
  • DB access: hand-written SQL through an async adapter (server/dbx.js, server/db/), centralized in repos.js. No ORM.
  • Cache / pub-sub: Redis 7 (redis ^4.7) for cross-instance real-time fan-out (chat/presence) — optional, only when PUBSUB_BACKEND=redis; default is in-memory single-instance (pubsub.js).

Identity & auth

No external auth library — Node's built-in crypto only (server/auth.js): scrypt password hashing (64-byte hash, per-user salt, timing-safe compare), opaque random session tokens, SHA-256-hashed token storage, and TOTP MFA (RFC 6238, SHA-1, 30s, 6 digits) with otpauth:// enrollment URLs.

  • Sessions (sessions_auth table, resolved in server/session.js): one opaque bearer token accepted over three transports — a sid HttpOnly cookie (web), Authorization: Bearer <token> (native desktop/mobile
    • WS upgrade), or a ?access_token=/?token= query param (browser WS fallback). 90-day TTL, sliding — any /api/me hit re-stamps the cookie and pushes the expiry out, so active users stay signed in indefinitely and only lapse after 90 days of no use (or logout).
  • Native refresh tokens (refresh_tokens): 90-day, one-time-use with rotation, stored only as a hash; POST /api/auth/refresh swaps a refresh token for a fresh access token so mobile/desktop never re-prompt for a password.
  • MFA: optional TOTP second factor. A requireMfa gate in currentUser holds a session back until mfa_passed; flow is /api/mfa/setup (secret/QR) → /api/mfa/enable (confirm) → /api/login/mfa (step-2 code).
  • Identity providers:
    • Local password accounts — dev/test and self-contained runs.
    • BizGaze as IdP (server/bizgaze.js, active when BIZGAZE_LOGIN_URL is set): credentials are verified against BizGaze's ValidateAndLogin and BizGaze becomes the sole authority (local passwords rejected unless ALLOW_LOCAL_LOGIN=1). A local user row is provisioned/synced (name, avatar, role, tenant) on every login, keyed on the BizGaze person-id (not the typed email/phone) so signing in with either identifier resolves to one account — legacy duplicates are auto-merged.
    • BizGaze SSO (GET /sso): an HMAC-SHA256-signed, time-boxed token (SSO_SECRET) signs an agent straight in (auto-provisioning the user) and redirects to /connect.
  • Roles & tenancy: every user belongs to a team (tenant); roles are admin / technician / viewer (default technician). Admins manage members (/api/users, /api/users/manage: add / reset-password / rename / activate / deactivate / delete) and see tenant-wide views. Role is synced from BizGaze isAdmin each login; ADMIN_EMAILS is a lockout safety-net.
  • Registration (POST /api/register): first run creates the team + admin user; closed afterward unless ALLOW_REGISTRATION=1.
  • Third-party API keys (api_keys, apiKeyFromReq): bzc_-prefixed, stored hashed, comma-scoped (* = all), presented via X-API-Key or Authorization: Bearer bzc_… — for the roadmap's 3rd-party integration.
  • Directory search (server/directory.js, /api/directory/search): server-side BizGaze cross-tenant user lookup; the directory auth token stays server-side and is never exposed to the browser.
  • Audit log (audit_log): logins, SSO/BizGaze provisioning, account merges, and every admin action on a member are recorded.

Real-time & media

  • WebRTC for calls/meetings: built-in P2P mesh by default; LiveKit SFU when configured.
  • LiveKit SFU server: livekit/livekit-server:**v1.9** (Docker, sfu compose profile, dormant unless LIVEKIT_* env is set). Server mints per-user JWT join tokens (server/livekit.js).
  • LiveKit clients:
    • Web: self-hosted livekit-client ~2.20 UMD (server/public/vendor/livekit-client.umd.min.js).
    • iOS: LiveKit Swift SDK 2.15.3 (SPM, client-sdk-swift, product LiveKit).
    • Android: LiveKit Android SDK io.livekit:livekit-android 2.29.2 (+ com.twilio:audioswitch for output routing, Kotlin coroutines) — native call audio + Speaker/Bluetooth/Earpiece (Increment 2a); native video is Increment 2b. Needs the JitPack repo; raises the app to minSdk 24.

Web client (server/public/)

  • Vanilla JavaScript SPA — home.html is the app (large single file with inline JS). No framework, no bundler, no build step. Supporting files: icons.js (shared Lucide icon set via ic()), connect.html (embed), sw.js (service worker), bizconnect-toast.js.
  • PWA: installable; service worker for Web Push + offline shell.
  • Icons: Lucide (shared set, rendered through ic()); no emoji as control icons.

Mobile app (mobile/) — Capacitor

  • Framework: Capacitor 8 (@capacitor/core ^8) — a native shell (iOS WKWebView / Android WebView) loading the remote web UI from https://remote.bizgaze.com. Not Flutter/React Native.
  • Official plugins (^8): App, Camera, Filesystem, Keyboard, Share, Push Notifications, Splash Screen, Status Bar; community @capacitor-community/safe-area ^8; @capacitor/assets ^3 (icons/splash).
  • Local/custom plugins (mobile/plugins/):
    • native-call — native calling. iOS: Swift, CallKit + PushKit (VoIP) + LiveKit Swift SDK (native media, video tiles, screen-share via a ReplayKit broadcast extension), on-device live transcription (SFSpeechRecognizer). Android: Java, full-screen incoming-call UI over the lock screen + a FirebaseMessagingService woken by high-priority FCM data messages. Increment 2a adds native LiveKit audio (Kotlin CallMedia + a microphone foreground service for background audio + AudioSwitchHandler routing), gated by the server flag ANDROID_NATIVE_MEDIA; native video = Increment 2b.
    • audio-route (iOS audio output routing), media-library (save to Photos/gallery), share-inbox (inbound share sheet), file-opener (native Quick Look / open-with).
  • iOS build: Capacitor 8 + SPM (CocoaPods retired); Xcode 26; a share extension + a broadcast extension.
  • Android build: JDK 21, AGP 8.7 / Gradle 8.11, minSdk 24 (raised for LiveKit), target SDK 36, edge-to-edge; the native-call plugin module also applies kotlin-android for the LiveKit media layer. mobile/android/ is generated fresh per build, so permissions, the minSdk bump, and the JitPack repository (allprojects/dependencyResolutionManagement) that LiveKit Android's transitive com.github.davidliu:audioswitch fork needs are all applied in CI by mobile/scripts/android-patch.js.

Desktop app (desktop/) — Electron

  • Electron 31 + electron-builder 24 (NSIS/Windows, DMG/mac, AppImage/Linux) + electron-updater 6 (auto-update from https://remote.bizgaze.com/downloads/). Wraps the web UI and adds native screen capture. Optional @nut-tree-fork/nut-js for input automation.

Native host agent (agent/) — Electron

  • Legacy remote-access agent (remote-access-agent): Electron 31 host for screen capture, WebRTC, consent, and OS input injection (@nut-tree-fork/nut-js), signaling over ws.

Push notifications

  • Web Push via VAPID (web-push ^3.6) for browsers/PWA.
  • Android: Firebase Cloud Messaging (FCM) — Firebase project bizgaze-connect; client google-services.json ships in the APK; server uses a service-account key (base64 in .env). High-priority data messages wake the native-call plugin for incoming calls.
  • iOS: Apple Push Notification service (APNs) over HTTP/2 with a token (.p8) auth key, including VoIP pushes (PushKit → CallKit) for incoming calls.

Infrastructure & deployment

  • Containers: Docker + Docker Compose (Dockerfile, docker-compose.yml). Services: app (Node), bizgazepg (Postgres 16), bizgazeredis (Redis 7, scale profile), livekit (SFU, sfu profile).
  • Reverse proxy / TLS: Nginx Proxy Manager (external, shared nginx_proxy_manager_default network) — terminates HTTPS and proxies remote.bizgaze.com → app:8090 and wss://livekit.bizgaze.com → LiveKit.
  • Persistence: named Docker volumes — app data (/data: uploads, recordings, transcripts, downloads) and Postgres data.
  • Deploy flow: push to master (Gitea remote code.bizgaze.com) → redeploy.sh (plink/SSH to the host) → server-side deploy.sh (git pull + docker rebuild + recreate). Secrets live in a gitignored .env on the server (/opt/bizgaze-support/.env); see .env.example.
  • CI (mobile builds): Codemagic (codemagic.yaml) — iOS (mac_mini_m2, SPM, publishes to TestFlight) and Android (mac_mini_m2, Node 22, JDK 21, produces an installable debug APK).

External integrations

  • BizGaze SSO / identity provider + outbound webhook (bizgaze.js, webhooks.js).
  • SMTP email via Nodemailer ^6.9 (optional) — meeting invites to external participants.
  • GIPHY GIF search (server-proxied, optional, key-gated).
  • TURN/STUN for WebRTC NAT traversal (credentials via .env).

Dev & test tooling

  • Local DB for tests: PostgreSQL 16 (local).
  • Browser automation: puppeteer-core driving local Chrome (scratch testing).
  • Android debugging: adb (platform-tools).
  • Shell/OS: developed on Windows; Git Bash + PowerShell; deploy via plink (PuTTY).

Repo layout (key directories)

Path What
server/ Node backend (API, WS, data access, push, media)
server/public/ Vanilla-JS web UI (home.html, icons.js, sw.js, vendor/)
server/db/ PostgreSQL schema + DB adapter
mobile/ Capacitor app (iOS/Android)
mobile/plugins/ Custom Capacitor plugins (native-call, audio-route, media-library, share-inbox, file-opener)
desktop/ Electron technician desktop client
agent/ Legacy Electron remote-access host agent
Dockerfile, docker-compose.yml, livekit.yaml Deploy stack
codemagic.yaml Mobile CI (iOS + Android)
redeploy.sh, deploy.sh Deployment
ARCHITECTURE.md, DEPLOY.md, CLIENTS.md, README.md Docs

Maintaining this file

Update this document in the same commit as any change that alters the stack, specifically when you:

  • add/remove a dependency, or bump a major version (server package.json, mobile/package.json, desktop/package.json, agent/package.json, plugin manifests);
  • change a platform/toolchain version (Node, Postgres, Redis, LiveKit, Capacitor, Electron, JDK, Xcode);
  • add/remove a service in docker-compose.yml or a build workflow in codemagic.yaml;
  • add a new custom Capacitor plugin or a new external integration.

Bump the Last updated date at the top and add a line to the Changelog below.

Changelog

  • 2026-10-30 (rev 3) — Android native call media (Increment 2a): added io.livekit:livekit-android 2.29.2 + com.twilio:audioswitch + Kotlin to the native-call plugin, a microphone foreground service, the ANDROID_NATIVE_MEDIA server flag, and raised the Android app to minSdk 24. Build fix: android-patch.js now injects the JitPack repo (LiveKit's com.github.davidliu:audioswitch fork resolves nowhere else).
  • 2026-10-27 (rev 2) — Added the Identity & auth section (sessions/cookies + bearer, refresh tokens, TOTP MFA, BizGaze IdP/SSO, roles & tenancy, API keys, directory, audit).
  • 2026-10-27 — Initial tech-stack document created.