Skip to content
MSRX Meeting logoMSRX Meeting

Under the hood

The technology behind MSRX Meeting

How a call gets from one browser to another without a media server, how the AI features work, and every open-source library, model and typeface the product is built from.

The shape of it

MSRX Meeting is a Next.js application that does almost all of its work in your browser. There is no media server, no database and no account system. The server side is a handful of stateless API routes that exist for one reason: to hold the AI provider keys so they never reach a browser.

A call is the browsers talking to each other. The site hands you the code, a signalling server introduces you to the other participants, and from then on audio, video and chat travel directly between devices.

The call: a WebRTC mesh

Every participant connects directly to every other participant — a full mesh. With 5 people, each browser sends its camera to the other 4 and receives 4 streams back. That is why the room has a cap: in a mesh the upload grows with every seat, and past a handful of people a home connection cannot keep up.

Media is encrypted with DTLS-SRTP, which WebRTC makes mandatory; there is no way to turn it off. Because no server sits in the media path, there is nothing in the middle that could decrypt it.

  • Signalling: PeerJS over a secure WebSocket. It carries connection offers and network candidates, never media.
  • NAT traversal: Google's public STUN servers, with an optional TURN relay for networks that block direct connections.
  • Video codecs: VP8/VP9 as negotiated by the browser. Audio: Opus with in-band forward error correction and DTX, so a lost packet is repaired without a retransmit and silence costs no bandwidth.
  • Quality presets from 360p to 1080p, each pairing a capture size with a sender bitrate cap, plus an Auto mode that leaves it to congestion control. The total upload budget is divided across however many people you are sending to.
  • Screen sharing through getDisplayMedia replaces the camera track in place on every connection, so nobody renegotiates and the presenter is spotlighted automatically.

Rooms without a server

A room link is twelve characters drawn from an alphabet without look-alike letters, generated by crypto.getRandomValues — 31¹² combinations, about 59 bits of randomness, far too many to guess. It is the room's only name; no registry of rooms exists anywhere.

The host's connection id is derived from the room id, so a guest can find the host with nothing but the link. The host admits each guest: it checks the passcode if there is one, enforces the seat cap and replies with the list of who is already there. The newcomer then calls each of them directly, completing the mesh.

An optional passcode travels in the link's fragment, the part after #, which browsers never send to any server — so it cannot end up in a web server's logs.

  • Room control — mute, remove, end meeting, the recording policy — is a typed message protocol over WebRTC data channels. Control messages are obeyed only when they come from the host's connection.
  • Every incoming message is parsed and shape-checked before it touches the app, and each peer has a token-bucket budget, so one misbehaving browser cannot flood a room.
  • Liveness: a heartbeat every 5 seconds over the data channel. A tab that is killed without saying goodbye is noticed and its seat freed.
  • Rooms end on their own after 5 hours, with warnings before that.

Captions, speaking rings and reactions

Live captions use the Web Speech API — the browser's own recogniser, which streams words as they are spoken and revises them as it hears more. The app asks for on-device recognition first; where the browser has none it falls back to its vendor's cloud, and a notice tells you which one is running.

The ring that lights up around whoever is talking is measured locally with a Web Audio AnalyserNode on the streams already playing: five loudness samples a second, on at the first loud one, off only after 700 ms of quiet so it does not flicker between words. Nothing about it is sent anywhere.

Raised hands and emoji reactions are data-channel messages, rate-limited per person.

The transcript: Whisper, with guardrails

When you switch transcription on, your browser records your own microphone in 6 seconds chunks with MediaRecorder. Before a chunk is uploaded, a voice-activity check measures how much of it is speech; below 12% voiced it is dropped, because Whisper invents words when it is given silence.

Chunks that pass go to Whisper large-v3, an open model, running on Groq. The request is steered with a short prompt carrying the participants' names, which fixes most name misspellings, and a language setting that stops each short chunk from being guessed as a different language.

What comes back is filtered twice: segments the model itself marks as probably not speech, or decodes with low confidence, are dropped, and a list of Whisper's well-known phantom phrases catches the rest. The finished line is sent to everyone over the mesh.

  • Each browser transcribes only its own microphone. No participant's device ever captures another participant's audio for the AI features.

Assistant, summary and rolling memory

Questions and summaries are answered by gpt-oss-120b, an open-weight model, also served by Groq. Summaries come back as structured JSON — decisions, risks, action items, next steps, sentiment — and are rendered by the app rather than shown as raw model text.

Long meetings never resend the whole transcript. Once 40 lines have piled up, the older ones are folded into a short digest, merged with the previous digest. Every AI request then carries the digest plus the last 30 lines verbatim, so a five-hour meeting costs about the same per question as a five-minute one.

Live highlights

The Highlights view picks out decisions, action items and open questions as they happen. It is built as a selection, not a generation: the code offers each transcript line to TypeSafe's Jev model, which judges which of the four kinds it is — including “none of these” — and returns a probability for each.

A line is marked only when that probability is at least 60%, because a false “decision” is worse than a missed one. What you see is always the speaker's own words with a label on them; nothing is paraphrased, so nothing can be invented.

Recording and exports, on your device

Recording composites every video tile onto a canvas and mixes every voice through Web Audio, then encodes it with MediaRecorder. It asks for MP4 with H.264 video and AAC audio first, because that is the format every phone and desktop player opens, and falls back to WebM where the browser records nothing else. The file downloads straight to your disk.

