12 KiB
Asterisk Lab on Linux VM
Use this runbook to stand up the Track 9 human baseline, the IVR staging entrypoint, and the Voice AI V1 staging entrypoint on the same Asterisk VM.
Target
- Host: dedicated Linux VM
- OS: Ubuntu 22.04 LTS or Ubuntu 24.04 LTS
- Asterisk: 20 LTS
- Integration mode: AMI + custom
UserEvent - Call source: softphone lab only
- Human baseline entrypoint:
7000 - IVR staging entrypoint:
7200 - Voice AI staging entrypoint:
7100
Prepare the VM
- Install Asterisk 20 LTS.
- Ensure these ports are reachable from the platform host:
5060/udpfor SIP5038/tcpfor AMI22/tcpfor SFTP
- Create the recording directory:
/var/spool/asterisk/monitor/mvpcc
Apply repo configs
Copy these files from the repository:
deployment/asterisk/pjsip.confdeployment/asterisk/extensions.confdeployment/asterisk/manager.conf
Before reload:
- Replace default passwords.
- Confirm extension
1001. - Keep extension
7000as the human-only inbound test entrypoint. - Keep extension
7100reserved for Voice AI staging. - Keep extension
7200reserved for IVR staging.
Voice AI dialplan notes:
deployment/asterisk/extensions.confnow defines two queue codes:voice_labfor7000voice_lab_aifor7100
7100falls back to the human dial targets unlessMVPCC_AI_AUDIOSOCKET_SERVICEis populated.- When you are ready to test the media bridge, load
app_audiosocket.soand setMVPCC_AI_AUDIOSOCKET_SERVICE=<media-host>:<port>. - Asterisk
AudioSocket()requires a raw TCP media listener and a UUID per call. The template generates the UUID withuuidgen. - Official app reference:
IVR dialplan notes:
deployment/asterisk/extensions.confdefinesvoice_lab_ivrfor7200.7200calls FastAGI atMVPCC_IVR_FASTAGI_HOSTPORTand expectsprompt_audio_keyvalues to match pre-provisioned Asterisk sound files.- If FastAGI does not return
MVPCC_IVR_TARGET_EXTENSION, the call falls back to the human dial targets.
Configure the platform bridge
Set these environment variables for asterisk-bridge-service:
ASTERISK_BRIDGE_ENABLED=1ASTERISK_AMI_HOST=<linux-vm-host>ASTERISK_AMI_PORT=5038ASTERISK_AMI_USERNAME=mvpccASTERISK_AMI_SECRET=<ami-secret>ASTERISK_AMI_EVENT_PREFIX=MVPCCASTERISK_QUEUE_MAP_JSON={"voice_lab":"<platform_queue_id>","voice_lab_ai":"<platform_ai_queue_id_or_same_queue_id>","voice_lab_ivr":"<platform_ivr_queue_id>"}ASTERISK_BRIDGE_AUTH_MODE=legacy_headers(orbearer_first/bearer)ASTERISK_BRIDGE_AUTH_FALLBACK_LEGACY=1(used only withbearer_first)ASTERISK_BRIDGE_AUTH_SUBJECT=svc:asterisk-bridgeASTERISK_BRIDGE_AUTH_USER=asterisk-bridgeASTERISK_BRIDGE_AUTH_ROLE=adminASTERISK_BRIDGE_AUTH_TOKEN_TTL_SECONDS=300ASTERISK_IVR_FASTAGI_ENABLED=1ASTERISK_IVR_FASTAGI_HOST=0.0.0.0(or the explicit bridge bind IP)ASTERISK_IVR_FASTAGI_PORT=4573ASTERISK_IVR_DTMF_TIMEOUT_SECONDS=5ASTERISK_IVR_MAX_NO_INPUT_RETRIES=2ASTERISK_IVR_MAX_INVALID_RETRIES=2IVR_RUNTIME_TRUSTED_SERVICE_SUBJECTS=svc:asterisk-bridgeVOICE_ADAPTER_TRUSTED_SERVICE_SUBJECTS=svc:asterisk-bridge,svc:ivr-serviceRECORDING_IMPORT_TRUSTED_SERVICE_SUBJECTS=svc:asterisk-bridgeRECORDING_IMPORT_ALLOW_ADMIN=1(set0in strict mode)ASTERISK_SFTP_HOST=<linux-vm-host>ASTERISK_SFTP_PORT=22ASTERISK_SFTP_USERNAME=<linux-user>ASTERISK_SFTP_PASSWORD=<linux-password>ASTERISK_SFTP_BASE_PATH=/var/spool/asterisk/monitorASTERISK_TRANSFER_TARGET_MAP_JSON={"voice_lab":"2001","voice_lab_ai":"2001","voice_lab_ivr":"7200"}AI_VOICE_ENABLED=1AI_VOICE_QUEUE_CONFIG_JSON={"voice_lab_ai":{"mode":"ai_first","agent_profile":"voice_support","handoff_queue_code":"voice_lab","language":"ru"}}AI_VOICE_RUNTIME_SERVICE_URL=http://ai-voice-runtime-service:8000AI_VOICE_RUNTIME_TRUSTED_SERVICE_SUBJECTS=svc:ai-voice-runtimeAI_VOICE_ASR_PROVIDER=openaiAI_VOICE_TTS_PROVIDER=openaiAI_VOICE_MAX_CONTEXT_SEGMENTS=8AI_VOICE_HANDOFF_TIMEOUT_SECONDS=8
For Track 9 QA acceptance baseline, keep:
- bridge auth mode in compatibility headers (
X-User/X-Role) - this is expected for Track 9 baseline and moved to hardening in Track 9.1
For Track 9.1 hardening, switch to:
ASTERISK_BRIDGE_AUTH_MODE=bearer_firstfirst- then
ASTERISK_BRIDGE_AUTH_MODE=bearerafter successful smoke and no auth regressions - set
RECORDING_IMPORT_ALLOW_ADMIN=0 - in Helm-based environments, use
deployment/helm/values.track9-strict.yamlas an overlay
Voice AI V1 staging guidance:
- Start with
MVPCC_AI_AUDIOSOCKET_SERVICE=blank inextensions.conf. - This lets
7100validate the AI control-plane wiring while still falling back to the existing human voice path. - Only point
MVPCC_AI_AUDIOSOCKET_SERVICEto a live AudioSocket listener after the media listener is reachable from the VM. AI_VOICE_RUNTIME_SERVICE_URLis not the AudioSocket address; it is only the HTTP control-plane URL.
Register the softphone
In your softphone:
- username:
1001 - password: the value set in
pjsip.conf - server: the Linux VM IP/hostname
QA preflight (required)
- Copy
.env.production.templateto local.env.production. - Complete
.env.production.checklist.md. - Start the stack with production env loaded:
powershell -ExecutionPolicy Bypass -File scripts\start_track9_qa.ps1
- Run:
python scripts\migrate_core_db.py
pytest -q
python scripts\track9_preflight.py --base-url http://127.0.0.1:8080 --check-sftp
python scripts\track9_preflight.py --base-url http://127.0.0.1:8080 --check-sftp --require-strict-service-auth
Run the first test call
- Start the platform.
- Start
asterisk-bridge-service. - Call
7000from extension1001. - Hang up after the playback finishes.
Expected result:
asterisk-bridge-servicereceives:MVPCCCallStartedMVPCCCallEndedMVPCCRecordingReady
- platform creates a new
voiceinteraction voice_eventscontain:call.startedcall.ended
- one recording is uploaded into
recording-service
Run the IVR-over-Asterisk smoke
- Create or activate an IVR flow for the queue mapped from
voice_lab_ivr. - Make sure every live node uses a valid
prompt_audio_key, and the audio files are present in Asterisk sounds. - Call
7200from extension1001. - Test both branches:
- enter
1or2to complete IVR and transfer into the mapped queue target - stay silent until retry exhaustion to verify fallback into the human baseline
- enter
Expected result:
asterisk-bridge-servicereceivesMVPCCCallStartedwithQueueCode=voice_lab_ivr.ivr_sessionscontains a live session for thecall_id.- interaction timeline contains
ivr.session.startedandivr.step.no_inputorivr.step.completed. - terminal completion writes
voice_events.event_type="ivr.completed". - telephony returns to
mvpcc-transferfor the resolved queue target, or to the human baseline on retry exhaustion.
Run the Voice AI control-plane smoke
Use this smoke before turning on the real AudioSocket media path.
- Keep
MVPCC_AI_AUDIOSOCKET_SERVICE=blank inextensions.conf. - Start the platform and
ai-voice-runtime-service. - Call
7100from extension1001. - Let the call ring through to the fallback human dial targets.
Expected result:
asterisk-bridge-servicereceivesMVPCCCallStartedwithQueueCode=voice_lab_ai.- platform creates:
- a
voiceinteraction - a
voice_ai_sessionsrow - an
ai_sessionsrow withchannel=voice
- a
- operator UI shows the AI badge on the live/recent call card
- normal call recording flow stays green
This smoke proves queue selection, bridge-to-runtime session creation, timeline events, and safe human fallback. It does not prove realtime ASR/TTS yet.
Verify from the platform
- Check:
GET /proxy/asterisk-bridge/asterisk/status
- Then run:
python scripts\track9_preflight.py --base-url http://127.0.0.1:8080 --check-sftp
- For smoke validation:
python scripts\asterisk_lab_smoke.py --base-url http://127.0.0.1:8080 --database-url <database-url> --require-recording
- For formal validation:
python scripts\track9_check.py --base-url http://127.0.0.1:8080 --database-url <database-url> --require-recording
- For Voice AI control-plane validation, also verify:
GET /proxy/asterisk-bridge/asterisk/live-callsGET /proxy/asterisk-bridge/asterisk/live-calls/{call_id}/ai-summaryai.session_startedexists in the interaction timeline for the7100call
Build formal acceptance evidence pack
After smoke/check pass:
python scripts\track9_collect_evidence.py --base-url http://127.0.0.1:8080 --database-url <database-url> --run-checks --require-recording
Then fill:
docs/acceptance/track9/<timestamp>/track9-acceptance.md
At minimum include:
- bridge status snapshot
- smoke output
- track9_check output
- sample rows from:
asterisk_event_logvoice_events- recording linkage
Retry semantics and duplicate safety
Bridge event statuses:
received: event accepted by bridge, no forward result yet.forwarded: event processed successfully and forwarded to platform services.failed: forwarding failed; inspectlast_error, fix root cause, then retry.
Retry endpoint:
POST /proxy/asterisk-bridge/asterisk/events/{bridge_event_id}/retry
Expected behavior:
- Retry is safe for the same
bridge_event_id. - Existing
call_id -> interaction_idlinks prevent duplicate interaction creation. - Recording retry will update missing upload state if SFTP/upload issue is fixed.
- Keep retries manual in QA until root cause is verified.
Timeout / retry policy baseline (QA)
| Area | Current baseline | Operator action |
|---|---|---|
| AMI reconnect | ASTERISK_BRIDGE_POLL_INTERVAL_SECONDS (default 1s) reconnect loop |
Verify ami_connected=true in /asterisk/status |
| SFTP fetch | Single attempt per event processing | Fix credentials/path/network, then call retry endpoint |
| Recording upload | Single upload attempt per event processing | Fix recording-service reachability/auth, then retry |
| Failed event aging | No auto-waiver | Track 9 is not accepted with unresolved failed events |
Troubleshooting
AMI is not connected- verify
manager.conf, firewall, and credentials
- verify
Unknown QueueCode- ensure dialplan
QueueCodematchesASTERISK_QUEUE_MAP_JSON - for Voice AI staging, verify both
voice_labandvoice_lab_ai - verify mapped
queue_idexists in platform:GET /proxy/routing/queues
- re-run failed event with retry endpoint
- ensure dialplan
7100immediately falls back to a human and no AI badge appears- verify
AI_VOICE_ENABLED=1 - verify
AI_VOICE_QUEUE_CONFIG_JSONcontainsvoice_lab_ai - verify the
MVPCCCallStartedevent showsQueueCode=voice_lab_ai
- verify
7100errors as soon as AudioSocket is enabled- verify
app_audiosocket.sois loaded on Asterisk - verify
MVPCC_AI_AUDIOSOCKET_SERVICEpoints to a raw TCP media listener, nothttp://...:8000 - verify the media host and port are reachable from the Asterisk VM
- verify
No uploaded recordings linked to Asterisk events- verify SFTP credentials and
/var/spool/asterisk/monitor/mvpcc - verify
ASTERISK_SFTP_BASE_PATH - verify
POST /proxy/recording/recordings/import-uploadpath is reachable from bridge host - retry failed
MVPCCRecordingReadyevents after fix
- verify SFTP credentials and
Failed bridge events present- inspect:
GET /proxy/asterisk-bridge/asterisk/events?status=failed
- then retry with:
POST /proxy/asterisk-bridge/asterisk/events/{bridge_event_id}/retry
- inspect:
Track 9.2 production cutover
After QA acceptance is signed, switch to controlled production rollout:
- track9-2-production-cutover.md
scripts/track9_2_cutover.ps1