# Matrix Connector SDK for Rust
A Rust SDK for building connectors that integrate with the Matrix Connector Framework.
## Features
- **gRPC bidirectional streaming** - Efficient communication with Matrix server
- **Type-safe protobuf integration** - Auto-generated types from protobuf definitions
- **Async/await support** - Built on Tokio for high-performance async operations
- **Multiple payload encodings** - JSON, RAW_BYTES, JSON_LINES, and more
- **Automatic reconnection** - Handles connection failures gracefully
- **Session token support** - Fast reconnection using session tokens
- **Comprehensive error handling** - Detailed error types and messages
## Installation
### From GitHub (Internal - Recommended)
Add to your `Cargo.toml`:
```toml
[dependencies]
matrix-connector-sdk = {
git = "https://github.com/Strike48/matrix-mcf-grpc-v1",
path = "src/apps/matrix_connectors/sdks/rust"
}
```
**For specific version/tag:**
```toml
[dependencies]
matrix-connector-sdk = {
git = "https://github.com/Strike48/matrix-mcf-grpc-v1",
tag = "v0.1.0",
path = "src/apps/matrix_connectors/sdks/rust"
}
```
**For specific branch:**
```toml
[dependencies]
matrix-connector-sdk = {
git = "https://github.com/Strike48/matrix-mcf-grpc-v1",
branch = "develop",
path = "src/apps/matrix_connectors/sdks/rust"
}
```
### For Private Repositories
Configure Git authentication for Cargo:
```bash
# Option 1: Use SSH (recommended)
# Cargo uses SSH by default if available
# Option 2: Use Personal Access Token via .netrc
cat >> ~/.netrc << EOF
machine github.com
login YOUR_GITHUB_USERNAME
password YOUR_GITHUB_TOKEN
EOF
chmod 600 ~/.netrc
# Option 3: Configure git credential helper
git config --global credential.helper store
```
Then add to `Cargo.toml`:
```toml
[dependencies]
matrix-connector-sdk = {
git = "ssh://git@github.com/Strike48/matrix-mcf-grpc-v1",
path = "src/apps/matrix_connectors/sdks/rust"
}
```
### From Crates.io (Coming Soon)
```toml
[dependencies]
matrix-connector-sdk = "0.1.0"
```
### From Local Path (Development)
```toml
[dependencies]
matrix-connector-sdk = { path = "path/to/matrix-connector-sdk" }
```
### From Source (Development)
```bash
cd src/apps/matrix_connectors/sdks/rust
# Build (proto stubs auto-generated via build.rs)
cargo build
```
**Prerequisites:**
```bash
# Install protoc (macOS)
brew install protobuf
# Install protoc (Linux)
apt install protobuf-compiler
```
The Rust SDK uses `tonic-build` in `build.rs` to auto-generate protobuf stubs during compilation. No manual proto generation is needed.
## Quick Start
### Basic Echo Connector
```rust
use strike48_connector_sdk::*;
use std::sync::Arc;
struct EchoConnector;
impl BaseConnector for EchoConnector {
fn connector_type(&self) -> &str {
"echo"
}
fn version(&self) -> &str {
"1.0.0"
}
fn execute(
&self,
request: serde_json::Value,
_capability_id: Option<&str>,
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<serde_json::Value>> + Send>> {
Box::pin(async move {
Ok(serde_json::json!({
"success": true,
"echo": request,
"timestamp": chrono::Utc::now().to_rfc3339()
}))
})
}
}
#[tokio::main]
async fn main() -> Result<()> {
init_logger();
let config = ConnectorConfig {
matrix_host: "localhost:50061".to_string(),
tenant_id: "default".to_string(),
connector_type: "echo".to_string(),
instance_id: format!("echo-{}", chrono::Utc::now().timestamp_millis()),
version: "1.0.0".to_string(),
auth_token: String::new(),
use_tls: false,
..ConnectorConfig::default()
};
let connector = Arc::new(EchoConnector);
let runner = ConnectorRunner::new(config, connector);
runner.run().await?;
Ok(())
}
```
## Configuration
The SDK supports configuration via environment variables or programmatically:
### Environment Variables
| `MATRIX_HOST` or `MATRIX_URL` | Matrix server URL | `localhost:50061` |
| `TENANT_ID` | Tenant identifier | **required** |
| `INSTANCE_ID` | Connector instance ID | Auto-generated (see below) |
| `AUTH_TOKEN` | JWT token (optional) | Empty (uses approval flow) |
| `USE_TLS` | Enable TLS | `false` |
| `CONNECTOR_DISPLAY_NAME` | Human-readable name for UI | `instance_id` |
| `CONNECTOR_TAGS` | Comma-separated tags | Empty |
### Instance ID
The `INSTANCE_ID` uniquely identifies your connector instance. This is critical for:
- **Credential persistence** - Saved credentials are keyed by instance ID
- **Session management** - Matrix server tracks sessions by instance
- **Reconnection** - Same instance ID allows seamless reconnection
If not provided, the SDK generates a random instance ID:
```
{connector_type}-{timestamp_millis}
```
**Best Practice**: Always set `INSTANCE_ID` for production connectors to enable auto-reconnection after restarts.
```bash
# Recommended: Stable instance ID
export INSTANCE_ID=echo-prod
# Auto-generated: Random on each start (not recommended for production)
# (omit INSTANCE_ID)
```
### Authentication & Private Key Storage
The SDK uses asymmetric authentication (`private_key_jwt`). On first connection:
1. Admin approves the connector in Matrix Studio
2. SDK generates an RSA key pair automatically
3. Private key is saved for future reconnections
**Default Storage Locations:**
| Private Keys | `~/.matrix/keys/{connector_type}_{instance_id}.pem` |
| Credentials | `~/.matrix/credentials/matrix_{tenant}_{type}_{instance}.json` |
**Override Storage Paths:**
```bash
# Override keys directory
export MATRIX_KEYS_DIR=/custom/path/to/keys
# Direct private key path (for cert-manager/K8s)
export MATRIX_PRIVATE_KEY_PATH=/var/run/secrets/matrix/tls.key
export MATRIX_CLIENT_ID=my-connector-client
export MATRIX_KEYCLOAK_URL=https://keycloak.example.com/realms/matrix
```
### Reconnection Behavior
When the connector restarts with the same `INSTANCE_ID`:
1. SDK loads saved credentials from `~/.matrix/credentials/`
2. Fetches a fresh JWT using the saved private key
3. Reconnects to Matrix without requiring re-approval
**This only works if:**
- `INSTANCE_ID` is the same as the previous run
- Credentials file exists at the expected path
- Private key file exists at the expected path
### Programmatic Configuration
```rust
let config = ConnectorConfig {
matrix_host: "matrix.example.com:50061".to_string(),
tenant_id: "my-tenant".to_string(),
connector_type: "my-connector".to_string(),
instance_id: "my-instance".to_string(),
version: "1.0.0".to_string(),
auth_token: "jwt-token".to_string(),
use_tls: true,
..ConnectorConfig::default()
};
```
### Instance Metadata (Multi-Instance Routing)
Instance metadata enables multi-instance routing and better UI display:
```rust
let config = ConnectorConfig::from_env()
.display_name("Production Server 1") // Human-readable name for UI
.tag("prod") // Tags for grouping
.tag("us-east-1")
.with_metadata("location", "AWS US-East-1") // Key-value metadata
.with_metadata("owner", "platform-team");
```
**Available Builder Methods:**
| `.display_name("Name")` | Human-readable name for UI (defaults to instance_id) |
| `.tag("prod")` | Add a single tag for grouping |
| `.tags(["prod", "us-east-1"])` | Add multiple tags at once |
| `.with_metadata("key", "value")` | Add operator metadata |
| `.metadata_from_env("PREFIX_")` | Load metadata from env vars with prefix |
**Example with Environment Metadata:**
```rust
// Reads CONNECTOR_LOCATION, CONNECTOR_OWNER, etc.
let config = ConnectorConfig::from_env()
.display_name("Analytics Node")
.tags(["prod", "analytics"])
.metadata_from_env("CONNECTOR_");
```
**Agent Routing Behavior:**
- "run command" → round-robin across all instances
- "run command on prod-1" → route to specific instance_id
- "run command on Production Server 1" → route via display_name
- "run command on all prod servers" → broadcast to instances tagged "prod"
## Examples
See the `examples/` directory for complete examples:
- `echo_connector.rs` - Simple echo connector that returns the input
## Running Examples
```bash
# Set environment variables
export MATRIX_HOST=localhost:50061
export TENANT_ID=default
# Run echo connector
cargo run --example echo_connector
```
## Building
```bash
# Build the SDK
cargo build
# Build with examples
cargo build --examples
# Run tests
cargo test
# Run integration tests (requires running Matrix server)
cargo test -- --ignored
```
## Architecture
The SDK consists of several key components:
- **`BaseConnector`** - Trait that connectors must implement
- **`ConnectorRunner`** - Manages connector lifecycle and message handling
- **`ConnectorHandle`** - Safe handle for sending messages from callbacks (WebSocket frames, invoke)
- **`types`** - Type definitions matching protobuf schemas
- **`utils`** - Serialization, deserialization, and helper functions
- **`process`** - Async process execution (non-blocking `tokio::process::Command` wrappers)
- **`error`** - Comprehensive error types
## Payload Encodings
The SDK supports multiple payload encodings:
- `JSON` - Standard JSON encoding (default)
- `RAW_BYTES` - Raw byte arrays
- `JSON_LINES` - Newline-separated JSON
- `ARROW_IPC` - Apache Arrow IPC format
- `PROTOBUF` - Protocol Buffers
- `MSGPACK` - MessagePack
- `PARQUET` - Apache Parquet
## Error Handling
All operations return `Result<T, ConnectorError>`:
```rust
match runner.run().await {
Ok(_) => println!("Connector stopped gracefully"),
Err(e) => eprintln!("Error: {} (code: {})", e, e.code()),
}
```
## Logging
The SDK uses the `tracing` crate for logging. Initialize with:
```rust
init_logger();
```
Control log level via `RUST_LOG` environment variable:
```bash
RUST_LOG=debug cargo run --example echo_connector
```