> ## Documentation Index
> Fetch the complete documentation index at: https://docs.routr.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Message Dispatcher

> Reference for the Message Dispatcher: its image, port, configuration file, environment variables, and gRPC contract.

The Message Dispatcher receives messages from the EdgePort, runs the configured middlewares, and forwards each message to the first Processor whose `matchFunc` returns `true`. It is stateless.

## Docker image

`fonoster/routr-dispatcher`

The image reads its configuration from `/etc/routr/dispatcher.yaml` (`CONFIG_PATH`).

## Ports

| Port | Protocol | Purpose |
| - | - | - |
| `51901` (by convention, set by `spec.bindAddr`) | gRPC | `Processor` service and the standard gRPC health service |

## Configuration file

The Dispatcher reads a YAML or JSON file and validates it against a JSON schema at startup.

| Field | Type | Required | Default | Description |
| - | - | - | - | - |
| `kind` | string | Yes | | Resource type. Use `MessageDispatcher` |
| `apiVersion` | string | Yes | | Resource version, for example `v2beta1` |
| `ref` | string | Yes | | Dispatcher reference |
| `spec.bindAddr` | string | No | | Address to listen on, for example `0.0.0.0:51901`. There is no built-in default, so set it |
| `spec.processors` | object\[] | Yes | | Processors, evaluated in order |
| `spec.processors[*].ref` | string | Yes | | Processor reference |
| `spec.processors[*].addr` | string | Yes | | Processor address, for example `connect:51904` |
| `spec.processors[*].matchFunc` | string | Yes | | JavaScript function that takes the request and returns a boolean, for example `req => true` |
| `spec.processors[*].methods` | string\[] | Yes | | SIP methods the Processor handles |
| `spec.middlewares` | object\[] | No | `[]` | Middlewares |
| `spec.middlewares[*].ref` | string | Yes | | Middleware reference |
| `spec.middlewares[*].addr` | string | Yes | | Middleware address, for example `rtprelay:51903` |
| `spec.middlewares[*].postProcessor` | boolean | Yes | | `true` runs the middleware after the Processor, `false` runs it before |

`matchFunc` receives the JSON form of the `MessageRequest` protobuf ([sipmessage.proto](https://github.com/fonoster/routr/blob/main/mods/common/src/protos/sipmessage.proto)). These are typical match functions:

```javascript theme={null}
req => true
req => req.method === "MESSAGE"
req => req.message.from.address.uri.user === "alice"
req => req.message.extensions.find(e => e.name === "User-Agent" && e.value.includes("Zoiper"))
```

## Environment variables

| Name | Default | Description |
| - | - | - |
| `CONFIG_PATH` | `/etc/routr/dispatcher.yaml` (image) | Path to the configuration file. Required |
| `LOGS_LEVEL` | | Log level, for example `verbose` or `info` |
| `OTEL_EXPORTER_OTLP_ENDPOINT` | | OTLP/HTTP endpoint. Setting it turns on tracing |
| `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` | | OTLP/HTTP traces endpoint. Takes precedence over `OTEL_EXPORTER_OTLP_ENDPOINT` |
| `OTEL_SERVICE_NAME` | `dispatcher` | Service name reported in traces |
| `OTEL_SDK_DISABLED` | | `true` turns tracing off |

## gRPC contract

The Dispatcher serves the same contract as a Processor, so the EdgePort can talk to either. It is also a client of that contract toward each Processor and middleware.

| Service | Method | Request | Response |
| - | - | - | - |
| `fonoster.routr.processor.v2beta1.Processor` | `ProcessMessage` | `MessageRequest` | `MessageResponse` |

See [processor.proto](https://github.com/fonoster/routr/blob/main/mods/common/src/protos/processor.proto).

## Minimal example

This configuration sends every message to the Connect processor.

```yaml theme={null}
kind: MessageDispatcher
apiVersion: v2beta1
ref: message-dispatcher
spec:
  bindAddr: 0.0.0.0:51901
  processors:
    - ref: connect-processor
      addr: connect:51904
      matchFunc: req => true
      methods:
        - REGISTER
        - MESSAGE
        - INVITE
        - ACK
        - BYE
        - CANCEL
```

This command runs the Dispatcher with that file.

```bash theme={null}
docker run -it \
  -v $(pwd)/dispatcher.yaml:/etc/routr/dispatcher.yaml \
  -p 51901:51901 \
  fonoster/routr-dispatcher
```

## Notes

* You need a Dispatcher only with more than one Processor or at least one middleware. Otherwise, point the EdgePort at the Processor directly.
* Order matters. The first Processor whose `matchFunc` returns `true` gets the message, so put catch-all functions last.
* Set `postProcessor` on every middleware. A middleware without it runs in neither phase.
* The schema does not enforce the fields inside `spec.processors[*]`, so a Processor entry without `matchFunc` or `methods` is not rejected at startup. Check each entry yourself.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.