lyno.yaml reference
The main configuration file holds what every tenant shares. Paths in it resolve against the directory containing the file.
sip: {...}
media: {...}
moh: {...}
trunks: [...]
inbound_overrides: [...]
outbound_routes: [...]
default_trunk: carrier
tenants_dir: tenants
timezone: Europe/Amsterdam
allow_inter_tenant: false
Top level
| Key | Type | Default | Purpose |
|---|---|---|---|
sip | object | see below | SIP listeners and identity. |
media | object | see below | Codecs and RTP. |
moh | object | — | Global music-on-hold classes. |
trunks | list | — | Carrier trunks. |
inbound_overrides | list | — | DID redirects that beat every tenant claim. |
outbound_routes | list | — | Fallback outbound routes, tried after a tenant's. |
default_trunk | string | — | Trunk used when no route matches. The number is sent unchanged. |
tenants_dir | string | tenants | Directory of tenant subdirectories. |
timezone | string | Local | IANA timezone for time conditions, unless a tenant or condition overrides it. |
allow_inter_tenant | bool | false | Ceiling for inter-tenant calling. |
sip
sip:
bind_host: 0.0.0.0
bind_port: 5060
transports: [udp]
external_host: 203.0.113.10
user_agent: lyno-pbx
| Key | Type | Default | Notes |
|---|---|---|---|
bind_host | string | 0.0.0.0 | Address to listen on. |
bind_port | int | 5060 | Port to listen on. |
transports | list | [udp] | udp and/or tcp. Anything else is a validation error. |
external_host | string | — | Address advertised in SIP when the PBX is behind NAT. |
user_agent | string | lyno-pbx | Product token in User-Agent and Server headers. Also the fallback identity in From when nothing more specific applies. |
media
media:
codecs: [alaw, ulaw]
rtp_port_min: 10000
rtp_port_max: 20000
external_ip: 203.0.113.10
| Key | Type | Default | Notes |
|---|---|---|---|
codecs | list | [alaw, ulaw] | Only alaw and ulaw are supported. Offered identically on both legs — there is no transcoding. |
rtp_port_min | int | library default | Must be set together with rtp_port_max. |
rtp_port_max | int | library default | Must be greater than rtp_port_min. |
external_ip | string | — | Address advertised in SDP when the PBX is behind NAT. |
moh
moh:
default: ../sounds/moh/default.wav
classes:
jazz: ../sounds/moh/jazz.wav
| Key | Type | Notes |
|---|---|---|
default | path | The class named default. |
classes | map | Class name → WAV path. Tenants may add to and override this map. |
All files must be 8 kHz mono 16-bit PCM WAV.
trunks[]
trunks:
- name: carrier
tenant: acme
host: sip.provider.example
port: 5060
transport: udp
auth: {username: "...", password: "..."}
registration: {enabled: true, expiry: 5m, contact_user: "..."}
inbound: {match_ips: ["198.51.100.7"], auto_ips: true}
caller_id: {number: "+31201234567", name: Acme}
| Key | Type | Default | Notes |
|---|---|---|---|
name | string | — | Required, unique. Referenced by routes and default_trunk. |
host | string | — | Required. Hostname or IP. |
tenant | string | — | Pins every inbound call on this trunk to one tenant. Must exist. |
port | int | 5060 | |
transport | string | udp | udp or tcp. |
auth.username | string | — | |
auth.password | string | — | |
registration.enabled | bool | false | Requires auth. |
registration.expiry | duration | 5m | The re-registration period, not a retry delay. |
registration.contact_user | string | — | User part of the Contact URI. |
inbound.match_ips | list | — | Extra source addresses, added to what DNS discovers. |
inbound.auto_ips | bool | true | Resolve host (A/AAAA + SIP SRV) to discover source addresses, refreshed every 5 minutes. |
caller_id.number | string | — | Fallback when the tenant has none. |
caller_id.name | string | — |
See Trunks for the behaviour behind these keys.
inbound_overrides[]
inbound_overrides:
- did: "+31201234567"
tenant: globex
destination: {type: extension, target: "900"}
| Key | Type | Required | Notes |
|---|---|---|---|
did | string | yes | The number, or * as a global catch-all. |
tenant | string | yes | The tenant that handles the call. |
destination.type | string | no | extension or ring_group. |
destination.target | string | no | Extension number or ring group name. |
Without a destination, only the tenant is reassigned and that tenant's own
inbound routes decide the rest.
outbound_routes[]
outbound_routes:
- name: national
pattern: '^0[1-9]\d{8}$'
strip: 1
prepend: "+31"
trunk: carrier
| Key | Type | Required | Notes |
|---|---|---|---|
name | string | yes | Label used in logs. |
pattern | string | yes | Go regular expression. Must compile. Not anchored implicitly. |
strip | int | no | Digits removed from the front, applied before prepend. |
prepend | string | no | Text put in front of what remains. |
trunk | string | yes | Must name an existing trunk. |
See Outbound routing.
Durations
Duration values are Go duration strings: 20s, 1m30s, 5m, 1h. A bare
number is invalid.
Validation summary
The whole tree is checked at load, and every problem is reported:
media: unsupported codec; only one RTP bound set;rtp_port_max≤rtp_port_min.sip: unsupported transport.trunks: missingnameorhost; duplicate name; unknowntenant;registration.enabledwithoutauth.outbound_routes: pattern that does not compile; unknowntrunk.- Across tenants: a domain claimed twice; a DID claimed twice.
- Per tenant: see the tenant reference.