pulse-lets-go/docs/deployment.md
Maksim Totmin f20d5aa0e2 docs: restructure documentation into docs/ directory
- Create docs/ with 7 focused files: architecture, api, scoring, agent,
  deployment, freeswitch, development
- Rewrite README.md as concise overview with links to docs/
- Remove AGENTS.md (content merged into docs/ and README)
2026-06-25 22:35:19 +07:00

4.3 KiB

Deployment

Quick Deploy

make deploy      # full deploy to /opt/pulse-lets-go
make install-systemd  # install systemd unit

Directory Structure (after deploy)

/opt/pulse-lets-go/
├── bin/pulse-lets-go       # Binary
├── data/
│   ├── config.json         # Configuration
│   ├── trunks.json         # Trunks
│   └── users.json          # Users
├── web/                    # SvelteKit static files
└── log/
    └── metrics.YYYY-MM-DD.log  # ASCII metrics log

systemd Unit

# deploy/pulse-lets-go.service
[Unit]
Description=Pulse Lets Go — Telephone Load Balancer
After=network.target

[Service]
User=pulse
Group=pulse
ExecStart=/opt/pulse-lets-go/bin/pulse-lets-go
WorkingDirectory=/opt/pulse-lets-go
Restart=always
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/opt/pulse-lets-go/data /opt/pulse-lets-go/log

[Install]
WantedBy=multi-user.target

Security

  • NoNewPrivileges=true — privilege escalation blocked
  • ProtectSystem=strict — system directories read-only
  • ProtectHome=yes — home directory isolated
  • PrivateTmp=true — isolated /tmp
  • JWT secret and API-key auto-generated on first run

Production Deployment (8 Steps)

Step 1 — Deploy to Server

make deploy
scp -r /opt/pulse-lets-go devadmin@10.101.60.81:/opt/
scp contrib/route.lua devadmin@10.101.60.81:/tmp/

Step 2 — NATS Server (if not installed)

ssh devadmin@10.101.60.81
sudo mkdir -p /opt/nats
# Install nats-server from repository or copy binary
# Run: nats-server -p 4222 -D &

Step 3 — Deploy Agent on PBX Nodes

Repeat for each UC/Asterisk node:

# Copy binary and config to each PBX:
scp bin/pulse-lets-go-agent devadmin@PBX_IP:/usr/local/bin/
scp contrib/agent-uc.json devadmin@PBX_IP:/etc/pulse-lets-go-agent/agent.json

# Start:
ssh devadmin@PBX_IP "systemctl start pulse-lets-go-agent"

# Check log:
# journalctl -u pulse-lets-go-agent -f
# → [ami] connected to 127.0.0.1:6154
# → [agent] published metric: calls=0/150 load=0.28 cpu=99% status=ok

Step 4 — route.lua in FS Scripts

sudo mkdir -p /etc/freeswitch/scripts
sudo cp /tmp/route.lua /etc/freeswitch/scripts/

Step 5 — Dialplan

Add pulse_route extension in /etc/freeswitch/dialplan/default.xml before other extensions:

<extension name="pulse_route">
  <condition field="destination_number" expression="^(.*)$">
    <action application="set" data="balancer_url=http://127.0.0.1:8080"/>
    <action application="lua" data="route.lua"/>
  </condition>
</extension>
sudo fs_cli -x "reloadxml"

Step 6 — Create Balance Trunks

Trunks are created automatically when an agent sends its first metric with sip_gateway. Manual creation:

curl -X POST .../api/trunks \
  -d '{"name":"uc06","type":"balance","node_id":"uc06","gateway":"sip:10.101.60.115:5060","enabled":true}'

Step 7 — Start pulse-lets-go

# config.json auto-creates on first run in /opt/pulse-lets-go/data/
# Add esl block to config.json:
# "esl": {"host": "127.0.0.1", "port": 8021, "password": "ClueCon"}
/opt/pulse-lets-go/bin/pulse-lets-go

Step 8 — Verify

curl http://127.0.0.1:8080/api/health | jq '.connections'
# → {"nats":"connected","esl":"connected"}
curl 'http://127.0.0.1:8080/api/route?caller_id=123&dest=456'
# → {"node_id":"pbx-03","score":88,"sip_gateway":...}

Rollback

# 1. Remove pulse_route from dialplan
sudo sed -i '/pulse_route/,/<\/extension>/d' /etc/freeswitch/dialplan/default.xml
sudo fs_cli -x "reloadxml"

# 2. Stop pulse-lets-go
sudo systemctl stop pulse-lets-go

# Calls continue through original dialplan without balancer.

Access Permissions (production)

Operation Required Permissions
Read FS configs root or freeswitch
fs_cli -x commands root or freeswitch
ESL (port 8021) ACL in event_socket.conf.xml
Install software root
Write to /etc/freeswitch/scripts/ root
reloadxml root or freeswitch

Tested Versions

Component Version
FreeSWITCH 1.10.12+
NATS Server 2.10+
Go 1.26
OS Linux (CentOS 7+, AlmaLinux, Arch)