Thanks for your interest in contributing to ChatMux! Before you start, please take a moment to read through this guide.
- Search first. Check existing issues and pull requests to avoid duplicating work.
- Discuss first for new features. Open an issue to discuss your idea before investing time in implementation. We may already have plans or opinions on how it should work.
- Bug fixes are always welcome. If you spot a bug, feel free to open a PR directly.
- Node.js
22.22.2+on the 22.x line or24.15.0+on the 24.x line - npm
- Git
- tmux
- Rust
1.85+ - At least one supported coding-agent CLI installed and configured
- Fork the repository.
- Clone your fork:
git clone https://github.com/<your-username>/chatmux.git cd chatmux
- Install dependencies:
npm ci
- Start the development server:
npm run dev
- Create a branch for your changes:
git checkout -b feat/your-feature-name
chatmux/
├── src/ # React frontend (Vite + Tailwind)
│ ├── components/ # UI components
│ ├── contexts/ # React context providers
│ ├── hooks/ # Custom React hooks
│ ├── i18n/ # Internationalization and translations
│ ├── lib/ # Shared frontend libraries
│ ├── types/ # TypeScript type definitions
│ └── utils/ # Frontend utilities
├── server/ # Express backend
│ ├── routes/ # API route handlers
│ ├── middleware/ # Express middleware
│ ├── modules/ # Domain routes, database, providers, fleet, and WebSocket
│ └── shared/ # Backend contracts and utilities
├── shared/ # Code shared between client and server
└── public/ # Static assets, icons, PWA manifest
npm run dev— Start both the frontend and backend in development modenpm run build— Create a production buildnpm run server— Start only the backend servernpm run client— Start only the Vite dev server
- Reference the issue number in your PR if one exists
- Describe how to reproduce the bug in your PR description
- Add a screenshot or recording for visual bugs
- Keep the scope focused — one feature per PR
- Include screenshots or recordings for UI changes
- Documentation improvements are always welcome
- Keep language clear and concise
- Keep installation and self-hosting instructions aligned with the self-hosting guide
- Multi-PC documentation must preserve the shipped contract in REMOTE-ACCESS.md §8 and Fleet RFC revision 6: one hub plus at most nine full peers, owner-only enrollment, Tailscale HTTPS/WSS by default, and only literal loopback
ws://behind an owner-managed or explicitly requested hub-managed SSH forward. Optional SSH bootstrap requires owner opt-in and a missing installation; it never updates or repairs an existing peer. Do not describe a relay, downgrade, automatic failover, fleet updater, cloud sync, remote desktop/IDE, arbitrary commands, or zero-configuration reachability.
We follow Conventional Commits to generate release notes automatically. Every commit message should follow this format:
<type>(optional scope): <description>
Use imperative, present tense: "add feature" not "added feature" or "adds feature".
| Type | Description |
|---|---|
feat |
A new feature |
fix |
A bug fix |
perf |
A performance improvement |
refactor |
Code change that neither fixes a bug nor adds a feature |
docs |
Documentation only |
style |
CSS, formatting, visual changes |
chore |
Maintenance, dependencies, config |
ci |
CI/CD pipeline changes |
test |
Adding or updating tests |
build |
Build system changes |
feat: add conversation search
feat(i18n): add Japanese language support
fix: redirect unauthenticated users to login
fix(editor): syntax highlighting for .env files
perf: lazy load code editor component
refactor(chat): extract message list component
docs: update self-hosting guideAdd ! after the type or include BREAKING CHANGE: in the commit footer:
feat!: redesign settings page layout- Give your PR a clear, descriptive title following the commit convention above
- Fill in the PR description with what changed and why
- Link any related issues
- Include screenshots for UI changes
- Make sure the build passes (
npm run build) - Keep PRs focused — avoid unrelated changes
ChatMux uses trunk-based development: main plus short-lived branches. There is no long-lived dev branch — main must stay releasable at all times, and releases are cut from it by explicit workflow dispatch.
- Every change goes through a PR, including documentation and release bookkeeping, even from maintainers. Create a short-lived branch (
fix/...,feat/...), open a PR, let CI pass, then squash-merge and delete the branch. PR titles follow the commit convention above — release notes are generated from merged PR titles. - Group related small fixes into one PR; keep unrelated changes out.
- Never batch-merge a long-lived integration branch into
main. If a change is too big for one PR, land it as a series of PRs that each keepmaingreen. - Branch protection enforces the gate:
mainrequires thePR policy,Verify Node 22,Verify Node 24, andCanonical server bundlechecks to pass before a PR can merge. These checks must be from the latestmain, force-pushes/deletions are blocked, and zero human approvals are required. - Do not use routine administrative bypasses. Re-run a proven CI-infrastructure flake or fix the source condition instead.
Fleet-capable releases are rolled out hub first, then one full peer at a time. Verify the hub's direct UI and peer inventory after the hub update. After each peer update, wait for it to leave Syncing and return Online before continuing. Incompatible is a hard stop: use a supported version pairing or the documented manual recovery; never downgrade the transport or bulk-update the remaining peers. There is no fleet-wide update command, and every peer retains its own updater and direct recovery UI.
Cutting a release (maintainers):
- Open one atomic release-prep PR containing the compatibility declaration in
packaging/release/update-compatibility.jsonand the version and lockfile update (npm version X.Y.Z --no-git-tag-version). Recorddatabase.schemaGenerationfrom the registered migration count reported bynpm run release:check-metadata. When the database schema is unchanged, carry forward every prior version sharing that schema inrollbackCompatibleFrom(the release workflow proves each declared entry); reset the list to only the immediately preceding version only when the registered schema generation actually increases. A single-entry declaration on an unchanged schema strands any install more than one release behind inmanual_required(#49). Never edit the entries of already-published releases: their metadata is anchored to the immutable predecessor tag, and preflight rejects any divergence. GitHub-generated release notes are canonical; do not maintain a second hand-written release-note source. - Merge the release-prep PR only after the required checks run on latest
main. - Dispatch the Release ChatMux Server workflow from
main(gh workflow run "Release ChatMux Server" --ref main). The workflow rejects duplicate, stale, and non-increasing stable versions before it verifies, builds, proves rollback compatibility, and publishes the GitHub Release with the tag.npm run cua:releaseis manual release evidence and recommended before dispatch; it does not gate publication.
The release-preflight job enforces compatibility-declaration completeness before anything is built: npm run release:check-metadata (scripts/release/check-release-metadata.mjs) checks that package.json and both package-lock.json version fields agree, derives the schema generation from the ordered migration registry, requires that exact generation in the target compatibility entry, and anchors the predecessor and its rollback window to metadata published at the predecessor tag. The predecessor's schema generation is derived from the migration registry source at the predecessor tag rather than trusted from mutable metadata, which also bootstraps releases whose predecessor predates schemaGeneration (v1.8.14 and earlier record none). A singleton predecessor is accepted only when the schema generation increases; otherwise every version declared by the predecessor plus the predecessor itself must be carried forward. Local runs use the in-repository history and the recorded predecessor generation with a warning because no canonical predecessor files are available.
The scripts/chatmux.sh commands below are for the retained source-deployment
lifecycle, not the canonical release publication or installation path. When
maintaining such a deployment, pin an approved immutable commit SHA. Canonical
server releases are published only by the workflow above and installed from the
GitHub Release artifacts described in INSTALL.md.
./scripts/chatmux.sh status
./scripts/chatmux.sh update --ref <approved-full-commit-sha>See the self-hosting guide for installation, updates, rollback, and service operations.
By contributing, you agree that your contributions will be licensed under the AGPL-3.0-or-later License, including the additional terms specified in Section 7 of the LICENSE file.