Tenants
A Tenant document describes one customer. The minimum is a domain; everything
else has a global fallback or a default.
configVersion: lyno/v1
kind: Tenant
metadata:
name: acme # the identifier; it namespaces everything below
spec:
description: Acme Industries
domains:
- acme.pbx.example.com
- voice.acme.example
dids:
numbers: ["+31201234567", "+31201234568"]
patterns: ['^\+312012345\d{2}$']
callerId:
number: "+31201234567"
name: Acme
timezone: Europe/Amsterdam
moh:
classes:
jazz: ../sounds/moh/jazz.wav
interTenant: [globex]
Domains
domains: [acme.pbx.example.com]
The SIP domains this tenant's phones register with. They select the tenant for any endpoint request and become the digest realm for that tenant's authentication.
- Domains must be unique across tenants; validation refuses a duplicate.
- A tenant may have several domains, which is useful during a migration.
- Phones must use the domain as their SIP domain and the PBX address as their outbound proxy.
- At least one is required. A tenant with none is a validation error, because nothing could ever be attributed to it.
DIDs
dids:
numbers: ["+31201234567"]
patterns: ['^\+312012345\d{2}$']
The numbers this tenant owns on the shared trunks. numbers are exact claims,
patterns are Go regular expressions matched against the called number.
Exact claims are checked before patterns, and a number claimed by two tenants is
a validation error. An InboundOverride document beats both — see
Inbound routing.
^ and $ are not implied. '^\+312012345\d{2}$' claims a block of one
hundred numbers; '\+312012345' would match any number containing that string.
Two patterns that overlap cannot be decided statically, so validation lets them through and the runtime resolves them by tenant name order. Exact-against-pattern clashes are caught.
Caller ID
callerId:
number: "+31201234567"
name: Acme
Presented on outgoing calls, overriding the trunk's own caller ID. That is what lets several tenants share one carrier account and still each present their own number.
The resolution order is: the tenant's callerId, then the trunk's
callerId, then nothing.
Timezone
timezone: Europe/Amsterdam
Used by this tenant's time conditions,
overriding the global timezone. An individual condition can override it again
with its own timezone: key. Names come from the IANA database.
Music on hold
moh:
default: ../sounds/moh/acme.wav
classes:
jazz: ../sounds/moh/jazz.wav
The tenant's classes are layered on top of the global ones, key by key: a class
name defined in both resolves to the tenant's file. A dial, moh or queue
step then refers to a class by name.
Paths resolve against the configuration root, like every other path.
golden-hold: ../sounds/moh/golden-hold.wav reads better in a dialplan than
class2: does, and a step naming it then says what the caller will hear.
What lives inside a tenant
Accounts, dialplans, ring groups, queues, menus, mailboxes and routes are
documents of their own, tied to this tenant by metadata.tenant. They are not
keys inside the tenant:
---
configVersion: lyno/v1
kind: Queue
metadata:
name: support # the name a dialplan refers to
tenant: acme # the namespace it means something in
spec:
strategy: fewestrecent
agents: ["100", "101"]
---
configVersion: lyno/v1
kind: Mailbox
metadata:
name: "100" # the account extension whose messages it holds
tenant: acme
spec:
displayName: Alice
pin: "4821"
email: alice@example.com
| Kind | metadata.name is | Page |
|---|---|---|
Account | the SIP username | Accounts and extensions |
Extension | the number the plan answers on | Dialplans |
RingGroup | a name you choose | Ring groups |
Queue | a name you choose | Call queues |
IVRMenu | a name you choose | IVR menus |
Mailbox | the account extension whose messages it holds | Voicemail |
InboundRoute | a label for the route | Inbound routing |
OutboundRoute | a label for the route | Outbound routing |
A queue, a menu or a mailbox is a document rather than a field on the tenant for the same reason a ring group is: several dialplans can share one, and a name means nothing outside the tenant that defined it.
Overriding the global blocks
A tenant may override recording, storage, voicemail, smtp and security
field by field — a key it does not mention keeps the global value:
kind: Tenant
metadata: { name: acme }
spec:
domains: [acme.pbx.example.com]
voicemail:
maxMessage: 5m # this tenant only
smtp:
from: Acme Phones <phones@acme.example>
Not all of them take effect. storage and security.acl are merged and then
ignored, and only enabled and mode are honoured from recording. The
reference has the
table.
A tenant can also take a dataDir of its own instead of a subdirectory of the
global one — see The data directory.
Other keys
| Key | Purpose |
|---|---|
description | Free text, shown in the editor. |
interTenant | Tenants this one may dial directly. Needs the global allowInterTenant. |
featureCodes | Validated, and not dispatched on — see Feature codes. |
The full key-by-key listing is in the tenant configuration reference.