apntalk/freeswitch-xml-projection

Standalone PHP package for rendering APNTalk SIP endpoint projections as FreeSWITCH mod_xml_curl directory XML.

Maintainers

Package info

github.com/apn-ra/freeswitch-xml-projection

pkg:composer/apntalk/freeswitch-xml-projection

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v0.2.0 2026-05-18 09:53 UTC

This package is auto-updated.

Last update: 2026-05-18 10:08:27 UTC


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_auth parsing 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\XmlCurlRequest
  • APNTalk\FreeSwitchXmlProjection\Http\XmlCurlRequestParser
  • APNTalk\FreeSwitchXmlProjection\Http\XmlCurlResponse
  • APNTalk\FreeSwitchXmlProjection\Directory\DirectoryDocument
  • APNTalk\FreeSwitchXmlProjection\Directory\DirectoryDomain
  • APNTalk\FreeSwitchXmlProjection\Directory\DirectoryUser
  • APNTalk\FreeSwitchXmlProjection\Directory\DirectoryParam
  • APNTalk\FreeSwitchXmlProjection\Directory\DirectoryVariable
  • APNTalk\FreeSwitchXmlProjection\Directory\DirectoryCredential
  • APNTalk\FreeSwitchXmlProjection\Directory\PlainPasswordCredential
  • APNTalk\FreeSwitchXmlProjection\Directory\A1HashCredential
  • APNTalk\FreeSwitchXmlProjection\Directory\DirectoryXmlRenderer
  • APNTalk\FreeSwitchXmlProjection\Dialplan\DialplanDocument
  • APNTalk\FreeSwitchXmlProjection\Dialplan\DialplanContext
  • APNTalk\FreeSwitchXmlProjection\Dialplan\DialplanExtension
  • APNTalk\FreeSwitchXmlProjection\Dialplan\DialplanCondition
  • APNTalk\FreeSwitchXmlProjection\Dialplan\DialplanAction
  • APNTalk\FreeSwitchXmlProjection\Dialplan\DialplanXmlRenderer
  • APNTalk\FreeSwitchXmlProjection\Dialplan\Conference\ConferenceRoomName
  • APNTalk\FreeSwitchXmlProjection\Dialplan\Conference\ConferenceProfileName
  • APNTalk\FreeSwitchXmlProjection\Dialplan\Conference\ConferenceEntryTarget
  • APNTalk\FreeSwitchXmlProjection\Dialplan\Conference\ConferenceEntryAction
  • APNTalk\FreeSwitchXmlProjection\Result\ResultXmlRenderer
  • APNTalk\FreeSwitchXmlProjection\Security\Redactor
  • APNTalk\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-hash over 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.