docs: mark PJSUA2 build complete and add build procedure
All checks were successful
CVE Scan & Docker Build / security-scan (push) Successful in 58s
CVE Scan & Docker Build / build-and-push (push) Successful in 1m42s

Update deployment validation plan to reflect Phase 1b completion — pjsua2
built from pjproject 2.17 on caliban without sudo. Document the non-obvious
RPATH/patchelf step, the status() method name correction, and two remaining
caveats (not captured by pip install, Docker still runs stub media).

Update README to point at the new docs/pjsua2-build.md and clarify stub-mode
behavior.
This commit is contained in:
2026-07-28 22:05:26 -04:00
parent 394e3fc920
commit e9219f2d4a
3 changed files with 177 additions and 14 deletions

View File

@@ -18,7 +18,7 @@ Verified on `caliban`, not assumed:
| Fact | State | Consequence |
|---|---|---|
| `sippy` 2.3.0 | installed | SIP signaling is real |
| `pjsua2` | **not installed** | media pipeline is a **stub** — signaling works, audio does not |
| `pjsua2` | **built 2026-07-28** (pjproject 2.17) | real media pipeline; see [pjsua2-build.md](pjsua2-build.md) |
| `USE_MOCK_SIP` | `true` | engine is `MockSIPEngine`; `/health` can never be `healthy` |
| `SIP_TRUNK_*` | placeholders (`sip.yourprovider.com`) | no trunk configured |
| `SIP_TRUNK_DID` | `+16472474242` | real DID already allocated |
@@ -160,20 +160,48 @@ teardown is clean, no leaked legs after 10 call cycles, suite still green.
---
## Phase 1b — Gate: build PJSUA2
## Phase 1b — Gate: build PJSUA2 ✅ COMPLETE
**Everything audio-shaped is blocked until this is done.** No TTS into a call,
no STT out of one, no recording, no hold-music classification on live audio.
`MediaPipeline` logs `⚠️ PJSUA2 not installed — media pipeline running in stub
mode` and every media call becomes a no-op that *returns successfully* — which
is the dangerous part, since it looks like it worked.
**Done 2026-07-28.** Built out of order (ahead of Phase 1a) because nothing
useful works without it. pjproject **2.17** — newer than expected, with Python
3.13 and modern-gcc support already upstream, so no patching was needed.
Build pjproject with Python bindings (`--enable-shared`, swig python2/3
bindings) into the `/home/robert/env/hold-slayer` venv. Budget real time for
this; it is a from-source build, not a pip install.
`MediaPipeline.status()` now reports **`pjsua2_available: True`**; the stub-mode
warning is gone and the pipeline starts and stops cleanly at 16 kHz. Full
procedure recorded in **[pjsua2-build.md](pjsua2-build.md)**; README note now
points at it.
**Exit criteria:** `python -c "import pjsua2"` succeeds in the venv, and
`/health` / `media_pipeline.get_status()` reports `pjsua2_available: true`.
Notes worth carrying forward:
- **No `sudo` was needed.** Both missing tools (`swig`, `patchelf`) have PyPI
wheels and installed into the venv, so nothing on the host changed outside
`~/src` and `~/.local`.
- **The RPATH step is the non-obvious part.** The bindings compile and install
cleanly and then fail at import with
`ImportError: libpjsua2.so.2: cannot open shared object file`, because
`~/.local/lib` isn't on the loader path. Fixed with `patchelf --set-rpath` on
both the extension **and** all 12 `libpj*.so.2` libraries — patching only the
extension just surfaces the transitive deps one layer down. Chosen over
`LD_LIBRARY_PATH` because that would have to be set for uvicorn, systemd, cron
and every subprocess, and it fails at call time rather than startup. Verified
under `env -i` from `/`, so it depends on no inherited environment.
- **Configure found OpenSSL, ALSA and Opus** — the full codec/TLS surface.
- **165 tests still pass.** All `pjsua2` imports in the codebase are lazy
(inside functions), so the suite still runs without touching real media.
**Exit criteria:** `import pjsua2` succeeds in the venv ✅; `MediaPipeline`
reports `pjsua2_available: true` ✅ (note the method is `status()`, not
`get_status()` as this plan originally said).
**Two caveats this build does not solve:**
1. **Not captured by `pip install -e ".[dev]"`.** It lives outside Python
packaging metadata — a fresh venv, new host or rebuilt container needs it
repeated. Host provisioning, not a dependency.
2. **The Docker image still runs stub media** — the Dockerfile deliberately
skips this build. So the Phase 2 audio validation must run **outside the
container**, or the Dockerfile needs a pjproject build stage. Worth deciding
before Phase 2, since it determines where you test.
---
@@ -301,7 +329,7 @@ docs. The README config table was already current — no drift there.
| # | Blocker | Blocks | Severity |
|---|---|---|---|
| 1 | PJSUA2 not installed | all audio: TTS/STT/recording/classifier in-call | **hard gate** |
| 1 | PJSUA2 not installed | all audio: TTS/STT/recording/classifier in-call | **fixed** — pjproject 2.17 built, `pjsua2_available: True` |
| 2 | REGISTER accepts any credentials, no digest auth | safe exposure of port 5060 | **security** |
| 3 | `TTS_BASE_URL` unset → defaults to app's own port | TTS entirely | config — ✅ fixed in template; **still set it in your real `.env`** |
| 4 | STT/LLM endpoints unreachable as tested | Phase 2 | environment |