apntalk / freeswitch-xml-projection
Standalone PHP package for rendering APNTalk SIP endpoint projections as FreeSWITCH mod_xml_curl directory XML.
Package info
github.com/apn-ra/freeswitch-xml-projection
pkg:composer/apntalk/freeswitch-xml-projection
Requires
- php: ^8.2
- ext-dom: *
- ext-libxml: *
- ext-xmlwriter: *
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.64
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5
README
Framework-agnostic PHP library for parsing FreeSWITCH mod_xml_curl request fields and rendering package-owned directory and dialplan XML projections.
Boundary
APNTalk owns authority. FreeSWITCH owns provider-local runtime behavior. This package owns only XML projection.
v0.1.0 is intentionally narrow:
- Directory
sip_authparsing and projection only. - Reverse-auth, message-count, gateways, and network-list are parsed only enough for the caller to return not-found XML.
- No Laravel dependency.
- No Symfony dependency.
- No PSR-7 dependency.
- No APNTalk core dependency.
- No database, HTTP client, filesystem writes, or FreeSWITCH ESL behavior.
v0.2.0 adds dynamic dialplan conference-entry XML projection. Configuration projection, conference.conf generation, runtime ESL conference control, and APNTalk conferenceReady / callReady decisions remain downstream-owned and out of scope.
Install
composer require apntalk/freeswitch-xml-projection
Public API
APNTalk\FreeSwitchXmlProjection\Http\XmlCurlRequestAPNTalk\FreeSwitchXmlProjection\Http\XmlCurlRequestParserAPNTalk\FreeSwitchXmlProjection\Http\XmlCurlResponseAPNTalk\FreeSwitchXmlProjection\Directory\DirectoryDocumentAPNTalk\FreeSwitchXmlProjection\Directory\DirectoryDomainAPNTalk\FreeSwitchXmlProjection\Directory\DirectoryUserAPNTalk\FreeSwitchXmlProjection\Directory\DirectoryParamAPNTalk\FreeSwitchXmlProjection\Directory\DirectoryVariableAPNTalk\FreeSwitchXmlProjection\Directory\DirectoryCredentialAPNTalk\FreeSwitchXmlProjection\Directory\PlainPasswordCredentialAPNTalk\FreeSwitchXmlProjection\Directory\A1HashCredentialAPNTalk\FreeSwitchXmlProjection\Directory\DirectoryXmlRendererAPNTalk\FreeSwitchXmlProjection\Dialplan\DialplanDocumentAPNTalk\FreeSwitchXmlProjection\Dialplan\DialplanContextAPNTalk\FreeSwitchXmlProjection\Dialplan\DialplanExtensionAPNTalk\FreeSwitchXmlProjection\Dialplan\DialplanConditionAPNTalk\FreeSwitchXmlProjection\Dialplan\DialplanActionAPNTalk\FreeSwitchXmlProjection\Dialplan\DialplanXmlRendererAPNTalk\FreeSwitchXmlProjection\Dialplan\Conference\ConferenceRoomNameAPNTalk\FreeSwitchXmlProjection\Dialplan\Conference\ConferenceProfileNameAPNTalk\FreeSwitchXmlProjection\Dialplan\Conference\ConferenceEntryTargetAPNTalk\FreeSwitchXmlProjection\Dialplan\Conference\ConferenceEntryActionAPNTalk\FreeSwitchXmlProjection\Result\ResultXmlRendererAPNTalk\FreeSwitchXmlProjection\Security\RedactorAPNTalk\FreeSwitchXmlProjection\Security\SensitiveFieldList
See docs/public-api.md for the full surface.
Usage
<?php declare(strict_types=1); use APNTalk\FreeSwitchXmlProjection\Directory\A1HashCredential; use APNTalk\FreeSwitchXmlProjection\Directory\DirectoryDocument; use APNTalk\FreeSwitchXmlProjection\Directory\DirectoryDomain; use APNTalk\FreeSwitchXmlProjection\Directory\DirectoryParam; use APNTalk\FreeSwitchXmlProjection\Directory\DirectoryUser; use APNTalk\FreeSwitchXmlProjection\Directory\DirectoryXmlRenderer; use APNTalk\FreeSwitchXmlProjection\Http\XmlCurlRequestParser; $request = (new XmlCurlRequestParser())->parse($_POST); if (! $request->isDirectory() || $request->action()?->value !== 'sip_auth') { return; } $document = new DirectoryDocument([ new DirectoryDomain( 'tenant.example.test', [DirectoryParam::dialStringDefault()], [], [ new DirectoryUser( '1001', A1HashCredential::fromPlainPassword('1001', 'tenant.example.test', 'secret'), ), ], ), ]); echo (new DirectoryXmlRenderer())->render($document);
Security
- Prefer
a1-hashover plaintext password. - Never log rendered XML containing live credentials.
- Basic auth, mTLS, IP allowlists, rate limits, and audit logging belong at APNTalk's HTTP edge, not in this package.
See docs/security.md.
Dialplan conference entry
v0.2.0 can render safe conference-entry dialplan XML for section=dialplan requests. The package only renders XML; FreeSWITCH joins or creates the conference when it executes the conference dialplan application.
See docs/dialplan-projection.md, docs/conference-entry-projection.md, and docs/conference-boundaries.md.
Fixture provenance
tests/Fixture/Requests/real-directory-sip-auth-redacted.php is based on a real redacted FreeSWITCH Docker mod_xml_curl directory sip_auth capture from 2026-05-09.
The capture used a local Docker lab with service lab01, container freeswitch, host networking, and a temporary local capture endpoint outside the repository. The full local docker/ lab is ignored to avoid committing logs and generated FreeSWITCH state; the sanitized capture evidence is tracked in docs/docker-capture-evidence.md. See docs/fixture-provenance.md.
Dialplan conference-entry fixtures include deterministic synthetic fixtures and a live-captured redacted Docker FreeSWITCH request fixture from the v0.2.0 release gate.
Synthetic dialplan fixtures remain for deterministic local tests. The v0.2.0 Docker live conference-entry smoke passed on 2026-05-18, and the real redacted dialplan request fixture is committed at tests/Fixture/Requests/dialplan-conference-entry-redacted.php. See docs/dialplan-fixture-provenance.md.
Live smoke
An opt-in live Docker FreeSWITCH smoke harness is available for operators who have the local lab. It is not part of normal CI or composer check.
FREESWITCH_XML_PROJECTION_LIVE_SMOKE=1 composer live:smoke
See docs/live-smoke.md.
Live conference-entry release gate
The live conference-entry smoke is an operator-run v0.2.0 release gate. It remains excluded from default CI and composer check.
FREESWITCH_XML_PROJECTION_LIVE_CONFERENCE_SMOKE=1 composer live:conference-smoke
The v0.2.0 release gate passed in an operator-local Docker lab: FreeSWITCH requested section=dialplan, the package parsed and rendered conference-entry XML, FreeSWITCH reached the conference dialplan application, tests/Fixture/Requests/dialplan-conference-entry-redacted.php was captured, and docs/dialplan-fixture-provenance.md records the evidence.
The repo-native command expects an operator-local ignored docker/ lab with service lab02-esl-xml-curl and container freeswitch-xml-curl. The lab is not part of the package release artifact; the committed release evidence is the redacted fixture and provenance docs.
Chaos smoke
An opt-in Docker FreeSWITCH chaos harness is available for controlled local failure-mode validation. It is not part of normal CI or composer check.
FREESWITCH_XML_PROJECTION_CHAOS_SMOKE=1 composer chaos:smoke
See docs/chaos-smoke.md.