docs: tighten public copy, add comparison, templates, and social preview

* docs: tighten README, CONTRIBUTING, and CLA prose

* docs: add platform comparison and README FAQ

* chore: add issue and pull request templates

* chore: add repository social preview image
This commit is contained in:
TheZwiss
2026-07-07 23:39:33 +02:00
committed by GitHub
parent 95b545d8b2
commit 3513a3dde9
9 changed files with 420 additions and 129 deletions
+82
View File
@@ -0,0 +1,82 @@
name: Bug report
description: Report something that is broken or behaving incorrectly.
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to file a bug. Please search existing issues first to avoid duplicates.
For security vulnerabilities, do not open a public issue. See SECURITY.md and report privately.
- type: textarea
id: what-happened
attributes:
label: What happened
description: A clear description of the bug and what you expected instead.
placeholder: When I do X, Y happens. I expected Z.
validations:
required: true
- type: textarea
id: repro
attributes:
label: Steps to reproduce
description: The exact steps that trigger the problem.
placeholder: |
1. Go to ...
2. Click ...
3. See ...
validations:
required: true
- type: dropdown
id: deploy-mode
attributes:
label: Deployment mode
description: How is this instance deployed?
options:
- All-in-One (bundled Caddy)
- Behind my own reverse proxy
- Behind a tunnel (Cloudflare, Tailscale)
- Local development (pnpm dev)
- Not sure
validations:
required: true
- type: dropdown
id: surface
attributes:
label: Where did it happen
description: Which client were you using?
options:
- Web browser
- Desktop app (Electron)
- Installed PWA on mobile
validations:
required: true
- type: input
id: version
attributes:
label: Backspace version
description: The version shown in the app, or the image tag / commit you deployed.
placeholder: "1.0.0"
validations:
required: true
- type: dropdown
id: subsystems
attributes:
label: Does it involve voice or federation
description: These subsystems have their own moving parts, so it helps to know up front.
multiple: true
options:
- Voice or video (LiveKit)
- Screen sharing
- Federation between instances
- None of these
- type: textarea
id: logs
attributes:
label: Relevant logs
description: Server logs (docker compose logs backspace) or browser console output, if any. This is automatically formatted as code.
render: shell
- type: input
id: environment
attributes:
label: Browser and OS
placeholder: "Firefox 128 on Ubuntu 24.04, or Chrome 126 on Windows 11"
+8
View File
@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Security vulnerability
url: https://github.com/TheZwiss/backspace/security/advisories/new
about: Do not open a public issue for security problems. Report privately here.
- name: Question or setup help
url: https://github.com/TheZwiss/backspace/discussions
about: For usage questions, deployment help, and general discussion.
@@ -0,0 +1,56 @@
name: Feature request
description: Suggest a new capability or an improvement to an existing one.
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Open an issue before starting work on anything non-trivial, so we can agree on direction first.
Please check the README feature list and existing issues before filing.
- type: textarea
id: problem
attributes:
label: The problem
description: What are you trying to do that Backspace does not support today?
placeholder: As a space admin, I want to ... so that ...
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed solution
description: How you imagine it working. Rough ideas are fine.
validations:
required: true
- type: dropdown
id: area
attributes:
label: Area
description: Which part of the project does this touch?
options:
- Text chat and messaging
- Voice, video, or screen sharing
- Spaces, roles, and permissions
- Direct messages and friends
- Federation between instances
- Admin and moderation
- Desktop app
- Mobile and PWA
- Deployment and self-hosting
- Other
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives considered
description: Workarounds you have tried, or how other tools handle this.
- type: dropdown
id: federation-impact
attributes:
label: Federation compatibility
description: Would this need to work across peered instances? If unsure, leave the default.
options:
- Not sure
- Yes, it should work across federated instances
- No, it is local to a single instance
+28
View File
@@ -0,0 +1,28 @@
<!-- Thanks for contributing. Keep one logical change per pull request. -->
## What this changes
<!-- A short summary of the change and the problem it solves. Link the issue it addresses. -->
Closes #
## Type of change
- [ ] Bug fix
- [ ] New feature
- [ ] Refactor or cleanup
- [ ] Documentation
- [ ] Other
## Checklist
- [ ] `pnpm build` succeeds (shared types, server, and web all build)
- [ ] `pnpm dev` starts the server and web client without errors
- [ ] Tests pass where applicable (`pnpm test`)
- [ ] I updated the relevant `docs/systems/` spec if this changed schema, API routes, WebSocket events, the federation protocol, permissions, or the design system
- [ ] This change resolves the correct federated identity where it compares IDs, checks permissions, or talks to remote servers (no assumption of a single global user ID)
- [ ] I have read and agree to the [CLA](../CLA.md)
## Notes for reviewers
<!-- Anything that helps review: screenshots for UI, migration notes, edge cases, follow-ups. -->
+7 -7
View File
@@ -19,8 +19,8 @@ submit.
**"Contribution"** means any original work of authorship, including any
modification of or addition to an existing work, that You intentionally submit
to the Project in any form including source code, object code, documentation,
configuration, designs, or assets through any means, including pull requests,
to the Project in any form (including source code, object code, documentation,
configuration, designs, or assets) through any means, including pull requests,
patches, issues with attached code, or electronic communication, but excluding
any communication You conspicuously mark in writing as "Not a Contribution".
@@ -36,7 +36,7 @@ fully sublicensable, and transferable license to use, reproduce, modify, prepare
derivative works of, publicly display, publicly perform, distribute, relicense
(under any terms, including open-source and commercial licenses), sell, and
otherwise exploit your Contributions, by all means and in all media now known or
later developed so that the Maintainer may offer the Project under both the
later developed, so that the Maintainer may offer the Project under both the
GNU AGPL-3.0 and one or more commercial licenses.
This grant covers all Contributions You have already submitted and all
@@ -46,17 +46,17 @@ Contributions You submit in the future, effective at the moment each is created.
To the extent that the exclusive license in Section 2 is, in any jurisdiction,
narrower than stated or otherwise limited by law, You grant the Maintainer the
broadest license permissible there, and only to the extent necessary to give
the Maintainer equivalent rights assign such rights to the Maintainer, so that
broadest license permissible there, and, only to the extent necessary to give
the Maintainer equivalent rights, assign such rights to the Maintainer, so that
the Maintainer obtains, as nearly as possible, the same rights as the exclusive
license in Section 2.
## 4. Patent License
You grant the Maintainer a perpetual, worldwide, non-exclusive, royalty-free,
irrevocable, sublicensable patent license under any patent claims You can
irrevocable, sublicensable patent license (under any patent claims You can
license that are necessarily infringed by your Contribution alone or by
combination of your Contribution with the Project to make, have made, use,
combination of your Contribution with the Project) to make, have made, use,
offer to sell, sell, import, and otherwise transfer your Contributions and the
Project.
+8 -8
View File
@@ -2,7 +2,7 @@
Thanks for considering a contribution! Backspace is free and open source software
(GNU AGPL-3.0, with a commercial dual-license option), and contributions of all
sizes are welcome bug reports, fixes, features, documentation, and design.
sizes are welcome: bug reports, fixes, features, documentation, and design.
## Before you start
@@ -22,7 +22,7 @@ Before your first contribution can be merged, you must sign the project's
Backspace is a single-owner project. Under the CLA **you keep the copyright to
your contribution** and grant the maintainer (Jannis Braun) an exclusive,
sublicensable license to it which is what lets the project be offered under both
sublicensable license to it, which is what lets the project be offered under both
the AGPL and a commercial license. In return, you receive a perpetual license to
reuse the specific code you authored in your own other projects (see CLA §5). You
also confirm that you have the right to contribute the code in the first place.
@@ -36,13 +36,13 @@ Signing is automatic and takes one comment:
> I have read the CLA Document and I hereby sign the CLA
4. The bot records your signature against your GitHub username. You only sign
once it covers all of your future contributions.
once, and it covers all of your future contributions.
## Development setup
Requirements: **Node.js 20 (LTS)** and **pnpm 10**. Run `nvm use` (reads
`.nvmrc`); Corepack activates the pinned pnpm from the `packageManager` field
automatically don't install pnpm globally.
automatically, so don't install pnpm globally.
```bash
pnpm install # install all workspace dependencies
@@ -53,7 +53,7 @@ pnpm dev # API server on :3005, Vite dev server on :5173
You can run the two halves separately with `pnpm dev:server` and `pnpm dev:web`.
Working on the **desktop** app additionally needs a C++ toolchain (`make`, `g++`,
`python3`) to build the native `uiohook-napi` module — on Debian/Ubuntu:
`python3`) to build the native `uiohook-napi` module. On Debian/Ubuntu:
`sudo apt install build-essential python3`. Without it `pnpm install` just warns
and skips that one rebuild; the server and web client are unaffected.
@@ -64,7 +64,7 @@ it.
## Coding standards
- **TypeScript strict mode**, no `any`. The codebase compiles cleanly under
strict settings keep it that way.
strict settings, so keep it that way.
- **Match the surrounding code.** Follow existing patterns, naming, and module
boundaries rather than introducing new ones.
- **Federation-aware.** Never assume a single global user ID. Resolve the
@@ -96,10 +96,10 @@ it.
Use GitHub Issues. For bugs, include reproduction steps, expected vs. actual
behavior, and your environment (deployment method, browser/desktop, and whether
federation or voice is involved). For security issues, please do **not** open a
public issue — see [`SECURITY.md`](SECURITY.md).
public issue. See [`SECURITY.md`](SECURITY.md).
## License
By contributing, you agree that your contributions are licensed under the
[GNU AGPL-3.0](LICENSE) and are subject to the [CLA](CLA.md) an exclusive-license
[GNU AGPL-3.0](LICENSE) and are subject to the [CLA](CLA.md), an exclusive-license
grant that also enables the project's commercial dual-license.
+149 -114
View File
@@ -4,7 +4,7 @@
# Backspace
**A self-hosted communication platform — text, voice, video, and federation — that you own.**
**A self-hosted communication platform you own. Text, voice, video, and federation.**
[![License: AGPL-3.0](https://img.shields.io/badge/license-AGPL--3.0-3da639.svg)](LICENSE)
[![TypeScript](https://img.shields.io/badge/TypeScript-strict-3178c6.svg)](https://www.typescriptlang.org/)
@@ -15,41 +15,41 @@
---
Backspace is a Discord-style chat platform you run on your own hardware. Spaces,
channels, roles, voice and video, screen sharing, direct messages, friends, file
sharing, and message search — plus **server-to-server federation**, so
independent Backspace instances can talk to each other while each stays under
its own control.
Backspace is a self-hosted, open-source Discord alternative: a Discord-style chat
platform you run on your own hardware. Spaces, channels, roles, voice and video,
screen sharing, direct messages, friends, file sharing, and message search. On top
of that, **server-to-server federation** lets independent Backspace instances talk
to each other while each stays under its own control.
It is **free and open source** under the **GNU AGPL-3.0**, and dual-licensed: a
commercial license is available if the AGPL doesn't fit your use. See
[License](#license) for the details.
> **Project status** <a name="project-status"></a>
> Backspace 1.0 — stable, self-hostable, and actively developed.
> Backspace 1.0. Stable, self-hostable, and actively developed.
## What makes Backspace different
Self-hosted chat usually forces a trade-off: gaming-grade voice and video, *or* a
polished Discord-style experience, *or* federation between independent servers
rarely all three, and rarely with the fine-grained media controls people expect.
polished Discord-style experience, *or* federation between independent servers.
Rarely all three, and rarely with the fine-grained media controls people expect.
Backspace does all three at once:
- **Voice & video with a real control surface.** Not just "it has screen share":
choose resolution, frame rate, codec (VP9 or hardware H.264), and bitrate; set
independent 0200% volume for every person *and* every screen-share; RNNoise
noise suppression; a live connection inspector (bitrate, codec, ping, packet
loss, jitter); and a per-tile badge showing each stream's measured
resolution/frame-rate. Screen sharing goes up to 4K/120fps within admin-set
- **Voice and video with a real control surface.** This goes past a screen-share
button. Choose resolution, frame rate, codec (VP9 or hardware H.264), and
bitrate; set independent 0-200% volume for every person and every screen-share;
RNNoise noise suppression; a live connection inspector (bitrate, codec, ping,
packet loss, jitter); and a per-tile badge showing each stream's measured
resolution and frame-rate. Screen sharing goes up to 4K/120fps within admin-set
bounds.
- **Federation, not a walled garden.** Run your own instance and peer it with
others: cross-instance friends, DMs, calls, and presence — each server
independently owned, requests HMAC-authenticated.
- **A complete, polished platform not a demo.** Role-based permissions with
per-category and per-channel overrides, friends and group DMs, inline playable
media, moderation with audit trails, search, a desktop app, and an installable
mobile PWA all in the warm, calm "Aether Drift" interface.
others for cross-instance friends, DMs, calls, and presence. Each server stays
independently owned, and requests are HMAC-authenticated.
- **A complete platform, not a demo.** Role-based permissions with per-category
and per-channel overrides, friends and group DMs, inline playable media,
moderation with audit trails, search, a desktop app, and an installable mobile
PWA, all in the warm, calm "Aether Drift" interface.
You own the server, the data, and the network it federates into.
@@ -59,7 +59,7 @@ You own the server, the data, and the network it federates into.
<img src="docs/screenshots/voice-video-grid.webp" alt="A voice channel with a grid of camera and screen-share tiles" width="900" />
<sub><em>A voice channel in full swing — camera tiles alongside live screen-shares, each with its own resolution / frame-rate label.</em></sub>
<sub><em>A voice channel in full swing. Camera tiles alongside live screen-shares, each with its own resolution and frame-rate label.</em></sub>
</div>
@@ -67,31 +67,31 @@ You own the server, the data, and the network it federates into.
<tr>
<td width="50%" valign="top">
<img src="docs/screenshots/chat.webp" alt="A text channel with messages and a typing indicator" /><br/>
<sub><b>Text channels</b> Markdown, replies, reactions, and live typing indicators.</sub>
<sub><b>Text channels.</b> Markdown, replies, reactions, and live typing indicators.</sub>
</td>
<td width="50%" valign="top">
<img src="docs/screenshots/screen-share-settings.webp" alt="The screen-share settings popover" /><br/>
<sub><b>Screen-share controls</b> — resolution, frame rate, codec, and bitrate, within admin-set bounds.</sub>
<sub><b>Screen-share controls.</b> Resolution, frame rate, codec, and bitrate, within admin-set bounds.</sub>
</td>
</tr>
<tr>
<td width="50%" valign="top">
<img src="docs/screenshots/space-discovery.webp" alt="The space discovery / Explore view" /><br/>
<sub><b>Spaces &amp; discovery</b> — browse public, request-to-join, and joined spaces.</sub>
<sub><b>Spaces and discovery.</b> Browse public, request-to-join, and joined spaces.</sub>
</td>
<td width="50%" valign="top">
<img src="docs/screenshots/group-dm.webp" alt="A federated group direct message" /><br/>
<sub><b>Direct messages</b> 1-on-1 and group DMs, including members on peer instances.</sub>
<sub><b>Direct messages.</b> 1-on-1 and group DMs, including members on peer instances.</sub>
</td>
</tr>
<tr>
<td width="50%" valign="top">
<img src="docs/screenshots/user-discovery.webp" alt="The find-people / user discovery view" /><br/>
<sub><b>Friends &amp; social</b> — find people across instances with mutual friends and spaces.</sub>
<sub><b>Friends and social.</b> Find people across instances with mutual friends and spaces.</sub>
</td>
<td width="50%" valign="top">
<img src="docs/screenshots/admin-federation.webp" alt="The federation admin panel showing peered instances" /><br/>
<sub><b>Federation admin</b> — manage peered instances, relay, and secret rotation.</sub>
<sub><b>Federation admin.</b> Manage peered instances, relay, and secret rotation.</sub>
</td>
</tr>
</table>
@@ -106,22 +106,22 @@ You own the server, the data, and the network it federates into.
- Message reactions, replies, editing, deletion, and per-message mark-as-unread
- Rich link embeds (YouTube, Vimeo, Spotify, and generic OpenGraph) with SSRF-protected scraping, plus GIF search (Klipy)
- Typing indicators, unread badges, and presence
- Direct messages 1-on-1 and group DMs (up to 10 people), with voice/video calls (ring / accept / reject)
- Direct messages: 1-on-1 and group DMs (up to 10 people), with voice/video calls (ring, accept, reject)
**Voice, video & screen sharing** (via [LiveKit](https://livekit.io/)):
- Screen sharing up to 4K / 120fps VP9 by default, an optional hardware-accelerated H.264 mode, and a VP8 simulcast fallback
- Per-stream quality controls resolution, frame rate, codec, and bitrate, within admin-set bounds
- Independent 0200% volume for every participant *and* every screen-share
**Voice, video, and screen sharing** (via [LiveKit](https://livekit.io/)):
- Screen sharing up to 4K/120fps: VP9 by default, an optional hardware-accelerated H.264 mode, and a VP8 simulcast fallback
- Per-stream quality controls: resolution, frame rate, codec, and bitrate, within admin-set bounds
- Independent 0-200% volume for every participant and every screen-share
- RNNoise noise suppression (on by default), plus echo-cancellation and auto-gain toggles and mic/speaker device selection
- Live connection inspector per-participant bitrate, codec, ping, packet loss, and jitter plus a per-tile badge showing each stream's measured resolution/frame-rate
- Live connection inspector for per-participant bitrate, codec, ping, packet loss, and jitter, plus a per-tile badge showing each stream's measured resolution and frame-rate
- Screen-share viewer detection ("who's watching") and auto-ducking that lowers stream audio when someone speaks
- Selective subscription mute or stop watching any camera/stream to save bandwidth
- Selective subscription: mute or stop watching any camera or stream to save bandwidth
- Push-to-talk and fully customizable keybinds (including mouse buttons), in the browser and the desktop app
- Picture-in-Picture for voice and video
### Organization
- Spaces with channel categories
- Role-based permissions bitwise RBAC with category- and channel-level overrides
- Role-based permissions: bitwise RBAC with category- and channel-level overrides
- Customizable user sidebar layout, with personal color-coded folders that group whole spaces
- Space discovery (public, request-to-join, and private)
- Shareable invite codes
@@ -132,8 +132,8 @@ You own the server, the data, and the network it federates into.
- Mutual friends and mutual spaces
- User profiles with banner, bio, and accent color
- Presence and rich activities (playing, listening, watching, streaming, custom)
- Manual status Online, Idle, or Do Not Disturb with a custom status message
- Privacy controls toggle discoverability and activity-status sharing
- Manual status (Online, Idle, or Do Not Disturb) with a custom status message
- Privacy controls: toggle discoverability and activity-status sharing
### Moderation
- Bans with reason and moderator attribution (who, why, and when)
@@ -144,7 +144,7 @@ You own the server, the data, and the network it federates into.
### Federation
- Multi-instance peering with HMAC-signed server-to-server requests
- Federated identity resolution (`username@instance`)
- Cross-instance DMs messages, reactions, and membership relay
- Cross-instance DMs: messages, reactions, and membership relay
- Cross-instance friends and presence
- File replication with size validation
- Background workers for outbox delivery, file download, peer health, and cleanup
@@ -152,27 +152,27 @@ You own the server, the data, and the network it federates into.
### Platform
- File uploads with image thumbnails (via `sharp`), drag-and-drop and paste-to-upload, and in-app avatar/banner cropping
- Message search with `from:`, `has:`, `before:`, and `after:` filters, plus jump-to-message
- Admin panel instance settings, user management, registration controls, storage management, and federation/peering, plus granular streaming controls (a per-resolution × per-frame-rate bitrate matrix, min/max caps, quality-slider step, and an optional user-set-bitrate mode)
- Admin panel: instance settings, user management, registration controls, storage management, and federation/peering, plus granular streaming controls (a per-resolution by per-frame-rate bitrate matrix, min/max caps, quality-slider step, and an optional user-set-bitrate mode)
- Automatic SQLite backups (pre-migration, scheduled, and manual) with restore tooling
- Electron desktop app (Windows, macOS, Linux) with global keybinds (push-to-talk, mute, deafen) and activity detection
- Native desktop notifications and unread badge counts
- Mobile-responsive web UI with a dedicated touch layout (bottom navigation, swipe gestures, full-screen views)
- Installable PWA add it to your phone's home screen to run it as a standalone app, with service-worker caching and an offline message queue (messages send once you reconnect)
- Account management password change and account deletion with safeguards
- Installable PWA: add it to your phone's home screen to run it as a standalone app, with service-worker caching and an offline message queue (messages send once you reconnect)
- Account management: password change and account deletion with safeguards
## Installation
The intended way to deploy Backspace is the **interactive installer** — it
The intended way to deploy Backspace is the **interactive installer**. It
configures everything (`.env`, secrets, HTTPS, optional voice) and brings the
stack up for you. It **auto-detects your environment** and picks one of three
deployment modes — the default "All-in-One" (below) needs nothing but a host and
deployment modes. The default "All-in-One" (below) needs nothing but a host and
a domain, but if ports 80/443 are already taken (an existing reverse proxy, a
tunnel, another app) the installer steers you to the right mode instead of
dead-ending. See [Deployment modes](#deployment-modes) for the full picture.
By default the installer **pulls a prebuilt multi-architecture image** from the
GitHub Container Registry (`linux/amd64` + `linux/arm64`), so weak or ARM boxes
(a Raspberry Pi) skip the heavy local build — it falls back to building from
(a Raspberry Pi) skip the heavy local build. It falls back to building from
source automatically if the image can't be pulled.
### Requirements
@@ -181,7 +181,7 @@ source automatically if the image can't be pulled.
- A **domain name** for your instance. In the default All-in-One mode it must
point at the host's public IP (Caddy obtains HTTPS certificates for it
automatically); behind your own reverse proxy or a tunnel it points at that
edge instead — see [Deployment modes](#deployment-modes).
edge instead. See [Deployment modes](#deployment-modes).
- The ability to open the firewall ports in step 2 (All-in-One), or a reverse
proxy / tunnel already terminating HTTPS for you.
@@ -203,34 +203,34 @@ The installer walks you through everything interactively:
### 2. Open the firewall ports
Open these on the host and, if it's behind a router, port-forward them to the host:
Open these on the host (and, if it's behind a router, port-forward them to the host):
| Port | Proto | When | Purpose |
|------|-------|------|---------|
| `80` | TCP | **Always** | HTTP Caddy's automatic-HTTPS (ACME) challenge + redirect to HTTPS |
| `443` | TCP | **Always** | HTTPS — web app, REST API, WebSocket, and LiveKit signaling (proxied) |
| `3478` | UDP | If voice enabled | TURN NAT traversal for WebRTC |
| `80` | TCP | **Always** | HTTP. Caddy's automatic-HTTPS (ACME) challenge + redirect to HTTPS |
| `443` | TCP | **Always** | HTTPS. Web app, REST API, WebSocket, and LiveKit signaling (proxied) |
| `3478` | UDP | If voice enabled | TURN. NAT traversal for WebRTC |
| `7881` | TCP | If voice enabled | WebRTC TCP fallback (clients that can't use UDP) |
| `5000060000` | UDP | If voice enabled | WebRTC media (voice / video / screen-share streams) |
Without voice, you only need `80` and `443`. The voice ports are required only
when you enable LiveKit. LiveKit's own signaling port (`7880`) stays internal
it's reverse-proxied through Caddy on `443`, so you do **not** forward it.
when you enable LiveKit. LiveKit's own signaling port (`7880`) stays internal.
It's reverse-proxied through Caddy on `443`, so you do **not** forward it.
> **Do this together with DNS, ideally before (or right after) running the
> installer.** Caddy gets your HTTPS certificate from Let's Encrypt the first
> time the stack starts, which requires your domain to resolve to this host
> **and** ports `80`/`443` reachable from the internet. If they aren't ready
> yet, that's fine Caddy keeps retrying, and HTTPS comes up automatically once
> yet, that's fine. Caddy keeps retrying, and HTTPS comes up automatically once
> DNS and the ports are in place.
### 3. Create your admin account
Open `https://your-domain` and register. **The first account created becomes the
instance admin** — there is no default username or password.
instance admin**. There is no default username or password.
If the page doesn't load over HTTPS, it's almost always DNS or ports `80`/`443`
not being reachable from outside — check `docker compose logs caddy` for
not being reachable from outside. Check `docker compose logs caddy` for
certificate errors. (The installer's health check confirms the app is up
internally, not that the certificate was issued.)
@@ -244,7 +244,7 @@ backup/restore and image-pinning guide.
### Manual setup (advanced, optional)
The installer above is the supported path. If you'd rather configure everything
by hand, you can skip it and drive Docker Compose directly but then DNS,
by hand, you can skip it and drive Docker Compose directly, but then DNS,
`.env`, secrets, voice config, and the same firewall ports from step 2 are your
responsibility:
@@ -265,7 +265,7 @@ The stack runs three services via Docker Compose:
|-------------|---------------------------------------------------|
| `backspace` | The app (API + WebSocket + built web client) on internal port `3000` |
| `caddy` | Reverse proxy with automatic HTTPS for your `DOMAIN` (ports `80`/`443`) |
| `livekit` | Voice/video server optional, enabled with `COMPOSE_PROFILES=voice` |
| `livekit` | Voice/video server; optional, enabled with `COMPOSE_PROFILES=voice` |
## Deployment modes
@@ -276,15 +276,15 @@ non-interactive install (`DEPLOY_MODE=proxy ./install.sh`).
| Mode | When | HTTPS handled by | Voice |
|------|------|------------------|-------|
| **`allinone`** (default) | Ports 80/443 are free and you have a domain | The bundled **Caddy** (automatic Let's Encrypt) | with UDP media ports open |
| **`proxy`** | You already run a reverse proxy (nginx, Traefik, Caddy, Nginx Proxy Manager, SWAG…) | **Your** reverse proxy | if you also proxy `/livekit` and open the media ports |
| **`tunnel`** | You expose the box through a tunnel (Cloudflare Tunnel, Tailscale…) | The **tunnel** provider | WebRTC/UDP can't traverse a tunnel |
| **`allinone`** (default) | Ports 80/443 are free and you have a domain | The bundled **Caddy** (automatic Let's Encrypt) | Yes, with UDP media ports open |
| **`proxy`** | You already run a reverse proxy (nginx, Traefik, Caddy, Nginx Proxy Manager, SWAG…) | **Your** reverse proxy | Yes, if you also proxy `/livekit` and open the media ports |
| **`tunnel`** | You expose the box through a tunnel (Cloudflare Tunnel, Tailscale…) | The **tunnel** provider | No, WebRTC/UDP can't traverse a tunnel |
In `proxy` and `tunnel` mode the bundled Caddy is **not** started; instead the app
is published on **`127.0.0.1:APP_PORT`** (loopback only never exposed directly)
is published on **`127.0.0.1:APP_PORT`** (loopback only, never exposed directly)
for your proxy or tunnel to forward to. This is driven by a small overlay,
`docker-compose.proxy.yml`, which the installer wires in for you by setting
`COMPOSE_FILE=docker-compose.yml:docker-compose.proxy.yml` in `.env` so every
`COMPOSE_FILE=docker-compose.yml:docker-compose.proxy.yml` in `.env`, so every
later `docker compose …` command in the directory keeps working with no `-f`
flags. The installer auto-picks a free `APP_PORT` (3000/8080 are often taken);
override it with `APP_PORT=…`.
@@ -292,7 +292,7 @@ override it with `APP_PORT=…`.
The installer prints ready-to-paste config for your mode at the end. The
canonical snippets are below.
### Mode 2 behind your own reverse proxy
### Mode 2: behind your own reverse proxy
The app answers plain HTTP on `127.0.0.1:APP_PORT`; your proxy terminates TLS and
forwards to it. Every snippet already includes the three things people get wrong:
@@ -302,7 +302,7 @@ scheme/IP), and a **body-size limit** matching `MAX_UPLOAD_SIZE` (default 100 MB
Replace `chat.example.com` and `8080` with your domain and `APP_PORT`.
**nginx** — the `map` goes in `http { }` once; the `server` block per site:
**nginx.** The `map` goes in `http { }` once; the `server` block per site:
```nginx
map $http_upgrade $connection_upgrade { default upgrade; '' close; }
@@ -330,7 +330,7 @@ server {
proxy_send_timeout 3600s;
}
# Voice only forward LiveKit signaling (strips the /livekit prefix):
# Voice only: forward LiveKit signaling (strips the /livekit prefix):
# location /livekit/ {
# proxy_pass http://127.0.0.1:7880/;
# proxy_http_version 1.1;
@@ -341,7 +341,7 @@ server {
}
```
**Caddy** (if you run your own it handles WebSocket and `X-Forwarded-*` itself):
**Caddy** (if you run your own; it handles WebSocket and `X-Forwarded-*` itself):
```caddy
chat.example.com {
@@ -354,7 +354,7 @@ chat.example.com {
}
```
**Traefik** (file provider Traefik handles WebSocket automatically):
**Traefik** (file provider; Traefik handles WebSocket automatically):
```yaml
http:
@@ -369,7 +369,7 @@ http:
loadBalancer:
servers:
- url: "http://127.0.0.1:8080"
# Voice only add a higher-priority router + stripPrefix middleware for
# Voice only: add a higher-priority router + stripPrefix middleware for
# PathPrefix(`/livekit`) → http://127.0.0.1:7880.
```
@@ -382,9 +382,9 @@ by hand. In **Nginx Proxy Manager**, add a **Proxy Host**:
|-------|-------|
| **Domain Names** | `chat.example.com` |
| **Scheme** | `http` |
| **Forward Hostname / IP** | `127.0.0.1` but **if NPM runs in Docker**, `127.0.0.1` is NPM's *own* container. Use the host's LAN IP, or `host.docker.internal` with `extra_hosts: ["host.docker.internal:host-gateway"]` on the NPM container. |
| **Forward Hostname / IP** | `127.0.0.1`, but **if NPM runs in Docker**, `127.0.0.1` is NPM's *own* container. Use the host's LAN IP, or `host.docker.internal` with `extra_hosts: ["host.docker.internal:host-gateway"]` on the NPM container. |
| **Forward Port** | your `APP_PORT` (e.g. `8080`) |
| **Websockets Support** | **ON** (required chat/live events break without it) |
| **Websockets Support** | **ON** (required; chat/live events break without it) |
| **Block Common Exploits** | fine to leave on |
| **SSL tab** | request a Let's Encrypt cert and enable **Force SSL** |
| **Advanced tab** | add `client_max_body_size 100m;` (match `MAX_UPLOAD_SIZE`) |
@@ -392,7 +392,7 @@ by hand. In **Nginx Proxy Manager**, add a **Proxy Host**:
The same three ideas apply to any GUI proxy: forward to the app's host+port,
enable WebSocket support, and raise the request-body limit.
### Mode 3 behind a tunnel (Cloudflare, Tailscale)
### Mode 3: behind a tunnel (Cloudflare, Tailscale)
Same loopback publish as Mode 2, but the tunnel daemon on the host reaches
`127.0.0.1:APP_PORT` and no inbound ports are opened at all. For **Cloudflare
@@ -419,33 +419,33 @@ Two tunnel-specific caveats, both handled by the installer:
the 100 MB default would let large uploads fail *at the edge*. In `tunnel` mode
the installer sets `MAX_UPLOAD_SIZE=94371840` (90 MB) with headroom. Don't raise
it back above ~100 MB behind Cloudflare.
- **No voice.** Voice/video is **WebRTC over UDP**, which a tunnel can't carry so
- **No voice.** Voice/video is **WebRTC over UDP**, which a tunnel can't carry, so
it's disabled in `tunnel` mode. If you need voice, use Mode 2 (reverse proxy)
with the media ports opened, or All-in-One.
### Voice per mode
Voice/video (LiveKit) needs its **UDP media ports** reachable from clients
these carry the actual audio/video and never pass through your HTTP proxy or
Voice/video (LiveKit) needs its **UDP media ports** reachable from clients.
These carry the actual audio/video and never pass through your HTTP proxy or
tunnel:
| Port | Proto | Purpose |
|------|-------|---------|
| `3478` | UDP | TURN WebRTC NAT traversal |
| `3478` | UDP | TURN. WebRTC NAT traversal |
| `7881` | TCP | WebRTC TCP fallback |
| `5000060000` | UDP | WebRTC media (voice / video / screen-share) |
- **All-in-One** — voice works once those ports are open/forwarded. LiveKit
- **All-in-One.** Voice works once those ports are open/forwarded. LiveKit
*signaling* is proxied through Caddy on 443 (`/livekit`); port `7880` stays
internal, never forwarded.
- **Reverse proxy** — you must **also** route `/livekit` to `127.0.0.1:7880` (see
- **Reverse proxy.** You must **also** route `/livekit` to `127.0.0.1:7880` (see
the commented lines in the snippets) **and** open the media ports above.
- **Tunnel** — voice does **not** work (UDP can't traverse the tunnel). This is a
- **Tunnel.** Voice does **not** work (UDP can't traverse the tunnel). This is a
known, unavoidable limitation, not a misconfiguration.
### Updating a running instance
Back up first — the app auto-snapshots the SQLite DB, and you can take one on
Back up first. The app auto-snapshots the SQLite DB, and you can take one on
demand with `./backup.sh` (see [`docs/systems/deployment.md`](docs/systems/deployment.md)).
Then, from the install directory:
@@ -460,12 +460,12 @@ docker compose up -d --build
```
Because `COMPOSE_FILE` lives in `.env`, these commands automatically use the
right compose files in every mode no `-f` flags to remember. A redeploy
right compose files in every mode, with no `-f` flags to remember. A redeploy
briefly restarts the `backspace` container (clients reconnect automatically).
## Development
Requirements: **Node.js 20 (LTS)** and **pnpm 10** both are pinned (`.nvmrc` +
Requirements: **Node.js 20 (LTS)** and **pnpm 10**, both pinned (`.nvmrc` plus
the `packageManager` field), so `nvm use` and Corepack select the right versions
automatically. Newer Node majors are untested; the Docker image always builds on
Node 20 regardless of your host.
@@ -478,11 +478,11 @@ pnpm dev # API server on :3005, Vite dev server on :5173
> **Server/web only?** `pnpm install` also builds the desktop app's native
> keyboard-hook module (`uiohook-napi`), which needs a C++ toolchain
> (`make`, `g++`, `python3`). If those are missing it now **warns and continues**
> the server and web client don't need it. Install a build toolchain
> (`make`, `g++`, `python3`). If those are missing it now **warns and continues**,
> and the server and web client don't need it. Install a build toolchain
> (Debian/Ubuntu: `sudo apt install build-essential python3`) only if you're
> building the **desktop** app. And to *self-host*, use the Docker installer
> above it never touches the desktop package.
> above; it never touches the desktop package.
Run the halves separately if you prefer:
@@ -506,22 +506,22 @@ The most important:
| Variable | Required | Default | Description |
|----------------------|----------|-------------|-------------|
| `DOMAIN` | yes | | Public domain name of your instance |
| `JWT_SECRET` | yes | | Auth signing secret, **min 32 chars** (`openssl rand -hex 32`) |
| `DEPLOY_MODE` | no | `allinone` | `allinone` \| `proxy` \| `tunnel` see [Deployment modes](#deployment-modes) |
| `DOMAIN` | yes | none | Public domain name of your instance |
| `JWT_SECRET` | yes | none | Auth signing secret, **min 32 chars** (`openssl rand -hex 32`) |
| `DEPLOY_MODE` | no | `allinone` | `allinone` \| `proxy` \| `tunnel`, see [Deployment modes](#deployment-modes) |
| `APP_PORT` | no | auto | `proxy`/`tunnel` only: host loopback port the app is published on |
| `PORT` | no | `3000` | App listen port (behind Caddy in Docker) |
| `HOST` | no | `0.0.0.0` | Bind address |
| `REGISTRATION_OPEN` | no | `true` | Set `false` to close signups after setup |
| `MAX_UPLOAD_SIZE` | no | `104857600` | Max upload size in bytes (100 MB; 90 MB in `tunnel` mode) |
| `BACKSPACE_IMAGE` / `BACKSPACE_IMAGE_TAG` | no | `ghcr.io/thezwiss/backspace` / `latest` | Prebuilt image to pull; pin a tag or point at your fork's registry |
| `LIVEKIT_URL` / `LIVEKIT_API_KEY` / `LIVEKIT_API_SECRET` | no | | Enable voice/video |
| `COMPOSE_PROFILES` | no | | Set to `voice` to start the bundled LiveKit service |
| `LIVEKIT_URL` / `LIVEKIT_API_KEY` / `LIVEKIT_API_SECRET` | no | none | Enable voice/video |
| `COMPOSE_PROFILES` | no | none | Set to `voice` to start the bundled LiveKit service |
## Voice & Video
Voice, video, and screen sharing require a [LiveKit](https://livekit.io/) server.
The Docker Compose file bundles one — enable it by setting these in `.env`:
The Docker Compose file bundles one. Enable it by setting these in `.env`:
```bash
COMPOSE_PROFILES=voice
@@ -531,10 +531,10 @@ LIVEKIT_API_SECRET=your-api-secret
```
Enabling voice also requires opening the WebRTC ports (`3478/UDP`, `7881/TCP`,
`5000060000/UDP`) — see [Open the firewall ports](#2-open-the-firewall-ports).
`5000060000/UDP`). See [Open the firewall ports](#2-open-the-firewall-ports).
Without LiveKit configured, everything else text, federation, DMs, uploads,
search — works fully; only voice/video channels won't connect.
Without LiveKit configured, everything else works fully (text, federation, DMs,
uploads, search); only voice/video channels won't connect.
## Federation
@@ -558,11 +558,11 @@ Grab the installer for your platform from the
| Platform | File | Notes |
|----------|------|-------|
| Windows | `Backspace-<version>.exe` | Universal installer (x64 + arm64). SmartScreen may warn on first run choose "More info" → "Run anyway". Auto-updates. |
| macOS | `Backspace-<version>-arm64.dmg` (Apple Silicon) / `Backspace-<version>-x64.dmg` (Intel) | Builds are currently **unsigned**: on first launch, right-click the app → **Open****Open**. Auto-update is not available on macOS yet check the releases page for new versions. |
| Windows | `Backspace-<version>.exe` | Universal installer (x64 + arm64). SmartScreen may warn on first run; choose "More info" → "Run anyway". Auto-updates. |
| macOS | `Backspace-<version>-arm64.dmg` (Apple Silicon) / `Backspace-<version>-x64.dmg` (Intel) | Builds are currently **unsigned**: on first launch, right-click the app → **Open****Open**. Auto-update is not available on macOS yet, so check the releases page for new versions. |
| Linux | `Backspace-<version>-x86_64.AppImage` / `-arm64.AppImage`, or `.deb` (`amd64` / `arm64`) | AppImage auto-updates; `.deb` installs update via new releases. |
On first launch the app asks for your instance URL — enter the address of the
On first launch the app asks for your instance URL. Enter the address of the
Backspace server you use (e.g. `https://chat.example.com`).
### Building from source
@@ -579,14 +579,14 @@ Cross-platform builds are produced for Windows, macOS, and Linux. See
## Mobile
Backspace works on mobile today — just open your instance in a phone browser.
Backspace works on mobile today. Just open your instance in a phone browser.
The UI has a dedicated touch layout (bottom navigation, swipe gestures, and
full-screen views), and because it ships as an installable **PWA** you can use
your browser's **Add to Home Screen** to install it as a standalone app: its own
icon, no browser chrome, and an offline message queue that flushes when you
reconnect.
Native **iOS and Android app-store apps are planned** once the project gains
Native **iOS and Android app-store apps are planned**, once the project gains
traction and the funding for the developer-program licenses is secured. Until
then, the installable PWA is the supported way to run Backspace on a phone.
@@ -596,10 +596,10 @@ Backspace is a TypeScript monorepo managed with pnpm workspaces.
```
packages/
shared/ Shared types, permission bits, constants
server/ Fastify API + WebSocket server, Drizzle/SQLite, federation
web/ React 18 SPA (Vite, Tailwind, Zustand)
desktop/ Electron wrapper
shared/ - Shared types, permission bits, constants
server/ - Fastify API + WebSocket server, Drizzle/SQLite, federation
web/ - React 18 SPA (Vite, Tailwind, Zustand)
desktop/ - Electron wrapper
```
| Layer | Technology |
@@ -614,26 +614,61 @@ packages/
| Deployment | Docker Compose + Caddy (auto-HTTPS) |
Every subsystem has a dedicated specification under
[`docs/systems/`](docs/systems/) database schema, REST API, WebSocket
[`docs/systems/`](docs/systems/): database schema, REST API, WebSocket
protocol, federation, permissions, voice, the design system, and more. **These
are the reference for how Backspace works**; start there if you want to
understand or extend a subsystem.
## FAQ
**Is Backspace a self-hosted Discord alternative?**
Yes. It gives you a Discord-style experience (spaces, channels, roles, voice,
video, screen sharing, DMs, friends) that you run entirely on your own server, so
you own the data and set the rules.
**How is it different from Revolt, Spacebar, Matrix, or Mumble?**
See the full [comparison](docs/comparison.md), including where each of those is the
better choice. In short: Backspace pairs a Discord-style client with a serious
voice and screen-share control surface and optional server-to-server federation.
**Does it have screen sharing and high-quality video?**
Yes. Screen sharing goes up to 4K/120fps within admin-set bounds, with per-stream
codec, bitrate, and resolution controls, RNNoise noise suppression, and a live
connection inspector. Voice and video use [LiveKit](https://livekit.io/) and are
optional; text, federation, DMs, and everything else run fully without them.
**Can I self-host it on a Raspberry Pi?**
Yes. The installer pulls a prebuilt multi-architecture image (amd64 and arm64), so
low-power and ARM boxes skip the heavy local build.
**Is it really open source?**
Yes, under the GNU AGPL-3.0. A separate commercial license is available for cases
the AGPL does not fit. Every released version stays available under the AGPL.
**Does it work on mobile?**
Yes, as an installable PWA with a dedicated touch layout. Native iOS and Android
apps are planned.
**What does "federation" mean here?**
Independent Backspace instances can peer with each other so users on different
servers can be friends, DM, and call across instances, while each server stays
independently owned. Requests between servers are HMAC-authenticated.
## Contributing
Contributions are welcome. Please read [`CONTRIBUTING.md`](CONTRIBUTING.md)
first. Backspace is a single-owner project, so all contributors sign a
[Contributor License Agreement](CLA.md) a one-time comment on your pull
[Contributor License Agreement](CLA.md), a one-time comment on your pull
request, handled automatically by a bot. **You keep the copyright to your
contribution** and grant the maintainer (Jannis Braun) an exclusive license to
it which is what lets Backspace be offered under both the AGPL and a commercial
it, which is what lets Backspace be offered under both the AGPL and a commercial
license. You also receive a perpetual license to reuse the specific code you
wrote in your own other projects.
## Security
If you discover a security vulnerability, please **do not** open a public issue.
Report it privately via a GitHub security advisory on this repository — see
Report it privately via a GitHub security advisory on this repository. See
[`SECURITY.md`](SECURITY.md). We'll work with you on a fix and coordinated
disclosure.
@@ -644,23 +679,23 @@ Backspace is **free and open source software**, licensed under the
In plain terms:
- ✅ Self-host, run, study, and modify it including commercially and inside a business.
- ✅ Redistribute it and your changes under the same AGPL-3.0 license.
- ⚠️ If you run a **modified** version as a network service, you must offer your
users its complete corresponding source (AGPL § 13). Backspace makes this easy
- Yes: self-host, run, study, and modify it, including commercially and inside a business.
- Yes: redistribute it and your changes under the same AGPL-3.0 license.
- Note: if you run a **modified** version as a network service, you must offer your
users its complete corresponding source (AGPL § 13). Backspace makes this easy:
set `BACKSPACE_SOURCE_URL` to your fork so the in-app "Source code" link points
at what you actually run.
- ⚠️ Preserve the copyright and license notices.
- Note: preserve the copyright and license notices.
**Commercial license.** If the AGPL doesn't fit embedding Backspace in a
**Commercial license.** If the AGPL doesn't fit (embedding Backspace in a
closed-source product, offering it as a managed service without publishing your
modifications, or an organization that can't use AGPL software a separate
modifications, or an organization that can't use AGPL software), a separate
commercial license is available on request. See
[`LICENSE-COMMERCIAL.md`](LICENSE-COMMERCIAL.md).
> **Our open-source commitment.** Every released version of Backspace is, and
> will remain, available under the AGPL-3.0. The Contributor License Agreement
> exists to enable a commercial license and optional enterprise add-ons **not**
> exists to enable a commercial license and optional enterprise add-ons, **not**
> to take the open-source edition private. If this project is ever abandoned, or
> the open-source edition is relicensed under non-free terms, the community stays
> free to fork the last AGPL release.
Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

+82
View File
@@ -0,0 +1,82 @@
# Backspace compared to other chat platforms
This page is an honest look at how Backspace fits next to the tools people usually
weigh against it: Discord, Revolt, Spacebar, Matrix/Element, and Mumble. It
includes the places where those tools are the better choice. Feature sets change,
so verify current details on each project before you decide, and open an issue if
anything here is out of date.
Short version: Backspace is for people who want a Discord-style experience they
fully self-host, with serious voice and screen-share controls, and the option to
federate independently owned servers. If you need the largest ecosystem, a mature
open federation standard, or native mobile apps today, one of the others may suit
you better.
## Feature matrix
| | Backspace | Discord | Revolt | Spacebar | Matrix / Element | Mumble |
|---|---|---|---|---|---|---|
| Self-hostable | Yes | No | Yes | Yes | Yes | Yes |
| Open source | Yes (AGPL-3.0) | No | Yes | Yes | Yes | Yes |
| Discord-style UX | Yes | Yes | Yes | Yes (Discord client) | Different model | No |
| Text chat, roles, reactions | Yes | Yes | Yes | Yes | Yes | Limited |
| Voice channels | Yes | Yes | Yes | Partial | Yes | Yes (focus) |
| Video and screen share | Yes, up to 4K/120fps within admin bounds | Yes | Limited | Partial | Yes | No |
| Per-stream media controls (codec, bitrate, resolution) | Yes | No | No | No | Partial | Some audio |
| Federation between servers | Yes, between Backspace instances | No | No | No | Yes, open standard | No |
| Native mobile apps | Installable PWA | Yes | Yes | Desktop client only | Yes | Yes |
| Runs on a Raspberry Pi | Yes (prebuilt arm64 image) | No | Yes | Yes | Yes | Yes |
"Partial" and "Limited" mean the capability exists but is less complete or less
polished than the leaders in that row at the time of writing. Check the current
state of each project.
## Backspace vs Discord
Discord is the reference experience and has the ecosystem, the bots, and the user
base. It is also proprietary, you cannot host it, and you do not own the data or
the moderation policy. Backspace exists for the people who want the Discord shape
without giving up ownership. You trade the ecosystem and the network effect for
control of the server, the data, and the rules. If you want the biggest community
and the deepest bot ecosystem, use Discord. If you want to own your instance, use
Backspace.
## Backspace vs Revolt
Revolt is the closest peer: an open-source, self-hostable, Discord-style chat with
an active community and a Rust backend. The main differences are the media stack
and federation. Backspace is built around a full voice and video control surface
(per-stream codec, bitrate, and resolution, RNNoise, a live connection inspector,
screen share up to 4K/120fps within admin limits) and supports peering independent
instances. If high-quality voice and screen sharing or cross-instance federation
are central to you, Backspace is aimed squarely at that. If you want a larger
existing community and a longer track record, look at Revolt.
## Backspace vs Spacebar
Spacebar reimplements the Discord backend so the actual Discord client can talk to
a server you host. That is a clever path to instant client familiarity, and it is
the right pick if using the real Discord app against your own backend is the goal.
The trade-off is that it inherits Discord's client and its constraints, and its
voice stack is still maturing. Backspace ships its own client and its own voice
and video stack, and it is federation-first rather than Discord-protocol-first.
## Backspace vs Matrix and Element
Matrix is the mature, standardized answer to open federation, and Element is its
best-known client. If interoperable, standards-based federation across many
different server and client implementations is your priority, Matrix is the
stronger choice and Backspace does not try to replace it. Backspace federation is
newer, simpler, and currently peers Backspace instances with each other rather
than speaking an open cross-ecosystem protocol. Where Backspace differs is the
experience: a tightly integrated Discord-style client with a purpose-built media
control surface, rather than a protocol with many clients of varying polish.
## Backspace vs Mumble
Mumble is outstanding at exactly one thing: low-latency voice for groups, self
hosted, lightweight. It has no rich text platform, no video, and no federation.
If all you need is the best self-hosted push-to-talk voice, Mumble is a great,
proven choice. Backspace is a full communication platform (text, voice, video,
files, social, federation) rather than a dedicated voice server, so pick it when
you want more than voice.