Repository navigation
Development
| Path | What it is |
|---|---|
crates/protocol |
Mumble protocol: TLS control channel, protobuf messages, UDP voice crypto |
crates/audio |
Capture/playback (cpal), Opus, processing chain, jitter buffer, mixer, bitrate controller |
crates/store |
SQLite database, identity certificate, OS secure storage |
crates/engine |
App state, server session, voice, chat, follow, diagnostics. The only thing the app shell talks to |
crates/bot |
mumcord-bot: scripted Mumble bot for tests and manual checks |
crates/positional |
Placeholder for stage 1b (Mumble Link, plugin host) |
src-tauri/ |
Tauri app shell: commands, events, tray, deep links, push-to-talk, window handling |
ui/ |
React 19 + TypeScript frontend (ui/src/features/*, locales in ui/src/locales/{en,es,de}) |
e2e/ |
WebdriverIO end-to-end tests, run in Docker |
testenv/ |
Local Mumble servers (Docker) for tests |
tools/netsim |
Network-fault proxy for tests |
specs/ |
Specifications, brainstorming files, task lists, implementation reports |
docs/ |
engine-api.md: the contract between UI and engine |
scripts/ |
generate-notices.sh, smoke/ packaging smoke test |
Specifications are the source of truth. Each epic has specs/{nr}-{name}-specification.md; the Epic 1 spec is specs/01-foundation-specification.md. Only build what the spec says; if it is unclear or contradictory, ask instead of guessing. No scope creep. Every acceptance criterion (AC-…) must be verifiable in the running client against a local test server.
The project ships two Claude Code skills for this: /spec (brainstorming to specification) and /dev (build a specification). See Spec and Dev Workflow.
- Client only. Never build, require or modify anything on the server side (no companion services, plugins or server config changes). Every feature must work against an unmodified standard Mumble server. No private protocol extensions.
- Real-time audio. Never block the audio thread: no allocation, locks, I/O or logging in the real-time path.
- Separation. Protocol handling, audio pipeline and UI are separate modules. The UI never talks to the network directly; it talks to the engine through the Tauri commands.
- Security and privacy. TLS always on; verify server certificates and warn on changes; protect the client certificate. No personal data, chat contents or audio in logs. Treat everything from the server (names, comments, chat HTML) as untrusted and sanitize before rendering.
- Secrets never go into the repo. Use git-ignored local env files and CI variables.
- UI. Small components; separate state/data access from presentation. Keyboard operable, screen-reader labels, respects system theme and scaling.
-
Code. Simple over clever, no dead code, comments explain why not what, never swallow errors silently, strict typing. Rust lints:
unwrap_used,todoanddbg_macrowarn, and clippy runs with-D warnings. - Tests. New logic gets tests. A bug is first reproduced with a test, then fixed. Before "done": build, tests, type checks and linters pass.
- Never connect to public or third-party Mumble servers for testing.
cargo fmt --all --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked
npm run lint # eslint + prettier --check
npm run typecheck # tsc -b
npm test # vitest run
npm run format applies prettier.
- Conventional Commits (
feat:,fix:,docs:,chore:and so on) with a clear English message. - Small and focused: one logical change per commit. Stage specific files, never
git add -A. - Branch off the default branch.
mainis protected: changes go in through a pull request, and the required checks (CI and E2E) must pass before merging. Direct pushes tomainare not possible. - Tags starting with
vare protected too, so release tags cannot be moved or deleted casually.
Releases are built from tags. Bump the version (package.json, package-lock.json, Cargo.toml, Cargo.lock), merge that through a pull request into main, then push a tag vX.Y.Z on main. The Release workflow checks the tag against the versions, builds the Windows installer, the universal macOS DMG and the Linux AppImage, smoke-tests each, and creates a draft GitHub Release with the files and SHA256SUMS. Review and publish the draft by hand. The full steps, including what to do when the workflow fails, are in RELEASING.md in the repository.
Requires Docker with compose, openssl and sqlite3. Set up once:
cp testenv/.env.example testenv/.env # then edit the passwords; .env is git-ignored
Start and stop:
testenv/up.sh <v15|v14|v13|limit> [--cert A|B]
testenv/down.sh [service]
testenv/reset.sh [v15|v14|v13|limit] [--cert A|B]
| Service | Server | Port (TCP+UDP) |
|---|---|---|
v15 |
Mumble 1.5.735 | 64738 |
v14 |
Mumble 1.4.230 | 64739 |
v13 |
Mumble 1.3.x (Debian bullseye package, built locally on first start) | 64740 |
limit |
1.5.735 with Listen refused (listenersperchannel=0, listenersperuser=0) |
64741 |
The superuser is SuperUser with the SUPERUSER_PASSWORD from .env. Channels Lobby, Raid and AFK are created on first start. Autoban is disabled so tests can reconnect freely. up.sh --cert B swaps the server certificate to test the "certificate changed" warning. reset.sh wipes the database (only SuperUser stays registered) and interrupts connected clients.
A scripted Mumble client for tests. Each run connects once, runs one subcommand (or with script one per stdin line), and exits non-zero on the first error.
cargo run -p mumcord-bot -- --help
Global options include --host, --port (default 64738), --name, --cert (persistent certificate name), --superuser, --timeout and --hold. Subcommands include connect, join, say, pm, rename, avatar, comment, register, listen, unlisten, mute, deaf, play, record, expect (user, user-gone, message, denied and audio checks), sleep and script. Run --help for the full, current list.
A local TCP+UDP forwarding proxy that simulates UDP packet loss, latency, jitter, a UDP blackout and TCP connection kills, adjustable at runtime. It needs no root and behaves the same in CI.
cargo run -p netsim -- run --listen 127.0.0.1:64739 --target 127.0.0.1:64738 --control 127.0.0.1:64740
Those are the defaults. Note that they overlap with the v14 and v13 test server ports, so pass other values when those servers run (the E2E setup uses 64800 for the proxy and 64801 for control). Control it with:
netsim ctl loss [up|down|both] <pct>
netsim ctl latency <ms>
netsim ctl jitter <ms>
netsim ctl block-udp on|off
netsim ctl kill-tcp
netsim ctl reset
netsim ctl status
Use --control <addr> on ctl if the proxy listens on another control address.
Mumcord is licensed under the AGPL-3.0 (versions up to 0.1.1: MIT) · Source code · Website · Website source
Mumcord
Contributing