siphon-control (Python)
Python client for the SIPhon
external control plane (siphon-control.v1) — an ARI/ESL-class rail for driving
handed-over calls out of process. Built with PyO3 over the
async Rust client, so the wire is hidden: no manual JSON, no request-id
bookkeeping.
Two connection modes
The plane runs in one of two modes; both are exposed here and share the SAME
@on_call decorator and the SAME Call handle — only the transport differs.
- Inbound-persistent (
ControlClient) — the app dials siphon and holds one long-lived socket (does thehellohandshake). Simplest to reason about; use it for development and single-process controllers. - Per-call-connect (
ControlServer) — siphon dials the app per handed-over call, so the app is a WebSocket server. Each accepted connection owns exactly one call and the first frame is a pushedStasisStart(nohello). This is the documented production default for multi-pod controllers: because the accepting socket is the call, "the audio lands on the wrong pod" can't happen.
Inbound-persistent
import asyncio
from siphon_control import ControlClient, ControlError
client = ControlClient(app="ivr-app", token="s3cr3t",
url="ws://siphon:9090/control/ws")
@client.on_call
async def handle(call):
await call.answer()
try:
await call.transfer("sip:agent@pbx") # REFER, awaits correlated reply
except ControlError as error:
print("transfer rejected:", error.code)
async def main():
async with client: # closes on the way out — see Shutdown below
await client.run()
asyncio.run(main())
Per-call-connect
import asyncio
from siphon_control import ControlServer, ControlError
server = ControlServer(app="ivr-app", token="s3cr3t", bind="0.0.0.0:8790")
@server.on_call
async def handle(call):
await call.answer()
try:
await call.transfer("sip:agent@pbx")
except ControlError as error:
print("transfer rejected:", error.code)
async def main():
async with server:
await server.serve()
asyncio.run(main())
Shutdown
Both classes are async context managers, and async with is the recommended
shape. close() is the same thing explicitly.
It matters more than it looks. run() / serve() are driven by a background
tokio task, and every handed-over call is dispatched from another one. Nothing
joins those tasks and the runtime outlives the interpreter, so an app that
finishes without closing leaves them delivering results into an asyncio loop —
and then into a Python — that is no longer there. Closing first means there is
nothing in flight to strand.
Not closing is handled rather than fatal: a handover arriving after the loop or interpreter has gone is dropped, and a handler cancelled during teardown is not reported as a failure. That is damage control, not a substitute for closing.
API
ControlClient (inbound-persistent)
ControlClient(app, token, url=…, protocol=1, reply_timeout_ms=…, reconnect_backoff_ms=…)@client.on_call— register an async (or sync) per-call handler.await client.connect()/await client.run()— connect / drive (reconnect + resync).await client.command(verb, module=None, target=None, args=None)— the generic{module, verb, target, args}primitive for any adapter (SIP today; SMPP/SS7 later).await client.originate(channel, to, *, media=False, sdp=None, body=None, content_type=None, …, session_timer=None)— place an outbound call under a channel id you choose; resolves to{"channel", "call_id", "sip_call_id"}once the INVITE is on the wire. Exactly one media plan (media=True,sdp=orbody=).session_timer={"expires", "min_se", "refresher"}runs an RFC 4028 session timer on the call, each key left out taking the server's default. What the server would refuse raisesValueErrorbefore a frame goes out.await client.describe()— adapter schema.client.shutdown()— stop the client and unblockrun().client.close()— shutdown, plus drop the handler so nothing else is dispatched.async with client:does this on the way out. See Shutdown above.
ControlServer (per-call-connect)
ControlServer(app, token, bind="0.0.0.0:8790", reply_timeout_ms=…)—bindis the address the app listens on for siphon to dial; the token is validated on the incoming upgrade.@server.on_call— the SAME decorator +Callhandle asControlClient.await server.bind()— bind the listener; resolves to the bound address string (bind to…:0to learn the ephemeral port before siphon dials in).server.local_addr— the bound address oncebind()/serve()has run, elseNone.await server.serve()/await server.run()— accept siphon's per-call dials forever (stop by cancelling the task).server.close()— drop the handler so no further accepted call is dispatched.async with server:does this on the way out. See Shutdown above.
Call (shared by both modes)
Callverbs:answer(),answer_with(code, …),progress(),reject(code, reason),hangup(reason=None),refer(to)/transfer(to),set_header(name, value),get_header(name),set_var(key, value),get_var(key),command(verb, args=None),next_event().- Media verbs
play_file(file)/dtmf(digits)raiseControlErrorwithcode == "unsupported_verb"until the server implements media.
Errors
A rejected command raises ControlError carrying a stable .code
(not_found, forbidden, unsupported_verb, unauthorized, …).
Build
maturin develop # into the active venv
maturin build --release
The target interpreter is free-threaded CPython 3.14t (the SIPhon runtime); the wheel also loads on a standard GIL build.
License
MIT
Release files for siphon-control 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| siphon_control-0.3.0.tar.gz | 98.6 kB | Details |
Built distributions (wheels)
Total release size: 15.8 MB
Release files / siphon_control-0.3.0.tar.gz
| Download URL | siphon_control-0.3.0.tar.gz |
|---|---|
| Size | 98.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4f3baf237a0ff90ea8855bc17895c07e81f335e20ece4f7a6ec2bff4688148bf
|
|
BLAKE2b-256 checksum How to use checksums |
b202cfa370173f7a8d1d85a751095c8bead1f206f6d9011bab05f02241887513
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / siphon_control-0.3.0-cp314-cp314t-win_amd64.whl
| Download URL | siphon_control-0.3.0-cp314-cp314t-win_amd64.whl |
|---|---|
| Size | 1.8 MB |
| Tags | CPython 3.14 CPython 3.14 free-threading Windows x86-64 |
|
SHA-256 checksum How to use checksums |
e7a5e0de2b2b9bf4547efa2d40aac689fd159b6039ccbcd219a8414f299d4127
|
|
BLAKE2b-256 checksum How to use checksums |
8fdefb343ea67046e9eb23fe5245067f2cd7446468433e19c65b5e9235c69b65
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / siphon_control-0.3.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | siphon_control-0.3.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 2.0 MB |
| Tags | CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
554a3e929db86aede3678216b44ac2e96ea6502f8a358a419f4bce0a970e6c1b
|
|
BLAKE2b-256 checksum How to use checksums |
8add367d9cc6975ef5cc9cebb8a230d8430c8120432690dfcbf498b7b37a4b7c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / siphon_control-0.3.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | siphon_control-0.3.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ ARM64 |
|
SHA-256 checksum How to use checksums |
15c1d092885785bb372fc0f67ba7bfee43b1d20e651aaf8f355f20e7d5b2119c
|
|
BLAKE2b-256 checksum How to use checksums |
a1a5dc328e8b6e8ec652412955c2c002a3d5b985a7b4c93e2bc1d959eb16df02
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / siphon_control-0.3.0-cp314-cp314t-macosx_11_0_arm64.whl
| Download URL | siphon_control-0.3.0-cp314-cp314t-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.9 MB |
| Tags | CPython 3.14 CPython 3.14 free-threading macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
24ddf8b18dc5778329ce44c733202bfa8366115d561e6bf86ea114b57d40f178
|
|
BLAKE2b-256 checksum How to use checksums |
bfd46114f63c827dbd0d2e57fd907e8014f5a236e910e892e380a1260641c3bc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / siphon_control-0.3.0-cp314-cp314-win_amd64.whl
| Download URL | siphon_control-0.3.0-cp314-cp314-win_amd64.whl |
|---|---|
| Size | 1.8 MB |
| Tags | CPython 3.14 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
a3c5d953eb58cb75740b65ea6ad89acdf9e8c90bf1ad7c1d317a161209734c00
|
|
BLAKE2b-256 checksum How to use checksums |
0c06d5783983a933f65ea51a52d85d25276f5af527c5e190bcca0207b6dac6d6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / siphon_control-0.3.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | siphon_control-0.3.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 2.0 MB |
| Tags | CPython 3.14 Linux glibc 2.17+ x86-64 |
|
SHA-256 checksum How to use checksums |
bb99e6b9d0d255ee9ef0031c07227261056b38c42ca240d9ea0ca0efc45c1904
|
|
BLAKE2b-256 checksum How to use checksums |
f073ff391f4212af3de56d44632617cea2dde93637a3b9491119a25ee8dca829
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / siphon_control-0.3.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | siphon_control-0.3.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | CPython 3.14 Linux glibc 2.17+ ARM64 |
|
SHA-256 checksum How to use checksums |
3452613fe9816b2e5ced0b64846160804bcfb76d18fc4c2ce7134c13dbc9d610
|
|
BLAKE2b-256 checksum How to use checksums |
28a9b1cc21c41f77610c42ccb22920c35350d5f093d115b3cc7e6fa160748a93
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency logRelease files / siphon_control-0.3.0-cp314-cp314-macosx_11_0_arm64.whl
| Download URL | siphon_control-0.3.0-cp314-cp314-macosx_11_0_arm64.whl |
|---|---|
| Size | 1.9 MB |
| Tags | CPython 3.14 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
70d981e0c03d701f656e0d87e6e64ecbfd14f7141e3bf31623c8d1848c5cad77
|
|
BLAKE2b-256 checksum How to use checksums |
b9b884b81179f253e069bd80af9badf75430414a883241b9d1792044023d15ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 16, 2026.
Transparency log