Skip to main content

Accounts and extensions

An account is a credential a phone registers with. An extension is a number inside a tenant, backed by a dialplan file. An account names the extension it belongs to, and several accounts may name the same one.

configs/tenants/acme/tenant.yaml
accounts:
- username: alice
password: "change-me"
extension: "100"
display_name: Alice
- username: alice-mobile
password: "change-me-too"
extension: "100" # same extension: both devices ring
- username: bob
password: "and-me"
extension: "101"
FieldRequiredPurpose
usernameyesThe SIP username, unique within the tenant.
passwordyesDigest password, stored in plain text in the file.
extensionyesThe extension number this account belongs to.
display_namenoCaller name shown on internal calls.

Registration

Phones register to the tenant's SIP domain with the PBX as their outbound proxy:

FieldValue
Usernamethe account username
Domain / SIP serverthe tenant domain, e.g. acme.pbx.example.com
Outbound proxythe 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
Minimum60s
Default when the phone asks for nothing300s
Maximum3600s

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.external_host and media.external_ip — 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 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 whose extension has no dialplan file can register and place calls, but calls to it have nowhere to go — so give every extension you intend to reach a file, even if it holds a single dial step:

configs/tenants/acme/extensions/100.yaml
extension: "100"
name: Alice
steps:
- id: ring
type: dial
timeout: 30s
target:
kind: endpoint
endpoint: "100"

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 the tenant directory as a secret, and keep it out of any public repository.