VEXYL AI

Quickstart: Binary

This chapter takes you from the vexyl-gateway bundle to a running gateway that answers a test call. It assumes you run the gateway yourself on a Linux server. For Docker, see the next chapter instead.

Before you start

You need:

  • A Linux server (Ubuntu 20.04+ or Debian 11+), x86-64, on a small VM (more memory for higher call counts).
  • The Vexyl bundle, which contains three items:
Item What it is
vexyl-gateway The gateway binary
node_modules/ Native speech modules. It must sit beside the binary
sounds/ Prompt and filler audio
  • API keys for the providers you will use (speech recognition, speech synthesis, language model).
  • Optionally, a licence key from Vexyl. Without one the gateway runs on the Free tier (see Licence).
  • Node.js and pm2 if you use pm2 to run it. The binary itself does not need Node.js installed.

Ports the gateway uses:

Port Used for Variable
8080 Asterisk connection (AudioSocket) AUDIOSOCKET_PORT
8081 HTTP API and health check HTTP_PORT
8082 Browser widget (WebSocket audio), only when enabled WEBSOCKET_AUDIO_PORT
5080 (UDP) SIP bridge, only when enabled (unset default 5060; this manual sets SIP_PORT=5080, see chapter 05) SIP_PORT

Set HTTP_PORT yourself. HTTP_PORT and AUDIOSOCKET_PORT both fall back to 8080 when unset, and the two servers cannot share a port. This manual uses HTTP_PORT=8081 everywhere.

Install

Pick a directory and copy the bundle into it. The binary must run with that directory as its working directory, because it finds node_modules/, sounds/ and .env relative to it.

sudo mkdir -p /opt/vexyl-gateway
cd /opt/vexyl-gateway
# copy vexyl-gateway, node_modules/ and sounds/ into this directory
sudo chmod +x vexyl-gateway
sudo mkdir -p config cache cdr logs

The layout should now look like this:

/opt/vexyl-gateway/
  vexyl-gateway      binary
  node_modules/      native speech modules
  sounds/
  .env               you create this below
  config/  cache/  cdr/  logs/

Create a service user, hand it the directory, and keep .env readable by that user only, because it holds your API keys:

sudo useradd -r -s /usr/sbin/nologin vexyl
sudo touch .env
sudo chmod 600 .env
sudo chown -R vexyl:vexyl /opt/vexyl-gateway

Licence

With no LICENSE_KEY, the gateway runs on the Free tier: 4 concurrent calls, and it never expires. This is enough to test everything in this chapter.

A paid licence raises the call cap. A key is bound to one machine, so you first ask the binary for this machine's id:

cd /opt/vexyl-gateway
sudo ./vexyl-gateway --get-machine-id

This works before any .env exists. Send the printed id to Vexyl. You get a key back; put it in .env as LICENSE_KEY= (see the next section). If you later change the server's hardware or host identity, the key stops matching and you need a new one.

Minimal .env

Create .env in the install directory. This example uses Sarvam for listening and speaking and a LiteBot bot as the AI brain. Replace the placeholder values.

HTTP_PORT=8081
AUDIOSOCKET_PORT=8080
TELEPHONY_MODE=audiosocket
LLM_PROVIDER=litebot
LITEBOT_API_URL=http://<litebot-host>:3345
LITEBOT_BOT_ID=default
STT_PROVIDER=sarvam
TTS_PROVIDER=sarvam
SARVAM_API_KEY=your-sarvam-key
LICENSE_KEY=

Leave LICENSE_KEY= empty to stay on the Free tier. TELEPHONY_MODE takes audiosocket (the default), sip_bridge or both; the last two turn on the built-in SIP bridge.

See Configuration recipes for full provider recipes: other speech and language providers, languages, voices and the matching API keys.

When you add lines later, append them with a leading and trailing newline so the new line does not merge into the last one:

printf '\nHTTP_ALLOWED_IPS=127.0.0.1,::1\n' | sudo tee -a .env

