Files and paths
Layout
configs/
lyno.yaml # shared: listeners, media, trunks, global routing
tenants/
acme/
tenant.yaml # domains, numbers, accounts, ring groups, routing
extensions/
100.yaml # this tenant's dialplans
200.yaml
globex/
tenant.yaml
extensions/
sounds/
welcome.wav
closed.wav
moh/default.wav
lyno.yaml holds what every tenant shares. Each tenant directory is
self-contained, so a tenant can be read, copied or moved on its own.
Two keys control the discovery:
| Key | File | Default | Resolves against |
|---|---|---|---|
tenants_dir | lyno.yaml | tenants | the directory holding lyno.yaml |
extensions_dir | tenant.yaml | extensions | the tenant's own directory |
Every subdirectory of tenants_dir that holds a tenant file is loaded. A
tenant's name defaults to its directory name when omitted.
The path rule
This is the one that bites:
- Paths in
lyno.yamlresolve against the directory holdinglyno.yaml. - Paths in
tenant.yamland extension files resolve against the tenant directory.
So a shared sounds/ folder at the repository root is ../sounds/… from
configs/lyno.yaml, but ../../../sounds/… from
configs/tenants/acme/extensions/200.yaml — because it resolves relative to
configs/tenants/acme/, not to the file itself.
moh:
default: ../sounds/moh/default.wav # configs/ → sounds/
steps:
- type: play
file: ../../../sounds/welcome.wav # configs/tenants/acme/ → sounds/
Absolute paths are left untouched and are the safest option when the layout is not under your control.
Loading makes every path absolute; saving converts it back to a path relative to
the configuration directory. This matters if you write configuration files with
your own tooling: keep them relative, exactly as lyno -configure writes them.
Files are rewritten, not edited
lyno -configure regenerates the YAML from the parsed configuration. Comments
are not preserved. If you keep the configuration in version control and
maintain comments by hand, edit the files by hand too, and use the editor only
for exploration.
Files are written atomically with mode 0600, because they contain SIP
passwords.
Validation
./bin/lyno -config configs/lyno.yaml -validate
Validation is a whole-tree check that collects every problem instead of stopping at the first. It covers, among others:
- unsupported codecs or transports;
- an RTP port range where only one bound is set, or where max ≤ min;
- trunks without a name or host, duplicate trunk names, a trunk naming an
unknown tenant, and
registration.enabledwithoutauth; - outbound routes with a pattern that is not a valid regular expression, or naming an unknown trunk;
- domains claimed by more than one tenant, and DIDs claimed by more than one tenant;
- dialplan steps referencing an unknown ring group, extension or file;
- time conditions with an invalid day, hour range, date or timezone.
Saving from the editor runs the same validation first, so the editor cannot write a file the PBX would refuse to load.