strike48-connector 0.3.4

Rust SDK for the Strike48 Connector Framework
Documentation
# 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

| Variable | Description | Default |
|----------|-------------|---------|
| `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:**

| Item | Location |
|------|----------|
| 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:**

| Method | Description |
|--------|-------------|
| `.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
```