Restart the gateway after every .env edit.

Run

Option 1: pm2

cd /opt/vexyl-gateway
sudo pm2 start ./vexyl-gateway --name vexyl-gateway --cwd /opt/vexyl-gateway
sudo pm2 save
sudo pm2 startup

The pm2 example runs the gateway as the user who starts pm2 (root, with sudo). pm2 startup prints one command. Run it to make pm2 (and the gateway) start at boot. If pm2 tries to run the binary through Node.js, start it again with --interpreter none.

Option 2: systemd

Create /etc/systemd/system/vexyl-gateway.service:

[Unit]
Description=Vexyl AI Voice Gateway
After=network-online.target

[Service]
User=vexyl
WorkingDirectory=/opt/vexyl-gateway
ExecStart=/opt/vexyl-gateway/vexyl-gateway
Restart=always
RestartSec=3
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

Then enable and start it:

sudo systemctl daemon-reload
sudo systemctl enable --now vexyl-gateway

Check it is up

curl -s 127.0.0.1:8081/health

The reply is JSON. Look at these fields:

Field What to expect
status ok. degraded means a component is unhealthy; read the logs
concurrentCalls active is the calls in progress now. max is your licence cap: 4 on the Free tier
storage Where sessions are kept (memory, or Redis if you configured it)
telephonyMode Matches your TELEPHONY_MODE

concurrentCalls.max is how you confirm which licence the gateway is using. A sipBridge block also appears when the SIP bridge is on.

A punycode deprecation warning in the logs at boot is normal. Ignore it.

If the check fails, read the logs: sudo pm2 logs vexyl-gateway, or sudo journalctl -u vexyl-gateway -e under systemd.

First call

Choose one path.

Asterisk (AudioSocket). Add this to your dialplan. It answers the call and streams the audio to the gateway on the AudioSocket port:

exten => _X.,1,Answer()
exten => _X.,n,Set(SESSION_UUID=${TRIM(${SHELL(uuidgen)})})
exten => _X.,n,AudioSocket(${SESSION_UUID},127.0.0.1:8080)
exten => _X.,n,Hangup()

AudioSocket() needs a real UUID as its first argument, so the dialplan generates one with uuidgen (package uuid-runtime on Debian/Ubuntu; needs Asterisk's func_shell module). Asterisk's own ${UNIQUEID} is not a UUID and is rejected.

Reload the dialplan, call the number and talk to the bot. Before the AudioSocket line you can optionally POST to /session/${SESSION_UUID}/metadata on the HTTP port with values such as caller_id or botId (see HTTP API). For the full Asterisk setup, see Asterisk over AudioSocket.

SIP trunk, no PBX. Set TELEPHONY_MODE=sip_bridge (or both) and point your carrier at port 5080. Follow SIP trunk.

While the call runs, curl -s 127.0.0.1:8081/health shows concurrentCalls.active as 1.

Upgrading

  1. Copy the new binary beside the old one under a different name and note its checksum:

    cd /opt/vexyl-gateway
    md5sum vexyl-gateway.new
    md5sum vexyl-gateway
  2. Wait until concurrentCalls.active in /health is 0, so no caller is cut off.

  3. Keep the old binary as a backup, named after its checksum, then swap and restart:

    sudo mv vexyl-gateway vexyl-gateway.bak-<old-md5>
    sudo mv vexyl-gateway.new vexyl-gateway
    sudo chmod 755 vexyl-gateway
    sudo pm2 restart vexyl-gateway

    Under systemd, use sudo systemctl restart vexyl-gateway instead of the pm2 command. If the bundle also ships a new node_modules/ or sounds/, replace those at the same time.

  4. Check /health again: status is ok and concurrentCalls.max still shows your licence cap.

To roll back, move the backup over the binary and restart:

sudo mv vexyl-gateway.bak-<old-md5> vexyl-gateway
sudo pm2 restart vexyl-gateway

Next: Configuration recipes.