Accounts and extensions
An account is a credential a phone registers with. An extension is a number inside a tenant. An account names the extension it belongs to, and several accounts may name the same one.
Each account is its own document, and metadata.name is the SIP username:
configVersion: lyno/v1
kind: Account
metadata:
name: alice
tenant: acme
spec:
password: "change-me"
extension: "100"
displayName: Alice
---
configVersion: lyno/v1
kind: Account
metadata:
name: alice-mobile
tenant: acme
spec:
password: "change-me-too"
extension: "100" # same extension: both devices ring
---
configVersion: lyno/v1
kind: Account
metadata:
name: bob
tenant: acme
spec:
password: "and-me"
extension: "101"
| Field | Required | Purpose |
|---|---|---|
metadata.name | yes | The SIP username, unique within the tenant. |
password | yes | Digest password, stored in plain text in the file. |
extension | yes | The extension number this account belongs to. |
displayName | no | Caller name shown on internal calls. |
record | no | Overrides the recording mode for calls this account answers or places. Unset follows the tenant. |
Registration
Phones register to the tenant's SIP domain with the PBX as their outbound proxy:
| Field | Value |
|---|---|
| Username | the account username |
| Domain / SIP server | the tenant domain, e.g. acme.pbx.example.com |
| Outbound proxy | the PBX address, e.g. 192.0.2.10:5060 |
The domain in the request selects the tenant and becomes the digest realm. A registration whose domain matches no tenant is refused with a log line naming the domain it offered.
Bindings are keyed tenant/extension, so nothing that looks up "extension 100"
without a tenant can ever be right.
Expiry
The requested expiry comes from the Contact expires parameter, or from the
Expires header, and is clamped:
| Value | |
|---|---|
| Minimum | 60s |
| Default when the phone asks for nothing | 300s |
| Maximum | 3600s |
A REGISTER with Expires: 0 removes the binding; Contact: * with
Expires: 0 removes every binding for that account. The 200 OK echoes the
current bindings with their remaining lifetime.
NAT
Endpoints are dialled at the source address observed during REGISTER, not the address advertised in the Contact header, because NAT clients advertise private addresses. Nothing needs configuring for the phone side of NAT.
If the PBX itself is behind NAT, set sip.externalHost and
media.externalIp — see Running behind NAT.
Several devices on one extension
Every registered device for an extension rings in parallel. The first to answer wins and the others are cancelled. This is how a desk phone and a softphone share extension 100 without any group configuration.
Extensions with runtime state
Two things can change what happens when an extension is rung, without any change to these files:
- Do-not-disturb makes the extension contribute no ring targets, which looks exactly like a phone that is not registered.
- Call forwarding sends the call somewhere else.
Both are set by the endpoint itself through feature codes, and both are kept in the data directory rather than written back into the configuration. An extension whose calls are vanishing and whose phone is registered is the usual sign of one of them being set.
A mailbox is also keyed by account extension — see Voicemail.
Extensions without an account
An extension number does not need an account. Extension 200 in the quick start is a dialplan entry point reached from a DID: it plays a greeting and rings other extensions, but nothing registers as "200". This is the normal shape for hunt groups, out-of-hours announcements and IVR-style entry points.
Conversely, an account needs no Extension document to be reachable. A colleague
dialling 100 gets the phone directly, and a ring group or follow-me stage can name
100 as a target, because those resolve against the account roster.
What a plan adds is everything past ringing: a fallback to voicemail, a greeting,
a time condition. Two places also require one — an InboundRoute or an IVR
option of type extension must name a dialplan, not a bare account — so give any
number you deliver a DID to a plan, even if it holds one dial step:
configVersion: lyno/v1
kind: Extension
metadata:
name: "100"
tenant: acme
spec:
description: Alice
steps:
- id: ring
type: dial
timeout: 30s
target:
kind: endpoint
endpoint: "100"
- id: box # what the plan is really for
type: voicemail
Naming a plan after an account extension is not a clash: the plan wins, for internal callers as much as external ones, which is exactly how that extension gets voicemail on no answer.
Passwords
Passwords are stored in plain text; digest authentication needs them. Lyno
writes configuration files with mode 0600 and the container image runs as a
non-root user, but the operational rule is simple: treat every file holding
these documents as a secret, and keep it out of any public repository.
A placeholder like change-me is a real credential the moment the PBX is
reachable, and the guard bans a wrong password rather than a right one. Give every
account a long random password — including one that exists only to own a shared
mailbox and that no handset ever registers as.