/projects/mutiny
Self-hostable chat and voice

Mutiny.

A self-hostable chat and voice app for small communities, built on an open-source base.

I work on the web client, the native iOS app, and the Electron desktop app, plus the unglamorous stuff around them: call-state bugs, desktop packaging, and an admin dashboard for keeping an eye on the server.

1,669web repo commits, upstream history included
399iOS repo commits, upstream history included
4surfaces: web, iOS, macOS, Windows
1.2.8desktop version in the reviewed source

Counted from the repos in November 2026. Both commit totals include the upstream project's history, so most of those commits aren't mine. 1.2.8 is the version number in the desktop source.

01 · What I built

One chat app, three clients

Mutiny has text channels, direct messages, file sharing, voice rooms, and screen sharing. The web app is Solid.js. The desktop app wraps that same web client in Electron, and iOS gets its own native SwiftUI app.

I didn't start from zero. Mutiny sits on an existing open-source codebase, and most of my time goes into adapting it, wiring the clients to the server, and chasing bugs where what the screen shows drifts from what's actually happening underneath.

02 · Architecture

How chat, files, and voice reach the server

Chat goes through the API, with a WebSocket pushing events back. Attachments go to a separate file service. To join voice, the client asks the API for room access, then connects to LiveKit directly. The admin dashboard reads straight from MongoDB.

scroll to see the full diagram →

Mutiny clients, service data, and media paths Web, native iOS, and Electron clients use the API and event socket. Files go through a file service. Voice and screen media use LiveKit. MongoDB collections feed a separate administrative dashboard. This is a simplified architecture, not a feature-parity claim. CLIENTS Web Solid.js + mutiny.js Native iOS SwiftUI + LiveKit SDK Desktop Electron + web client macOS / Windows API / events APPLICATION SERVICE API + event socket Messages, channels, membership Voice room access SERVICE DATA MongoDB collections Users, channels, messages Admin metrics files FILE SERVICE Attachment upload + delivery Separate media endpoint REALTIME MEDIA LiveKit rooms Voice + screen sharing voice + screen media
Simplified. The API hands out voice room access before a client connects to LiveKit, and not every client supports every feature yet.
03 · What's in it

Chat, voice, and the apps around them

You see the same community in every client, but each platform has its own quirks to get right.

Conversation

Channels, direct messages, and files

Channels and DMs have replies, reactions, and attachments. Roles and invites control who gets in and what they're allowed to do.

  • Direct messages
  • Attachments
  • Roles + invites
Realtime media

Voice rooms and screen sharing

LiveKit carries the audio and video. On the web you click to join voice, and the floating call card has mute, deafen, and a preview of what you're sharing.

  • LiveKit
  • Screen preview
  • Call controls
Desktop

The web client in a desktop app

The Electron app adds what you'd expect from a desktop app: a tray icon, native notifications, and auto-updates. Packaging is its own job on each platform, since macOS and Windows sign and ship apps differently.

  • Electron
  • macOS + Windows
  • Auto-updates
Native iOS

A separate SwiftUI client

The iOS app has native message views, attachment previews, a notification extension, and LiveKit voice. It's still in development, and the release checklist has manual steps I haven't finished.

  • SwiftUI
  • Notifications
  • In development
04 · Operations and release

Getting a release out the door

I write release requirements and known failure cases into the repo so they get checked again whenever the code changes.

01

Make the call controls tell the truth

The web client watches LiveKit for track changes and reads the real microphone, camera, and screen-share state from it. Leaving a call clears all of that. Tests cover each of those paths, plus the join button.

02

Tell a local build from one you can ship

A build that runs on my own Mac can still fail on someone else's. The desktop signing guide spells out what a real release needs: a Developer ID signature, notarization, a ticket check, and a Gatekeeper check. Faked signing in tests can't stand in for that.

03

Give whoever runs the server a dashboard

I built a separate admin dashboard that shows how many accounts, messages, communities, and files there are. It reads from MongoDB and has a sample-data mode so I can work on it locally. Web releases also have to ship with public patch notes, and that's written down as a rule.

05 · For your team

What building it taught me

The work I like most is chasing one bug from the button someone clicked, through the app's state, all the way to the build they installed.

Client architectureWhen shared code helps

Electron gets the web interface for free. SwiftUI has to be built separately, so I keep a clear list of what each client supports and don't assume they match.

Realtime systemsKnowing what's actually live

Clicking share screen only means someone wants to share. LiveKit is what knows whether a track actually went out. The difference shows up the moment someone cancels the screen picker or drops out of a room.

Release engineeringTesting the thing you ship

Signing taught me to keep code tests and packaged-app checks apart. A green test run tells me nothing about whether Apple accepted the build I'm about to hand out.

OperationsAdmin tools can change things

The metrics view only reads data. The admin routes beside it can edit accounts and communities, so giving someone the dashboard gives them that power too.

06 · Limits

What this page doesn't claim

  • I didn't write the whole stack. Mutiny is built on an existing open-source codebase, and the commit counts include that history.
  • The three clients don't all do the same things yet. iOS is still in development, and its release checklist still has manual provisioning and domain setup to finish.
  • Mutiny isn't end-to-end encrypted. The server stores messages and uploaded files, as its privacy policy says. I'm not claiming any outside security audit or compliance certification.
  • Commit counts and version numbers don't show how many people use it or how it holds up under load. I haven't included user counts, revenue, uptime, or benchmarks.
Next step

If your team builds realtime or cross-platform apps, let's talk.

I'm looking at hands-on systems and product engineering roles. I'm happy to walk through the client code, the call-state handling, or the desktop release process live.

← Back to selected systems