# matrix-bridge-teams
A Matrix <-> Microsoft Teams bridge written in Rust.
[中文文档](README_CN.md)
Maintainer: `Palpo Team`
Contact: `chris@acroidea.com`
## Overview
- Rust-only implementation for Matrix <-> Microsoft Teams bridging
- Matrix appservice + Microsoft Graph API integration
- HTTP endpoints for health/status/metrics and provisioning
- Database backends: PostgreSQL, SQLite, and MySQL (feature-gated)
- Dockerfile for local build and container runtime
## Repository Layout
- `src/`: bridge implementation
- `config/config.sample.yaml`: sample configuration
- `migrations/`: database migrations
- `Dockerfile`: multi-stage container build
## Prerequisites
- Rust toolchain (compatible with the project; Docker build uses Rust 1.93)
- A Matrix homeserver configured for appservices
- Microsoft Teams tenant and app registration
- Database: PostgreSQL, SQLite, or MySQL
## Quick Start (Local)
1. Create your config file:
```bash
cp config/config.sample.yaml config.yaml
```
2. Set the required values in `config.yaml`:
- `bridge.domain`
- `auth.tenant_id`, `auth.client_id`, `auth.client_secret`
- `database.url` (or `database.conn_string` / `database.filename`)
- registration values via either:
- `registration.id`, `registration.as_token`, `registration.hs_token`, or
- `teams-registration.yaml` next to your config file, or
- env vars (see Environment Overrides below)
3. Run:
```bash
cargo check -p matrix-bridge-teams
cargo test -p matrix-bridge-teams --no-run
cargo run -p matrix-bridge-teams
```
4. Verify:
```bash
curl http://127.0.0.1:9006/health
curl http://127.0.0.1:9006/status
```
## Configure Microsoft Teams (Step by Step)
1. Go to https://portal.azure.com/ and create a new App Registration:
- Navigate to **Azure Active Directory** > **App registrations** > **New registration**
- Name: "Matrix Bridge Teams"
- Supported account types: Choose based on your needs
- Redirect URI: `http://localhost:8080/callback` (for initial setup)
2. After registration, copy:
- **Application (client) ID** (for `auth.client_id`)
- **Directory (tenant) ID** (for `auth.tenant_id`)
3. Create a client secret:
- Go to **Certificates & secrets** > **New client secret**
- Copy the secret value (for `auth.client_secret`)
4. Configure API permissions:
- Go to **API permissions** > **Add a permission**
- Select **Microsoft Graph** > **Application permissions**
- Add these permissions:
- `Chat.Read.All` or `Chat.ReadWrite.All`
- `ChannelMessage.Read.All` or `ChannelMessage.ReadWrite.All`
- `Team.ReadBasic.All`
- `User.Read.All`
- Grant admin consent for your organization
5. Fill the auth section in `config.yaml`:
```yaml
auth:
tenant_id: "YOUR_TENANT_ID"
client_id: "YOUR_CLIENT_ID"
client_secret: "YOUR_CLIENT_SECRET"
```
6. To bridge a specific Teams channel, collect IDs from:
- Team ID and Channel ID from the Teams URL or API
## Configure Matrix / Palpo (Step by Step)
1. In Palpo config (`palpo.toml`), set your server name and appservice registration directory:
```toml
server_name = "example.com"
appservice_registration_dir = "appservices"
```
2. Place your bridge registration file under that directory, for example:
- `appservices/teams-registration.yaml`
3. Ensure tokens are consistent between Palpo registration and bridge config:
- `as_token` in registration == bridge appservice token
- `hs_token` in registration == bridge homeserver token
4. Ensure bridge homeserver fields point to Palpo:
```yaml
bridge:
domain: "example.com"
homeserver_url: "http://127.0.0.1:6006" # Replace with your Palpo URL
```
5. Start Palpo, then start this bridge.
6. Confirm connectivity both ways:
- Palpo must reach bridge registration `url` (for appservice transactions)
- Bridge must reach `bridge.homeserver_url` (your Palpo endpoint)
Notes:
- If Palpo and bridge run in different containers/hosts, do not use loopback addresses unless they are in the same network namespace.
- For Docker Desktop, `host.docker.internal` is often useful when bridge container needs to reach host Palpo.
## Configure Matrix / Synapse (Step by Step)
1. Set your Matrix-facing values in `config.yaml`:
```yaml
bridge:
domain: "example.com"
homeserver_url: "https://matrix.example.com"
bind_address: "0.0.0.0"
port: 9006
```
2. Create `teams-registration.yaml` next to `config.yaml` (or set `REGISTRATION_PATH`):
```yaml
id: "teams"
url: "http://127.0.0.1:9006"
as_token: "CHANGE_ME_AS_TOKEN"
hs_token: "CHANGE_ME_HS_TOKEN"
sender_localpart: "_teams_"
rate_limited: false
protocols: ["msteams"]
namespaces:
users:
- exclusive: true
regex: "@_teams_.*:example.com"
aliases:
- exclusive: true
regex: "#_teams_.*:example.com"
rooms: []
```
3. In Synapse `homeserver.yaml`, add:
```yaml
app_service_config_files:
- /path/to/teams-registration.yaml
```
4. Ensure the registration `url` is reachable by Synapse.
- Same host: `http://127.0.0.1:9006` is fine.
- Different host/container: use a routable address.
5. Restart Synapse, then start this bridge.
Notes:
- `bridge.domain` should match your Matrix server domain (right side of MXIDs).
- `bridge.homeserver_url` should be the real homeserver URL (preferably public HTTPS if Teams needs to fetch media).
- If `registration` fields are missing in `config.yaml`, values are loaded from `teams-registration.yaml`.
## Docker
Build:
```bash
docker build -t ghcr.io/palpo-im/matrix-bridge-teams:main -f Dockerfile .
```
Run (expects `/data/config.yaml` in the mounted directory):
```bash
docker run --rm \
-p 9006:9006 \
-v "$(pwd)/config:/data" \
-e CONFIG_PATH=/data/config.yaml \
ghcr.io/palpo-im/matrix-bridge-teams:main
```
Notes:
- Container listens on `0.0.0.0:9006` by default.
- Health check endpoint: `GET /health`
- Default registration file path is `teams-registration.yaml` resolved relative to `CONFIG_PATH`.
## Database Configuration
The bridge auto-detects DB type from connection string prefix:
- `postgres://` or `postgresql://` -> PostgreSQL
- `sqlite://` -> SQLite
- `mysql://` or `mariadb://` -> MySQL / MariaDB
- anything else -> PostgreSQL fallback
MySQL backend note:
- Build with the `mysql` feature enabled, e.g. `cargo run -p matrix-bridge-teams --features mysql`
- Install `libmysqlclient` (or MariaDB Connector/C) so `mysqlclient-sys` can link
Examples:
```yaml
database:
url: "postgresql://user:password@localhost:5432/matrix_bridge"
max_connections: 10
min_connections: 1
```
```yaml
database:
url: "sqlite://./data/matrix-bridge.db"
```
```yaml
database:
url: "mysql://user:password@localhost:3306/matrix_bridge"
max_connections: 10
min_connections: 1
```
## Environment Overrides
The following environment variables are supported:
- `CONFIG_PATH`
- `REGISTRATION_PATH`
- `APPSERVICE_TEAMS_AUTH_TENANT_ID`
- `APPSERVICE_TEAMS_AUTH_CLIENT_ID`
- `APPSERVICE_TEAMS_AUTH_CLIENT_SECRET`
- `APPSERVICE_TEAMS_REGISTRATION_ID`
- `APPSERVICE_TEAMS_REGISTRATION_AS_TOKEN`
- `APPSERVICE_TEAMS_REGISTRATION_HS_TOKEN`
- `APPSERVICE_TEAMS_REGISTRATION_SENDER_LOCALPART`
## Features
### Current Features
- [x] Basic Matrix AppService integration
- [ ] Microsoft Teams Graph API integration
- [ ] Message bridging (Matrix <-> Teams)
- [ ] User synchronization
- [ ] Room/Channel mapping
- [ ] File/Attachment handling
- [ ] Edit/Delete message support
- [ ] Typing notifications
- [ ] Read receipts
- [ ] Presence synchronization
### Planned Features
- [ ] Thread support
- [ ] Reaction bridging
- [ ] Rich text formatting
- [ ] Mentions (@user)
- [ ] End-to-end encryption support
- [ ] Multi-tenant support
## Development
### Building
```bash
cargo build
```
### Testing
```bash
cargo test
```
### Linting
```bash
cargo clippy
cargo fmt -- --check
```
## Contributing
Contributions are welcome! Please read our contributing guidelines before submitting PRs.
## License
Apache-2.0
## Acknowledgments
- Based on [matrix-bridge-discord](https://github.com/palpo-im/matrix-bridge-discord) architecture
- Inspired by [mautrix-teams](https://github.com/mautrix/teams)
- Uses [matrix-bot-sdk](https://github.com/matrix-org/matrix-rust-sdk)
- Integrates with [Microsoft Graph API](https://docs.microsoft.com/en-us/graph/)
## Support
- Issues: https://github.com/palpo-im/matrix-bridge-teams/issues
- Email: chris@acroidea.com