The self-hosted communication workspace you program.

Chat is the substrate. On top of it: a command palette, keybinds, macros, pipelines and client plugins. Encrypted DMs and groups with MLS. Your server, or a relay that never holds your keys.

Apache-2.0 Rust server, Tauri desktop, web client AI agent on your own Anthropic or OpenAI key, behind a confirmation gate
What RIM is

A communication workspace you program, and where it is heading

RIM is not a Slack, Discord or Matrix replacement. It is for people who want what those do not offer: a chat workspace they can shape with commands, keybinds, macros and plugins, running on a box they control. The closest relatives are VS Code, Raycast and Obsidian, applied to team communication.

today

What it is now

A Rust server and a Tauri desktop and web client. Chat with threads, attachments and search; a command registry with macros and pipelines; client plugins; key-based identity; MLS-encrypted DMs and groups; relays and hosts talking over a signed protocol.

direction

Where it is heading

A vanilla core that is a decentralized chat platform plus the programmable layer. Everything domain-specific lives in plugins, bots and pipelines, never in the server. Release readiness, a first-run path, hybrid post-quantum signatures and a mobile web app are next.

not

What it will not become

A platform that dictates how teams work, a race for integrations and user counts, or a product that competes on chat polish alone. It competes on being the most programmable, keyboard-efficient communication environment.

What makes it RIM

Six things no chat platform offers together

palette

Every action is a command

Fuzzy-searchable, typed, with sensitivity tiers. Plugins register into the same registry as the built-ins.

keybinds

Keyboard-first, modal if you want

Bind anything to any key, scoped by context. Three built-in profiles; export yours to a file and import someone else's. An opt-in Vim layer for moving around.

macros · pipelines

Compose your own workflows

Chain commands into one keystroke; react to events with pipelines. The workspace adapts to how you work.

plugins

Extend the app, not the channel

Panels, commands and context actions in a sandbox with capability grants. A signed workspace registry for your team.

mls

Encrypted where it matters

DMs and private groups use MLS with a hybrid post-quantum key agreement. Plugins read messages only through grants.

relays · hosts

Decentralized by design

Your identity is a key, not an account. A relay delivers; a host runs the workspace; the two talk over a signed protocol.

How it works

The design, in four layers

Everything a person or a plugin does is a command in one registry. Keybinds, macros, pipelines and the AI agent are all callers of that registry, so adding a plugin command gives every caller a new verb at once.

layer 1

Chat substrate

Workspaces, channels, groups and DMs, threads, attachments, reactions, search, notifications, moderation. Plain for channels; MLS-encrypted for DMs and private groups, with the server seeing only ciphertext.

layer 2

Command registry

Typed commands with sensitivity tiers and a confirmation gate for anything dangerous. The palette, keybinds, macros, pipelines and the agent all call it. The audit log records every run.

layer 3

Plugins

Client-side, sandboxed, with capability grants. Local plugins run as processes on the desktop; web plugins run in an isolated frame. A workspace can publish a signed registry of plugins for its members.

layer 4

Identity and servers

You are a key, with devices certified by it and a recovery phrase. A relay delivers to your devices; a host runs workspaces; servers prove themselves with signed descriptors and RFC 9421 request signatures.

One binary, two roles

A single server is its own relay and host. A public relay holds accounts and queues; workspaces live on hosts you or your team run.

Desktop or web clientHolds your keys and the MLS group state. Linked devices, recovery phrase.
rimp1_…
GraphQL + WS →
RelayYour account, devices and profile card. Queues what other servers send you.
rims1_…
signed S2S →
Workspace hostChannels, groups, plugins, state. Accepts members from any relay.
RIM_ROLE=host
Download

The desktop app, for the machine you work on

Linux and Windows installers, with signed checksums and signed in-app updates; on macOS you build it from source for now. Point it at your own server or at a relay. The web client needs no download: your server serves it on the same domain.

Linux

AppImage, .deb and .rpm for x86_64. Your keys stay in RIM's encrypted vault, unlocked through the desktop keyring.

with the first release

macOS

No download until signed and notarized builds exist. Build the app from source; it runs on the Mac that built it.

built from source

Windows

A setup .exe and an .msi for x86_64. The installers are unsigned, so SmartScreen asks once per version; the install guide shows the two clicks past it.

with the first release

No release exists yet. The install guide has the downloads and how to verify them, and the steps to build from source on each platform.

Self-host

Up in ten minutes with Docker Compose

PostgreSQL, the server, the web client and Caddy with TLS on one domain. Backup, restore and a restore drill are scripts in the same folder.

# one server for a team
cp deploy/.env.example deploy/.env   # POSTGRES_PASSWORD, RIM_DOMAIN
docker compose -f deploy/compose.yml up -d --build
docker compose -f deploy/compose.yml exec rim \
  /app/rim-server invite create     # first signup link
# a public relay instead
docker compose -f deploy/compose.yml \
  -f deploy/compose.relay.yml up -d --build

# switch modules off for this instance
RIM_FEATURES_DISABLED=attachments,email
Configure

Every server setting on one page

The server is configured by environment variables, all prefixed RIM_, grouped the way an operator thinks: where it listens, how people sign up, what it stores, what it talks to, what it switches off and how much it allows. Each entry: name, default, what it does, what else it needs.

