Skip to main content

Lyno · an open-source PBX by telqo

A PBX you declare, not administer.

Lyno is a multi-tenant PBX in a single Go binary. Tenants, trunks, extensions, IVR menus, queues and voicemail are YAML documents — no database, no admin panel. It reads the whole set at startup, validates it, and runs what it says.

one static binary · ~19 MB image · no database · no web panel

Configuration

Write it down, Lyno runs it

A tenant, a trunk, an extension, a queue — each one is a document that says what it is. Nothing gets applied in a particular order and nothing lives in a database: Lyno reads the set you point -config at, and that set is the system. Where the documents sit carries no meaning — one file, a file per tenant, or a tree something generates.

metadata.name is a resource’s identity and metadata.tenant its namespace, so a queue called support in one tenant is unrelated to one in another. Order lives in a priority rather than in file order. lyno -validate reads the whole set and either accepts it or names the document that is wrong — the PBX never starts half-configured.

the singleton, plus what it talks to
configVersion: lyno/v1
kind: System
spec:
sip:
bindHost: 0.0.0.0
bindPort: 5060
transports: [udp]
defaultTrunk: carrier
timezone: Europe/Amsterdam
---
configVersion: lyno/v1
kind: Trunk
metadata:
name: carrier
spec:
host: sip.provider.example
auth:
username: "12345678"
password: "${CARRIER_PASSWORD}"
registration:
enabled: true
---
configVersion: lyno/v1
kind: OutboundRoute
metadata:
name: national # no tenant: the global fallback
spec:
priority: 20
pattern: '^0[1-9]\d{8}$'
strip: 1
prepend: "+31"
trunk: carrier
How it works

Simple to hold in your head

There is no hidden state to reason about: what the files say is what the PBX does. Everything else on this page follows from these three.

The documents are the source of truth

There is no database. Every resource is a document that says what it is, so a change is a diff you can review and revert. What the PBX writes — call logs, recordings, voicemail — goes to a separate data directory, never back into these files.

Flexible where it matters

Twelve step types compose in any order, and every step can carry a time condition. Anything set globally — recording, storage, voicemail, mail, security — can be overridden by a tenant, field by field.

Scales by adding a document

Tenants share the SIP listeners and carrier trunks but nothing else: their own domains, numbers, extensions and dialplans. Extension 100 can exist in every one of them, and the next tenant is one more document.

Capabilities

What Lyno does

Carrier trunks

Register with a provider or authenticate by source IP. Gateway addresses come from DNS, refreshed every five minutes.

Number routing

Outbound routes match on regex, then strip and prepend before picking a trunk. Inbound routes map a DID to an extension or ring group.

Dialplans

Twelve step types per extension, run top to bottom. A dial nobody answers falls through to the next step.

Follow-me and ring groups

Ring in stages, or everyone at once. The first answer wins and the remaining legs are cancelled.

IVR menus

Keypad menus that live on the tenant, so several plans can share one. Keys may be more than one digit, matched longest-first.

Call queues

Four strategies, static and dynamic agents, music and a periodic announcement while the caller waits.

Voicemail

Per-mailbox greetings, PIN retrieval and a message-waiting lamp. Notification by email with the recording attached.

Call recording

Stereo WAV — caller left, far end right — with a JSON sidecar. Kept on disk or uploaded to S3.

Call logs

One JSON record per call plus a per-day index, written straight to disk. Direction and outcome from a closed set.

Management API

Read call logs, recordings, voicemail, status and security over HTTP with a scoped bearer token. Off by default, loopback-bound.

Source-address guard

Packet rate limits, bans after repeated auth failures, and per-tenant ceilings on concurrency, call rate and outbound minutes.

Feature codes and transfer

Do-not-disturb and forwarding are ordinary steps, so a feature code is just an extension. Blind transfer needs no configuration.

Time conditions

Days, hour ranges that wrap midnight and holidays, in the tenant’s timezone. Any step can carry a when:.

Terminal dashboard

Calls, endpoints, trunks, security and logs in one view. Filter by tenant and every counter follows.

Configuration editor

lyno -configure walks the whole config, validates before writing, and refuses to delete anything still referenced.

Prompt generation

lyno -tts synthesises a whole prompt set from a manifest, before the PBX runs and never during a call.

One static binary

A single Go binary, or a distroless image of about 19 MB. No shell in it, no database beside it.

Operations

Run it, watch it

lyno opens a live dashboard: calls, endpoints, trunks, security and logs. Hang up a call, drop a registration or lift a ban without leaving it. Add -headless to log to stderr instead, under systemd or in a container.

./bin/lyno -config configs/lyno.yaml
┌─ Lyno  PBX by Telqo ─────────────────── 0.0.0.0:5060 · 4/5 online · 1/1 trunks up · uptime 6h12m ─┐
│  Overview   Calls 2   Endpoints 5   Trunks 1   Security 1   Logs        tenant acme               │
├───────────────────────────────────────────────────────────────────────────────────────────────────┤
│   active calls       endpoints          trunks             calls finished     banned ips          │
│   2                  4/5                1/1                148                1                   │
│   in progress        registered         reachable          15 missed          412 rejected        │
│                                                                                                   │
│  Tenants                                                                                          │
│  TENANT    DOMAIN                     ONLINE   CALLS   DIDS                                       │
│  acme      acme.pbx.example.com       3/3      2       2      active                              │
│  globex    globex.pbx.example.com     1/2      0       1      idle                                │
│                                                                                                   │
│  Recent calls                                                                                     │
│  TIME      TENANT   FROM              TO        STATE       DURATION                              │
│  14:02:11  acme     +31612345678      200       answered    00:04:31                              │
│  13:58:02  acme     100               101       answered    00:00:47                              │
│  13:41:19  globex   +31201112233      900       missed      —                                     │
└───────────────────────────────────────────────────────────────────────────────────────────────────┘
 tab switch · t tenant · / filter · x act · ? help · q quit

Ten minutes and two softphones

That is all the quick start needs — no trunk and no carrier contract, which come later and are optional. Point -config at a path that does not exist and Lyno writes a working configuration to start from.