The transcript exports as plain text, the summary as Markdown or PDF. The PDF is built in the browser with react-pdf, loaded only when you ask for one.

The server, and what guards it

The API routes run as Node.js serverless functions on Vercel in Mumbai. They store nothing: no database, no files, no logs of what was said. Each one refuses requests from other sites, checks the size of the body before reading it, validates it against a schema and rate-limits per address and per instance, cheapest check first.

Every page is served with a strict Content-Security-Policy that allows connections only to this site and the signalling server, HSTS with preload, a Permissions-Policy that grants camera, microphone and screen capture to this origin alone and denies everything else, and cross-origin isolation headers so no other window can get a handle on a tab with a live camera.

How it is tested

Vitest covers the logic that is easy to get subtly wrong: rolling memory, the Whisper filters, the rate limiter, caption timing, the mesh message parser, the peer budgets and the promises made on the privacy page. Playwright opens several real Chromium tabs with fake cameras, joins them to the same room and checks the mesh, captions, recording, controls and reconnection end to end.

37 packages · all open source

Every library, with its version and license

The exact versions this build installs, read from its lockfile. Packages marked as build tooling are used to make and test the site; they never reach your browser.

Framework and runtime

  • next16.4.2MIT

    App Router, server-rendered pages, the API routes and the security headers. Built with Turbopack.

  • react19.1.0MIT

    Every screen, from the landing page to the video grid.

  • react-dom19.1.0MIT

    Renders React to the page.

  • server-only0.0.1MIT

    Fails the build if code holding an API key is ever imported into the browser bundle.

Real-time calling

  • peerjs1.5.5MIT

    Wraps WebRTC: signalling, media calls and the data channels that carry chat, captions and room control.

  • qrcode.react4.2.0ISC

    Draws the invite QR code, in the browser, so the link is never sent to a QR service.

AI

  • groq-sdk1.2.1Apache-2.0

    Server-side client for transcription, the assistant, summaries and the rolling digest.

State, forms and validation

  • zustand5.0.14MIT

    Nine small stores: meeting, chat, transcript, captions, AI, reactions, speaking, settings and UI.

  • zod4.6.7MIT

    Validates the JSON bodies the AI routes accept, before any of it reaches a model.

  • Installed but not currently imported: the join form dropped it to keep the landing page bundle small.

  • Installed alongside react-hook-form, and unused for the same reason.

Interface

  • shadcn4.22.1MIT

    Source for the dialog, sheet, menu, tabs and other primitives, copied into the repo and restyled.

  • Accessible, unstyled behaviour underneath those primitives: focus trapping, keyboard handling, ARIA.

  • lucide-react1.17.0ISC

    Icons.

  • sonner2.0.7MIT

    Toasts: someone joined, recording started, a duration warning.

  • next-themes0.4.6MIT

    Theme plumbing for the toaster.

  • Typed style variants for buttons and badges.

  • clsx2.1.1MIT

    Conditional class names.

  • Resolves conflicting Tailwind classes.

  • Enter and exit animations for dialogs and sheets.

Export and analytics

  • Builds the summary PDF inside the browser. Loaded only when someone asks for a PDF.

  • Cookieless page-view counts. Meeting rooms are dropped in the browser before anything is sent.

Styling and buildBuild tooling

  • tailwindcss4.3.0MIT

    Utility CSS, with the colour palette defined as OKLCH design tokens.

  • Runs Tailwind inside the Next.js build.

  • typescript5.9.3Apache-2.0

    Strict types across client, server and the mesh message protocol.

  • eslint9.39.4MIT

    Linting.

  • Next.js and React Hooks lint rules.

TestingBuild tooling

Open models and typefaces

  • Whisper large-v3large-v3MIT

    Speech-to-text for the meeting transcript. Open weights from OpenAI, served by Groq.

  • gpt-oss-120b120bApache-2.0

    The in-meeting assistant, the summary and the rolling digest. Open weights from OpenAI, served by Groq.

  • IntervariableOFL-1.1

    Body text. Self-hosted through next/font, so no request goes to Google Fonts.

  • FrauncesvariableOFL-1.1

    Display headings, using its optical-size axis. Self-hosted the same way.

Services it runs on

The code is open source; not every service it talks to is. These are the outside services involved, and which of them are open. What each one receives is set out on the privacy page.

  • PeerJS cloudRuns the open-source peerjs-server (MIT)

    Signalling only: introduces browsers so they can connect directly. Replaceable with a self-hosted server by configuration.

  • Google STUNOpen protocol (RFC 8489), Google-operated

    Tells each browser its public address so peers can reach it. Sees no call content.

  • GroqHosted inference, serving the open models above

    Runs Whisper and gpt-oss-120b for the AI features, only after you switch them on.

  • TypeSafeHosted, proprietary

    Its Jev model labels transcript lines as decisions, action items or open questions while Highlights is open.

  • Browser speech serviceWeb Speech API (W3C), engine chosen by your browser

    Live captions. On-device recognition is requested first; the browser falls back to its vendor's cloud where it has none.

  • VercelHosted, proprietary

    Serves the site and runs the API routes in the Mumbai region (bom1).

With thanks to the people who maintain every project on this page.