Messaging Bridge (Experimental)
What it does
Section titled “What it does”msgbridge is a standalone binary that connects to the Aether gateway as a Bridge-type principal (br::{impl}::{spec}). It acts as a bidirectional relay: outbound messages from Aether agents are delivered to Discord channels, Microsoft Teams conversations, or email addresses; inbound messages from those platforms are wrapped in a typed JSON envelope and forwarded into Aether as agent or user messages.
The binary maintains a small database (PostgreSQL or SQLite) of channel mappings — named rules that bind a platform channel to an Aether target — and user mappings that correlate platform user IDs to Aether user IDs. All message routing decisions go through these mappings; there is no open-relay behaviour.
When to use it
Section titled “When to use it”- Notifications — an agent publishes a status update and msgbridge delivers it to a Slack-like Teams channel or Discord server.
- Human-in-the-loop — a Discord or Teams user replies to an agent prompt; msgbridge routes the reply back to the originating agent.
- Lightweight chat front-ends — surface a single Aether agent or workflow behind a familiar chat interface without building a custom bot.
- Email integration — send structured alerts or reports via SMTP from within an agent workflow.
Architecture
Section titled “Architecture”External platform msgbridge Aether gateway───────────────── ───────────────────── ──────────────────Discord / Teams ──► inbound adapter BridgeClientEmail (IMAP TBD) ──► Router.HandleInbound ──► br::impl::spec │ Agents / Users │Discord / Teams ◄── Router.HandleOutbound ◄── BridgePayload JSONEmail (SMTP) ◄── outbound adaptermsgbridge identifies itself to the gateway with a Bridge principal identity: br::{implementation}::{specifier}. The implementation field is a stable logical name (e.g. aether-msgbridge); the specifier distinguishes individual instances. Together they form the topic the gateway routes bridge traffic to.
Agents send outbound bridge requests by publishing a BridgePayload JSON object (see Outbound payload). Inbound messages from platform users arrive as an InboundPayload envelope on the agent’s subscribed topic. Channel mappings control which Aether topics receive inbound traffic and which direction (inbound, outbound, or bidirectional) a mapping permits.
Quick start
Section titled “Quick start”# Buildcd server && go build -o msgbridge ./cmd/msgbridge
# Run with a config file./msgbridge --config configs/msgbridge.yaml
# AetherLite mode (SQLite, no PostgreSQL required)./msgbridge --config configs/msgbridge.yaml --lite --sqlite-path ./msgbridge.db
# Dev mode (no config file required, uses built-in defaults)./msgbridge --devExample config
Section titled “Example config”mode: postgres # or "sqlite"
aether: address: "localhost:50051" implementation: "aether-msgbridge" specifier: "instance-1" credentials: api_key: "" # set via AETHER_API_KEY env var tls: cert_file: "" key_file: "" ca_file: ""
postgres: host: "localhost" port: 5432 database: "aether" user: "aether" password: "" # set via POSTGRES_PASSWORD env var ssl_mode: "disable" max_connections: 10 max_idle_connections: 5
sqlite: path: "msgbridge.db" # used when mode: sqlite
platforms: discord: enabled: false bot_token: "" # set via DISCORD_BOT_TOKEN env var application_id: "" teams: enabled: false app_id: "" app_password: "" # set via TEAMS_APP_PASSWORD env var tenant_id: "" webhook_port: 8081 email: enabled: false smtp: host: "" port: 587 username: "" password: "" # set via SMTP_PASSWORD env var from_address: "" tls: true
admin: enabled: true port: 31882 api_key: "" # set via MSGBRIDGE_ADMIN_API_KEY env var
logging: level: "info" # debug | info | warn | error format: "" # "console" for human output, "" for JSONConfiguration
Section titled “Configuration”YAML keys
Section titled “YAML keys”| Key | Type | Default | Description |
|---|---|---|---|
mode | string | postgres | Storage backend: postgres or sqlite. |
aether.address | host:port | — | Gateway gRPC address. Required. |
aether.implementation | string | — | Bridge implementation name. Required. |
aether.specifier | string | — | Unique instance specifier. Required. |
aether.credentials.api_key | string | (none) | API key for gateway authentication. |
aether.tls.cert_file | path | (none) | Client TLS certificate. |
aether.tls.key_file | path | (none) | Client TLS private key. |
aether.tls.ca_file | path | (none) | CA certificate for gateway verification. |
postgres.host | string | — | PostgreSQL host. Required unless mode: sqlite. |
postgres.port | int | — | PostgreSQL port. |
postgres.database | string | — | Database name. |
postgres.user | string | — | Database user. |
postgres.password | string | — | Database password. |
postgres.ssl_mode | string | disable | PostgreSQL SSL mode. |
postgres.max_connections | int | (driver default) | Max open DB connections. |
postgres.max_idle_connections | int | (driver default) | Max idle DB connections. |
sqlite.path | path | msgbridge.db | SQLite file path. Required when mode: sqlite. |
platforms.discord.enabled | bool | false | Enable Discord adapter. |
platforms.discord.bot_token | string | — | Discord bot token. |
platforms.discord.application_id | string | — | Discord application ID. |
platforms.teams.enabled | bool | false | Enable Microsoft Teams adapter. |
platforms.teams.app_id | string | — | Teams bot app ID. |
platforms.teams.app_password | string | — | Teams bot app password. |
platforms.teams.tenant_id | string | — | Azure tenant ID. |
platforms.teams.webhook_port | int | 8081 | Local HTTP port for Teams Activity webhook. |
platforms.email.enabled | bool | false | Enable email adapter. |
platforms.email.smtp.host | string | — | SMTP server host. |
platforms.email.smtp.port | int | — | SMTP server port. |
platforms.email.smtp.username | string | — | SMTP username. |
platforms.email.smtp.password | string | — | SMTP password. |
platforms.email.smtp.from_address | string | — | Envelope From address. |
platforms.email.smtp.tls | bool | false | Use TLS for SMTP. |
admin.enabled | bool | true | Enable admin REST API. |
admin.port | int | 31882 | Admin API listen port. |
admin.api_key | string | (none) | API key for admin endpoints (open if unset). |
logging.level | string | info | Log level: debug, info, warn, error. |
logging.format | string | (auto) | console for human output; empty for JSON. |
Environment variable overrides
Section titled “Environment variable overrides”All secrets should be supplied via environment variables rather than written into the config file.
| Variable | Overrides | Description |
|---|---|---|
AETHER_ADDRESS | aether.address | Gateway address. |
AETHER_IMPLEMENTATION | aether.implementation | Bridge implementation name. |
AETHER_SPECIFIER | aether.specifier | Bridge specifier. |
AETHER_API_KEY | aether.credentials.api_key | Gateway API key. |
POSTGRES_HOST | postgres.host | PostgreSQL host. |
POSTGRES_PORT | postgres.port | PostgreSQL port. |
POSTGRES_USER | postgres.user | PostgreSQL user. |
POSTGRES_PASSWORD | postgres.password | PostgreSQL password. |
POSTGRES_DATABASE | postgres.database | PostgreSQL database name. |
DISCORD_BOT_TOKEN | platforms.discord.bot_token | Discord bot token. |
DISCORD_APPLICATION_ID | platforms.discord.application_id | Discord application ID. |
TEAMS_APP_ID | platforms.teams.app_id | Teams bot app ID. |
TEAMS_APP_PASSWORD | platforms.teams.app_password | Teams bot app password. |
TEAMS_TENANT_ID | platforms.teams.tenant_id | Azure tenant ID. |
TEAMS_WEBHOOK_PORT | platforms.teams.webhook_port | Teams webhook listen port. |
SMTP_HOST | platforms.email.smtp.host | SMTP host. |
SMTP_PORT | platforms.email.smtp.port | SMTP port. |
SMTP_USERNAME | platforms.email.smtp.username | SMTP username. |
SMTP_PASSWORD | platforms.email.smtp.password | SMTP password. |
SMTP_FROM_ADDRESS | platforms.email.smtp.from_address | SMTP from address. |
AETHER_LOG_LEVEL | logging.level | Log level override. |
MSGBRIDGE_ADMIN_ENABLED | admin.enabled | Toggle admin REST API. |
MSGBRIDGE_ADMIN_PORT | admin.port | Admin API port. |
MSGBRIDGE_ADMIN_API_KEY | admin.api_key | Admin API key. |
For the full environment variable reference see Environment Variables.
Supported platforms
Section titled “Supported platforms”The following platform adapters are present in the codebase and wired into the server:
| Platform | Inbound | Outbound | Notes |
|---|---|---|---|
| Discord | Yes | Yes | Bot token required; listens for messages in mapped channels. |
| Microsoft Teams | Yes | Yes | Bot Framework Activity webhook; requires a public HTTPS endpoint for Teams to POST to. |
| Email (SMTP) | No | Yes | Outbound-only for now. IMAP inbound is explicitly noted in the code as a future addition. |
No Slack adapter exists in the codebase. Do not assume Slack support.
Outbound payload
Section titled “Outbound payload”Agents send bridge messages by publishing a JSON object to the msgbridge topic (br::{impl}::{spec}):
{ "bridge_action": "send", "target": { "mapping": "my-channel-alias" }, "content": "Hello from Aether", "subject": "Optional email subject", "html_content": "<p>Optional HTML body for email</p>", "embeds": [], "metadata": {}}The target can specify a named channel mapping alias (mapping) or a direct platform/channel pair:
"target": { "platform": "discord", "channel_id": "1234567890", "thread_id": ""}ACL considerations
Section titled “ACL considerations”Bridge principals sit at a workspace boundary. A single msgbridge instance may relay messages into multiple Aether workspaces, so ACL rules must be configured carefully:
- The bridge identity
br::{impl}::{spec}must be grantedsendpermission for each target agent, user, or workspace topic it needs to reach. - Inbound messages from untrusted platform users should be routed to a dedicated workspace or agent that performs its own validation before acting.
- Wildcard bridge ACL rules (e.g.
br::*) are rejected by the gateway; at minimum bothimpland a specific or wildcardspecsegment must be present (br::my-bridge::*). - Per-mapping
directioncontrols (inbound,outbound, or both) limit blast radius if a platform credential is compromised.
Consult your gateway ACL configuration for rule syntax.
Operational notes
Section titled “Operational notes”Credential storage
Section titled “Credential storage”All platform credentials (bot tokens, app passwords, SMTP passwords) should be provided via environment variables, not written into the YAML config. In container deployments use a secrets manager (Vault, AWS Secrets Manager, Kubernetes Secrets) to inject them at runtime.
Reconnection behaviour
Section titled “Reconnection behaviour”msgbridge maintains an exponential-backoff reconnect loop against the gateway (initial 1 s, max 30 s). Platform adapter connections follow their own retry logic inside each adapter. A FORCE_DISCONNECT from the gateway (e.g. from MaxConnectionAge rotation) is treated as a clean disconnect and triggers an immediate reconnect attempt.
Rate limiting
Section titled “Rate limiting”No built-in rate limiting is implemented at the msgbridge layer. Discord and Teams each impose their own API rate limits. If your workload produces high message volumes, add a queuing layer in front of msgbridge or throttle at the agent side before publishing to the bridge topic.
Observability
Section titled “Observability”The admin server exposes Prometheus metrics at GET /metrics (port 31882 by default, no auth). Key metrics:
| Metric | Description |
|---|---|
msgbridge_messages_routed_total | Counter of routed messages, labelled by direction, platform, and status. |
msgbridge_message_routing_duration_seconds | Histogram of routing latency per direction and platform. |
msgbridge_platform_healthy | Gauge (0/1) per platform adapter health check. |
msgbridge_active_mappings | Gauge of enabled channel mappings. |
A GET /health endpoint (no auth) returns per-platform adapter health status as JSON.
Admin REST API
Section titled “Admin REST API”The admin server (port 31882, prefix /api/v1) exposes CRUD endpoints for channel mappings, user mappings, and message logs. Protect it with admin.api_key / MSGBRIDGE_ADMIN_API_KEY in any non-development deployment.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Check |
|---|---|
| Bridge not connecting to gateway | Verify aether.address, API key, and TLS settings. Check gateway logs for rejected connection. |
| Messages dropped with “no channel mapping found” | Create a channel mapping via the admin API for the target platform channel. |
| Teams inbound not arriving | Ensure the Teams webhook port is reachable from the internet (or Teams service IPs) and that the bot is registered in Azure Bot Service. |
| Email inbound not working | IMAP inbound is not yet implemented; only SMTP outbound is supported. |
| High error rate in Prometheus | Check msgbridge_messages_routed_total{status="failed"} and correlate with structured logs on stderr. |