Files
BizGaze_Remote/TECHSTACK.md
T
SravanandClaude Opus 4.8 7ec6c3f227 docs(techstack): add Identity & auth section
Document the auth/identity subsystem: crypto-only (scrypt + TOTP MFA),
opaque sliding sessions over cookie/bearer/query transports, rotating
native refresh tokens, BizGaze IdP + signed SSO, roles & tenancy,
bzc_ API keys, server-side directory search, and the audit log.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-27 20:29:59 +05:30

200 lines
12 KiB
Markdown

# Tech Stack — BizGaze Connect
> Living document. **Last updated: 2026-10-27** (rev 2). Update this file whenever a dependency, platform, service, or
> tool is added/removed/version-bumped. See [Maintaining this file](#maintaining-this-file) and the
> [Changelog](#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`](https://github.com/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`) — planned for native-calling Increment 2.
## 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 (native LiveKit media = Increment 2, WIP).
- `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, target SDK 36, edge-to-edge; manifest patched in CI
(`mobile/scripts/android-patch.js`) since `mobile/android/` is generated fresh per build.
## 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-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.