Conclave User Guide
Conclave is a self-hosted, end-to-end encrypted group messaging system built on the Messaging Layer Security (MLS) protocol. It provides private group communication with forward secrecy and post-compromise security, all within a single-server architecture that is simple to deploy and operate.
This guide covers installation, configuration, and deployment of Conclave servers and clients.
For the protocol specification — covering the wire format, API, MLS integration, and security properties — see the Conclave Protocol Specification.
Quick Start
This guide walks through setting up a Conclave server on a VPS with Caddy as a TLS reverse proxy, then connecting with a client.
Prerequisites
- A VPS with a public IP address
- A domain name (optional)
Download
Download the latest release binaries (conclave-server, conclave-cli, conclave-gui) from GitHub Releases.
Server Setup
The server runs with sensible defaults and does not require a config file.
- Install the
conclave-serverbinary to/usr/local/bin/conclave-server. - Download the systemd unit file to
/etc/systemd/system/conclave-server.service. - Create a
conclavesystem user:sudo useradd -r -s /usr/sbin/nologin conclave - Start the service:
sudo systemctl enable --now conclave-server
The server listens on 0.0.0.0:8080 (HTTP), stores data in /var/lib/conclave/conclave.db, and allows public registration. See Server Configuration for customization.
TLS with Caddy
Conclave clients require HTTPS. The simplest approach is to run Caddy as a reverse proxy — it handles TLS certificates automatically.
- Install Caddy (install docs).
- Create a Caddyfile. With a domain name:
With an IP address (uses ACME short-lived certificates):example.conclave.im { reverse_proxy 127.0.0.1:8080 }{ default_sni 203.0.113.10 } 203.0.113.10 { reverse_proxy 127.0.0.1:8080 tls { issuer acme { profile shortlived } } } - Enable and start Caddy:
sudo systemctl enable --now caddy
Connect and Chat
Launch conclave-cli (TUI) or conclave-gui (desktop). The commands below are for the TUI — the GUI provides equivalent functionality through its interface.
- Register an account (you will be prompted for a password):
/register example.conclave.im alice - Create a room:
/create general - Invite other users (after they have registered):
/invite bob,charlie - On the invited user’s client, accept the invite:
This accepts all pending invites. To accept a specific invite, use/accept/accept <invite_id>. - Send a message (type text without a
/prefix):Hello, world! - View a user’s signing key fingerprint:
/whois bob - After confirming the fingerprint out-of-band, verify their identity:
/verify bob a1b2c3d4 e5f6a7b8 c9d0e1f2 a3b4c5d6 e7f8a9b0 c1d2e3f4 a5b6c7d8 e9f0a1b2
Use /help to see all available commands.
Next Steps
/help— list all available commands.- Server Configuration — require a token to register, set message retention, etc.
- Client Configuration — themes, notifications, data directories.
Server Configuration
The server is configured via a TOML file. The server searches for configuration in the following order:
- Path specified via
--config(or-c) command-line flag. ./conclave.tomlin the current working directory./etc/conclave/config.toml.- Built-in defaults (if no config file is found).
All fields have sensible defaults and can be omitted.
Configuration Fields
Network
| Field | Type | Default | Description |
|---|---|---|---|
listen_address | string | "0.0.0.0" | IP address to bind to. |
listen_port | integer | 8443 (TLS) or 8080 (plain HTTP) | Port to listen on. Default depends on whether TLS is configured. |
Database
| Field | Type | Default | Description |
|---|---|---|---|
database_path | string | "conclave.db" | Path to the SQLite database file. Created automatically if it does not exist. |
Sessions
| Field | Type | Default | Description |
|---|---|---|---|
token_ttl_seconds | integer | 2592000 (30 days) | Session token lifetime in seconds. Token expiry is extended on every authenticated API call (sliding window). Idle sessions expire after this duration. |
Invitations
| Field | Type | Default | Description |
|---|---|---|---|
invite_ttl_seconds | integer | 2592000 (30 days) | Pending invite lifetime in seconds. Expired invites are cleaned up by the background task. |
Message Retention
| Field | Type | Default | Description |
|---|---|---|---|
message_retention | string | "-1" | Global message retention policy. "-1" disables retention (keep forever). "0" enables delete-after-fetch. Duration format (e.g., "30d") sets maximum message age. See Duration Format. |
cleanup_interval | string | "1h" | Interval between background cleanup runs. Same duration format. |
Authentication
| Field | Type | Default | Description |
|---|---|---|---|
auth_header | string | "Authorization" | HTTP header name for session authentication. When set to "Authorization" (default), clients send "Bearer {token}". When set to a custom value (e.g., "X-Conclave-Token"), clients send the raw token without the Bearer prefix. Must match the client’s auth_header. Useful when a reverse proxy uses the Authorization header for its own authentication. |
Registration Control
| Field | Type | Default | Description |
|---|---|---|---|
registration_enabled | boolean | true | Whether public registration is open. When false, registration requires a valid token. |
registration_token | string | (none) | Registration token for invite-only registration. Only checked when registration_enabled is false. Must contain only [a-zA-Z0-9_-]. |
TLS
| Field | Type | Default | Description |
|---|---|---|---|
tls_cert_path | string | (none) | Path to the TLS certificate file (PEM format). |
tls_key_path | string | (none) | Path to the TLS private key file (PEM format). |
When both tls_cert_path and tls_key_path are set, the server serves HTTPS directly. When neither is set, the server serves plain HTTP (suitable for running behind a reverse proxy). Setting only one of the two is invalid.
Example Configurations
Minimal (Plain HTTP Behind Reverse Proxy)
listen_address = "127.0.0.1"
listen_port = 8080
database_path = "/var/lib/conclave/conclave.db"
Native TLS
listen_address = "0.0.0.0"
listen_port = 8443
database_path = "/var/lib/conclave/conclave.db"
tls_cert_path = "/etc/conclave/cert.pem"
tls_key_path = "/etc/conclave/key.pem"
Invite-Only with Message Retention
listen_address = "0.0.0.0"
database_path = "/var/lib/conclave/conclave.db"
registration_enabled = false
registration_token = "my-secret-invite-code"
message_retention = "30d"
cleanup_interval = "1h"
tls_cert_path = "/etc/conclave/cert.pem"
tls_key_path = "/etc/conclave/key.pem"
Full Reference
# Network
listen_address = "0.0.0.0"
listen_port = 8443
# Database
database_path = "conclave.db"
# Sessions
token_ttl_seconds = 2592000
# Invitations
invite_ttl_seconds = 2592000
# Message retention
message_retention = "-1"
cleanup_interval = "1h"
# Authentication
# auth_header = "Authorization"
# Registration
registration_enabled = true
# registration_token = "your-secret-token"
# TLS
# tls_cert_path = "/path/to/cert.pem"
# tls_key_path = "/path/to/key.pem"
Systemd Service
A production-ready systemd unit file is provided in contrib/conclave-server.service. It runs the server as a dedicated conclave user with security hardening (sandboxed filesystem, restricted system calls, no new privileges).
To install:
sudo cp contrib/conclave-server.service /etc/systemd/system/
sudo useradd -r -s /usr/sbin/nologin conclave
sudo systemctl enable --now conclave-server
Place your config file at /etc/conclave/config.toml. The database is stored in /var/lib/conclave/.
Client Configuration
Both the TUI (conclave-cli) and GUI (conclave-gui) read configuration from:
$CONCLAVE_CONFIG_DIR/config.toml$XDG_CONFIG_HOME/conclave/config.toml(typically~/.config/conclave/config.toml)
All fields have sensible defaults and can be omitted. The client works without a config file.
Command-Line Arguments
| Flag | Description |
|---|---|
-c, --config <path> | Path to config file (overrides default search) |
-d, --data-dir <path> | Path to data directory (overrides config file and env vars) |
Running conclave-cli with no subcommand launches the interactive TUI. Running conclave-gui launches the graphical interface.
Configuration Fields
| Field | Type | Default | Description |
|---|---|---|---|
data_dir | string | $CONCLAVE_DATA_DIR or $XDG_DATA_HOME/conclave | Local data directory for SQLite databases, MLS keys, session state, and group mappings. |
accept_invalid_certs | boolean | false | Accept invalid TLS certificates (e.g., self-signed). Only enable for development or when using Caddy’s internal CA. |
show_verified_indicator | boolean | false | Show verification indicators next to verified users and fully-verified rooms. When false, only unverified [?] and changed [!] indicators are shown. |
notifications | string | "Native" | TUI-only. Notification method for new messages: "Native", "Bell", "Both", or "None". |
proxy_url | string | (unset) | Proxy URL for all HTTP traffic. Supports http://, https://, socks5://, and socks5h:// schemes. When unset, standard proxy environment variables (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY) are respected. |
ca_cert_path | string | (unset) | Path to a PEM file containing custom CA certificates to trust in addition to the system root certificates. The file may contain multiple concatenated PEM certificates. Useful for self-hosted servers with self-signed or private CA certificates. |
auth_header | string | "Authorization" | HTTP header name for session authentication. When set to a custom value (e.g., "X-Conclave-Token"), the client sends the raw token without the Bearer prefix, freeing the Authorization header for reverse proxy auth via [custom_headers]. Must match the server’s auth_header. |
Custom Headers
The [custom_headers] section allows sending arbitrary HTTP headers with every request. This is useful for authenticating with a reverse proxy in front of the Conclave server, which can help prevent active probing of the server.
[custom_headers]
Authorization = "Basic dXNlcjpwYXNz"
Headers are sent on all requests including SSE connections. Conclave’s own Authorization: Bearer token for session auth is set per-request and takes precedence over any Authorization header in [custom_headers].
Path prefix
If the reverse proxy serves Conclave under a path prefix, include the prefix in the server URL at login time. For example, if the proxy maps /myapp/ to the Conclave server:
/login https://example.com/myapp username
The client builds all API URLs from the server URL, so paths like /myapp/api/v1/... work automatically. No additional client configuration is needed.
Theme Customization
The GUI supports theme customization via the [theme] section. All fields are optional — unset fields keep the built-in defaults. Colors use #rrggbb hex format.
Preset themes are available in assets/themes/. Copy the [theme] section from a preset into your config file to use it.
Available presets: conclave, ferra, greyscale, navy.
Full Reference
# Local data directory.
# Default: $CONCLAVE_DATA_DIR, or $XDG_DATA_HOME/conclave
# (typically ~/.local/share/conclave)
#data_dir = "/home/user/.local/share/conclave"
# Accept invalid TLS certificates (e.g., self-signed). Default: false.
# Only enable this for development or testing environments.
#accept_invalid_certs = false
# Show verification indicators for verified users and fully-verified rooms.
# Default: false (hides verified indicators to reduce visual clutter).
#show_verified_indicator = false
# TUI-only: notification method for new messages.
# Possible values: "Native" (default), "Bell", "Both", "None".
#notifications = "Native"
# Proxy URL for all HTTP traffic.
# Supports http://, https://, socks5://, and socks5h:// schemes.
# When unset, standard proxy env vars (HTTP_PROXY, HTTPS_PROXY, ALL_PROXY)
# are respected.
#proxy_url = "socks5://127.0.0.1:1080"
# Path to a PEM file containing custom CA certificates to trust in addition
# to the system root certificates. May contain multiple PEM certificates.
#ca_cert_path = "/path/to/custom-ca.pem"
# HTTP header name for session authentication. Default: "Authorization".
# When set to a custom value (e.g., "X-Conclave-Token"), the client sends
# the raw token without the "Bearer " prefix, freeing "Authorization" for
# reverse proxy auth. Must match the server's auth_header setting.
#auth_header = "Authorization"
# Arbitrary HTTP headers sent with every request. Useful for authenticating
# with a reverse proxy to prevent active probing of the Conclave server.
#[custom_headers]
#Authorization = "Basic dXNlcjpwYXNz"
#X-Custom-Token = "my-secret"
# GUI theme overrides. All fields are optional; unset fields keep the
# built-in defaults. Colors use "#rrggbb" hex format.
# Theme presets are available in the assets/themes/ directory.
#[theme]
#background = "#2B292D"
#surface = "#242226"
#surface_bright = "#323034"
#title_bar = "#1E1C20"
#input_area = "#1E1C20"
#primary = "#FECDB2"
#text = "#FECDB2"
#text_secondary = "#AB8A79"
#text_muted = "#685650"
#error = "#E06B75"
#on_error = "#FFFFFF"
#warning = "#FFA07A"
#on_warning = "#2B292D"
#success = "#B1B695"
#border = "#4F474D"
#scrollbar = "#323034"
#selection = "#453D41"
Data Directory Layout
After logging in, the client stores data under data_dir:
~/.local/share/conclave/
conclave.lock # Exclusive file lock (prevents multiple instances)
session.toml # Server URL, auth token, user ID
users/<username>/
mls.db # MLS key material (SQLite)
message_history.db # Message store and TOFU fingerprints (SQLite)
Only one Conclave client instance can run at a time per data directory. Launching a second instance will fail with an error. The lock is released automatically when the process exits.
Censorship Circumvention
In censored environments, operators need to make a Conclave server indistinguishable from ordinary web traffic. This page covers deployment patterns that help resist active probing and server identification.
These techniques address the Active Probing and Origin Server Discovery threats described in the protocol specification’s threat model.
CDN Fronting
Place the server behind a CDN such as Cloudflare. From the network’s perspective, all traffic is standard HTTPS to the CDN’s domain — indistinguishable from any other site behind the same CDN.
- Point the domain’s DNS to the CDN.
- Configure the CDN to proxy traffic to the origin server.
- Firewall the origin to only accept connections from the CDN’s IP ranges. This prevents probers from bypassing the CDN and reaching the origin directly.
- Clients connect using the CDN domain as the server URL.
Tunnel Exposure
For servers behind NAT or without a public IP, tunnel services expose the server through an outbound connection. The origin has no open inbound ports and no public IP address.
Cloudflare Tunnel:
cloudflared tunnel --url http://localhost:8080
ngrok:
ngrok http 8080
Clients use the tunnel-provided URL (e.g., https://abc123.ngrok-free.app) as the server URL. The tunnel provider handles TLS termination and routing.
Reverse Proxy Authentication
Deploy a reverse proxy (Caddy, Nginx, Apache) in front of Conclave that requires authentication before forwarding requests. Unauthenticated probes receive a generic 401 or 403 response, revealing nothing about the upstream service.
Clients use the [custom_headers] config section to send the required credentials on every request, including SSE connections.
Caddy with Basic Auth
example.com {
basicauth /* {
user $2a$14$... # bcrypt hash
}
reverse_proxy localhost:8080
}
Client config (using custom auth_header so Authorization is free for the proxy):
auth_header = "X-Conclave-Token"
[custom_headers]
Authorization = "Basic dXNlcjpwYXNz"
The server must also set auth_header = "X-Conclave-Token" to match. Alternatively, if only the client authenticates with the proxy, the default auth_header can be kept — Conclave’s per-request Authorization: Bearer token takes precedence over [custom_headers].
Nginx with Basic Auth
server {
listen 443 ssl;
server_name example.com;
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}
Custom Token Header
Instead of Basic Auth, the proxy can validate a custom header:
server {
listen 443 ssl;
server_name example.com;
location / {
if ($http_x_access_token != "my-secret-token") {
return 403;
}
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
}
}
Client config:
[custom_headers]
X-Access-Token = "my-secret-token"
Secret Path Prefix
Serve Conclave under a non-obvious path prefix and proxy the default / to a benign website. Probers hitting the root see an ordinary site; only clients that know the prefix reach Conclave.
Clients include the prefix in the server URL at login — no additional client configuration is needed:
/login https://example.com/app-xyz123 username
Caddy
example.com {
handle /app-xyz123/* {
uri strip_prefix /app-xyz123
reverse_proxy localhost:8080
}
handle {
reverse_proxy https://example-blog.com {
header_up Host example-blog.com
}
}
}
Nginx
server {
listen 443 ssl;
server_name example.com;
location /app-xyz123/ {
proxy_pass http://127.0.0.1:8080/;
proxy_set_header Host $host;
}
location / {
proxy_pass https://example-blog.com;
proxy_set_header Host example-blog.com;
}
}
Combining Techniques
These techniques compose for defense in depth:
- CDN + proxy auth: CDN hides the origin IP; proxy auth blocks unauthenticated probes that reach the CDN.
- Tunnel + path prefix + decoy site: Origin has no public IP; the tunnel URL serves a decoy at
/and Conclave under a secret prefix. - CDN + path prefix + proxy auth: Maximum protection — the origin is hidden, the path is secret, and authentication is required.
Choose the combination that fits your threat model and operational constraints.