
title: "OrbitDesk — How I Built a Modern Workplace Operations Lab with WebRTC Team Calls and No Backend"
published: true
tags: nextjs, webrtc, typescript, pwa, microsoft365, entraid, intune, msp, helpdesk
cover_image: https://orbitdesk-gamma.vercel.app/og-image.png
OrbitDesk — How I Built a Modern Workplace Operations Lab with WebRTC Team Calls and No Backend
Live: https://orbitdesk-gamma.vercel.app
Case Study: https://devine-nyaenya-portfolio.vercel.app/projects/orbitdesk
Repo: https://github.com/Nyaenya-Devine/orbitdesk (source-available noncommercial)
Portfolio: https://devine-nyaenya-portfolio.vercel.app
I build security systems that prove they are secure — with tests, audit logs, and verifiable controls. OrbitDesk is my flagship training lab for Modern Workplace operations — Entra ID, Intune, Exchange, Teams — built in Nairobi, Kenya.
This is not a replacement for production systems. It's a safe, audited simulator for IT support professionals, MSP team leads, and interview prep.
Why I Built It
MSP work is high-pressure: P1 payroll blocked by Conditional Access 53000, shared mailbox not showing in Outlook, BitLocker required per policy. You triage tickets, check sign-in logs CA tab, run dsregcmd, verify Company Portal sync, and communicate with appropriate language for client profile (enterprise vs SMB vs regulated).
Existing training is either theoretical or requires real tenant credentials. I wanted a lab that runs entirely in the browser, no backend required for core training, with realistic policies, SLAs, and voice communication — like a real shift.
What It Is (High-Level)
OrbitDesk simulates a Modern Workplace support shift:
- Queue: Tickets with priorities, SLA timers, client context (Enterprise 24/7, SMB business hours, Regulated strict)
- Investigate: Sign-in logs with Conditional Access What-If, Audit Logs, Service Health, Message Trace
- Remediate: Fixes in admin portal simulations, verify via remote desktop (dsregcmd /status, Get-BitLockerVolume, Company Portal sync)
- Communicate: Team and client messaging with quality feedback (empathy, clarity, technical, fluency, client language)
- Collaborate: Class-based workforce — team lead can call agents in same class, presence (online, in-call, offline)
All data simulated locally — no real credentials, no external API calls for core. Progress saves locally for interview stats.
Architecture — Local-First
Stack: Next.js 16 App Router, React 19, TypeScript strict, Tailwind CSS, Framer Motion, Web Speech API, Web Audio API, WebRTC.
- State: React hooks, localStorage for progress and class code, BroadcastChannel for cross-tab signaling
- Voice: Web Speech API for STT (SpeechRecognition) and TTS (speechSynthesis), Web Audio for ringtone (440Hz + 480Hz) and hold music (C4 E4 G4 C5)
- PWA: Offline support, installable, background sync — works offline after first load
- Desktop: Electron 32 with hardening (contextIsolation, sandbox), auto-update, NSIS installer
- Packaging: Play Store via TWA (PWABuilder), Microsoft Store via MSIX — packagable from PWA
No backend required for core training. Team calls use BroadcastChannel for signaling — same origin cross-tab, no server.
Voice & Team Calls — The Hard Part
Client Calls (VoiceCallCenter)
Level-based frequency to avoid spam — Lvl 1 no auto calls (focus tickets), Lvl 2 P1 critical only, scales to realistic MSP volume. Manual Call Now button always allowed.
When incoming: ringtone via AudioContext oscillators (440Hz + 480Hz bandpass), vibration via navigator.vibrate, browser Notification if granted. Accept creates active call with transcript, Web Speech for client voice, hold music via oscillators.
Crash I fixed: acceptCall had no try-catch around speechSynthesis and AudioContext — if browser blocks audio, page crashed with "This page couldn't load". Now wrapped in try-catch with fallback text mode, defensive id?.[0] and (transcript || []).map.
Team Calls (ClassCallDock + ClassCallCenter + classCallEngine)
This is the flagship collaboration feature.
Requirement: Team Lead can call agents in same class/workforce — like Teams/Slack. Same class code only (INFLUX-2026-A), presence, coaching, escalation.
Implementation:
ts
// Signaling — no backend
const channel = new BroadcastChannel('orbitdesk-class-calls');
channel.postMessage({ type: 'call-offer', callId, classCode, from, to, payload });
// WebRTC — peer-to-peer audio
const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
stream.getTracks().forEach(track => pc.addTrack(track, stream));
pc.onicecandidate = e => {
if (e.candidate) channel.postMessage({ type: 'ice-candidate', callId, candidate: e.candidate });
};
// Mock auto-answer 80% for demo (students s1-s4)
if (mockStudents.includes(to.id) && Math.random() < 0.8) {
setTimeout(() => answerCall(callId), 1500 + Math.random() * 2000);
}

Top comments (0)