GroupVariablesNotes
ListeningRIM_SERVER_HOST, RIM_SERVER_PORT, RIM_PUBLIC_BASE_URL, RIM_CORS_ALLOWED_ORIGIN, RIM_TRUST_FORWARDED_HEADERS, RIM_FORCE_SECURE_COOKIES, RIM_KEY_LOGIN_AUDIENCESA non-loopback bind needs an audience or the public URL.
Database and dataDATABASE_URL, RIM_DATABASE_MAX_CONNECTIONS, RIM_DATA_DIR, RIM_STORAGE_LOCAL_PATH, RIM_MAX_WORKSPACE_STORAGE_BYTES, RIM_SERVER_KEY_FILE, RIM_FRANKING_KEY_FILEThe data directory is the server's identity; back it up.
Signups and sessionsRIM_OPEN_REGISTRATION, RIM_REGISTRATION_POW_BITS, RIM_SIGNUP_REQUIRE_KEYS, RIM_DEFAULT_WORKSPACE, RIM_RESERVED_NAMES, RIM_PROBATION_HOURS, RIM_SESSION_TTL_DAYS, RIM_TERMS_URL, RIM_ACCOUNT_DELETION_GRACE_DAYSInvite-only by default; invites come from the CLI.
Roles and federationRIM_ROLE, RIM_HOST_ALLOWED_RELAYS, RIM_DM_INBOUND, RIM_DM_OUTBOUND, RIM_DM_PARTNERS, RIM_S2S_*, RIM_RELAY_*relay, host or both; who may start a DM; quotas per user on a public relay.
MailRIM_SMTP_HOST, RIM_SMTP_PORT, RIM_SMTP_USERNAME, RIM_SMTP_PASSWORD, RIM_SMTP_FROM, RIM_SMTP_TLS, RIM_NOTIFICATION_DIGEST_INTERVAL_SECSWithout SMTP the email feature reports "unavailable".
OperationsRIM_METRICS_ADDR, RIM_AUDIT_LOG_PATH, RIM_AUDIT_RETENTION_DAYS, RIM_FEATURES_DISABLED, RUST_LOG, RIM_ENABLE_GRAPHIQL, RIM_INSTANCE_NOTICE, RIM_LINK_PREVIEW_CONCURRENCY, RIM_PASSWORD_HASH_CONCURRENCYMetrics on their own port; features off per instance.
Limits and rate limitsRIM_API_MAX_BATCH, RIM_WS_SESSION_RECHECK_SECS, RIM_RATE_*Each rate limit is off until set.
Instance limitsRIM_LIMIT_*Off until set, except pending invitations to encrypted conversations; clients read them from serverInfo.limits.

Generated from the server's config module and the Dockerfile's variable block, so a new variable cannot ship undocumented.

Docs

Guides for the four people who run RIM

API

One GraphQL endpoint, and the HTTP around it

Everything a client does goes through POST /api; subscriptions arrive over /api/ws. The schema is published at /api/sdl with a description on every type and field, and the reference on this site is generated from it, so it cannot drift from the server.

Specifications

Build a client or a server from the published specs

Security

What we claim, and what we don't yet

Contribute

Pull requests say what they change and why

Use whatever tools you like to write the code, including AI assistants. What we read is the pull request: it has to describe what it implements and the reason for it, in your words. A diff without that is sent back unread.

How the server code is laid out, and why we keep it that way

The server is three layers with a contract between each pair. A layer talks only to the one below it, and only through a trait. That is what lets us replace one piece, a repository on a different store, a service with different rules, a transport other than GraphQL, without touching the rest, and what lets every layer be tested against a mock of the one beneath. Pull requests that cut across the layers are sent back, however good the feature.

APIGraphQL resolvers and the other transports. Parses input, checks who is calling, maps errors to codes. No business rules.
rim_api · rim_server
service traits →
ServicesThe rules: permissions, validation, orchestration. Knows nothing about HTTP or SQL; sees repositories as traits.
rim_services · rim_infra_services
repository traits →
DataRepositories over PostgreSQL. Store and fetch domain models; no rules, no transport types.
rim_core · rim_infra_db

Contracts live apart from implementations

Domain models and repository traits sit in one crate, service traits with their commands, queries and DTOs in another. Implementations live in the infra crates. A caller imports the contract, never the implementation.

One wiring point

Concrete services and repositories meet in a single composition root at boot. Everything downstream holds a trait object, so swapping an implementation is a one-line change there and nowhere else.

Errors map at each boundary

A database error becomes a repository error, then a service error, then a GraphQL code. Each layer speaks its own error type, so a change in storage never leaks into the API contract.

Types stay in their layer

No Actix, GraphQL or SeaORM types in the services or the domain. Strongly typed ids everywhere. New modules are private to their crate until something outside needs them.

Tests follow the layers

Service tests mock the repositories; API tests run the schema end to end against a real database. A behaviour is pinned once, where the logic lives, with shared fixtures rather than repeated setup.

Two exceptions, both named

The composition root may import implementations, and non-GraphQL transports such as file upload may call service traits directly. Anything else that skips a layer is a bug, not a shortcut.

Where the project is

Pre-release, honest about it

Server and clients

Version 0.0.1 on every crate and package. Five roadmap stages shipped: plugins, client-held history, key identity, MLS encryption, relays and hosts.

source available

Desktop downloads

The signed updater is wired but dormant. Linux and Windows installers arrive with the first release; macOS is built from source.

no builds yet

Cryptography review

An outside review of the identity and MLS layers is prepared, with the brief published. Results and fixes will be posted here.

pending