# QUIC Portal (experimental)
> ⚠️ **Warning**: This library is experimental and not intended for production use.
High-performance QUIC communication library with automatic NAT traversal within Modal applications.
## Current features
- **Automatic NAT traversal**: Built-in STUN discovery and UDP hole punching, using Modal Dict for rendezvous.
- **High-performance QUIC**: Rust-based implementation for maximum throughput and minimal latency
- **Simple synchronous API**: Easy-to-use Portal class with static methods for server/client creation. WebSocket-style messaging.
## Upcoming roadmap
- **TODO: Improved NAT traversal**: Handle more complex client-side NATs using port scanning + birthday technique. Currently only supports clients behind "easy" NATs.
- **TODO: Shared server certificates**: Use a modal.Dict to share server/client certificates, to mutually validate identity.
## Installation
```bash
# Install from PyPi (only certain wheels built)
pip install quic-portal
```
```bash
# Install from source (requires Rust toolchain)
git clone <repository>
cd quic-portal
pip install .
```
## Quick Start
### Usage with Modal
```python
import modal
from quic_portal import Portal
app = modal.App("my-quic-app")
@app.function()
def server_function(coord_dict: modal.Dict):
# Create server with automatic NAT traversal
portal = Portal.create_server(dict=coord_dict, local_port=5555)
# Receive and echo messages
while True:
data = portal.recv(timeout_ms=10000)
if data:
message = data.decode("utf-8")
print(f"Received: {message}")
portal.send(f"Echo: {message}".encode("utf-8"))
@app.function()
def client_function(coord_dict: modal.Dict):
# Create client with automatic NAT traversal
portal = Portal.create_client(dict=coord_dict, local_port=5556)
# Send messages
portal.send(b"Hello, QUIC!")
response = portal.recv(timeout_ms=5000)
if response:
print(f"Got response: {response.decode('utf-8')}")
@app.local_entrypoint()
def main(local: bool = False):
# Create coordination dict
with modal.Dict.ephemeral() as coord_dict:
# Start server
server_task = server_function.spawn(coord_dict)
# Run client
if local:
# Run test between local environment and remote container.
client_function.local(coord_dict)
else:
# Run test between two containers.
client_function.remote(coord_dict)
server_task.cancel()
```
### Manual NAT Traversal
For advanced use cases where you handle NAT traversal yourself, or the server has a public IP:
```python
from quic_portal import Portal
# After NAT hole punching is complete...
# Server side
server = Portal()
server.listen(5555)
# Client side
client = Portal()
client.connect("server_ip", 5555, 5556)
# WebSocket-style messaging
client.send(b"Hello!")
response = server.recv(timeout_ms=1000)
```
## API Reference
### Portal Class
#### Static Methods
##### `Portal.create_server(dict, local_port=5555, stun_server=("stun.ekiga.net", 3478), punch_timeout=15)`
Create a server portal with automatic NAT traversal. **Synchronous operation.**
**Parameters:**
- `dict` (modal.Dict or dict): Modal Dict or regular dict for peer coordination
- `local_port` (int): Local port for QUIC server (default: 5555)
- `stun_server` (tuple): STUN server for NAT discovery (default: ("stun.ekiga.net", 3478))
- `punch_timeout` (int): Timeout in seconds for NAT punching (default: 15)
**Returns:** Connected Portal instance ready for communication
##### `Portal.create_client(dict, local_port=5556, stun_server=("stun.ekiga.net", 3478), punch_timeout=15)`
Create a client portal with automatic NAT traversal. **Synchronous operation.**
**Parameters:**
- `dict` (modal.Dict or dict): Modal Dict or regular dict for peer coordination (must be same as server)
- `local_port` (int): Local port for QUIC client (default: 5556)
- `stun_server` (tuple): STUN server for NAT discovery (default: ("stun.ekiga.net", 3478))
- `punch_timeout` (int): Timeout in seconds for NAT punching (default: 15)
**Returns:** Connected Portal instance ready for communication
#### Instance Methods
##### `send(data: Union[bytes, str]) -> None`
Send data over QUIC connection (WebSocket-style). **Synchronous operation.**
##### `recv(timeout_ms: Optional[int] = None) -> Optional[bytes]`
Receive data from QUIC connection. Blocks until message arrives or timeout. **Synchronous operation.**
**Parameters:**
- `timeout_ms` (int, optional): Timeout in milliseconds (None for blocking)
**Returns:** Received data as bytes, or None if timeout
##### `connect(server_ip: str, server_port: int, local_port: int) -> None`
Connect to a QUIC server (for manual NAT traversal). **Synchronous operation.**
**Parameters:**
- `server_ip` (str): Server IP address
- `server_port` (int): Server port
- `local_port` (int): Local port to bind to
##### `listen(local_port: int) -> None`
Start QUIC server and wait for connection (for manual NAT traversal). **Synchronous operation.**
**Parameters:**
- `local_port` (int): Local port to bind to
##### `is_connected() -> bool`
Check if connected to peer.
##### `close() -> None`
Close the connection and clean up resources.
## Examples
See the `examples/` directory for complete working examples:
- `modal_simple.py` - Basic server/client communication
- `modal_benchmark.py` - Performance benchmarking
## Requirements
- Python 3.9+
- Modal (for automatic NAT traversal)
- Rust toolchain (for building from source)
## Third-party Libraries
This project uses code from:
- `pynat` by Ariel Antonitis, licensed under MIT License
## License
MIT License
Raw data
{
"_id": null,
"home_page": null,
"name": "quic-portal",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "quic, networking, nat-traversal, p2p, communication",
"author": null,
"author_email": "Richard Gong <richard@modal.com>, Daniel Shaar <daniel@modal.com>",
"download_url": "https://files.pythonhosted.org/packages/bb/fe/a18d9ba37618911361695cf01db3bce2ca480ce88e93d15b5908289f995f/quic_portal-0.1.12.tar.gz",
"platform": null,
"description": "# QUIC Portal (experimental)\n\n> \u26a0\ufe0f **Warning**: This library is experimental and not intended for production use.\n\nHigh-performance QUIC communication library with automatic NAT traversal within Modal applications.\n\n## Current features\n\n- **Automatic NAT traversal**: Built-in STUN discovery and UDP hole punching, using Modal Dict for rendezvous.\n- **High-performance QUIC**: Rust-based implementation for maximum throughput and minimal latency\n- **Simple synchronous API**: Easy-to-use Portal class with static methods for server/client creation. WebSocket-style messaging.\n\n## Upcoming roadmap\n\n- **TODO: Improved NAT traversal**: Handle more complex client-side NATs using port scanning + birthday technique. Currently only supports clients behind \"easy\" NATs.\n- **TODO: Shared server certificates**: Use a modal.Dict to share server/client certificates, to mutually validate identity.\n\n## Installation\n\n```bash\n# Install from PyPi (only certain wheels built)\npip install quic-portal\n```\n\n```bash\n# Install from source (requires Rust toolchain)\ngit clone <repository>\ncd quic-portal\npip install .\n```\n\n## Quick Start\n\n### Usage with Modal\n\n```python\nimport modal\nfrom quic_portal import Portal\n\napp = modal.App(\"my-quic-app\")\n\n@app.function()\ndef server_function(coord_dict: modal.Dict):\n # Create server with automatic NAT traversal\n portal = Portal.create_server(dict=coord_dict, local_port=5555)\n \n # Receive and echo messages\n while True:\n data = portal.recv(timeout_ms=10000)\n if data:\n message = data.decode(\"utf-8\")\n print(f\"Received: {message}\")\n portal.send(f\"Echo: {message}\".encode(\"utf-8\"))\n\n@app.function()\ndef client_function(coord_dict: modal.Dict):\n # Create client with automatic NAT traversal\n portal = Portal.create_client(dict=coord_dict, local_port=5556)\n \n # Send messages\n portal.send(b\"Hello, QUIC!\")\n response = portal.recv(timeout_ms=5000)\n if response:\n print(f\"Got response: {response.decode('utf-8')}\")\n\n@app.local_entrypoint()\ndef main(local: bool = False):\n # Create coordination dict\n with modal.Dict.ephemeral() as coord_dict:\n # Start server\n server_task = server_function.spawn(coord_dict)\n \n # Run client\n if local:\n # Run test between local environment and remote container.\n client_function.local(coord_dict)\n else:\n # Run test between two containers.\n client_function.remote(coord_dict)\n \n server_task.cancel()\n```\n\n### Manual NAT Traversal\n\nFor advanced use cases where you handle NAT traversal yourself, or the server has a public IP:\n\n```python\nfrom quic_portal import Portal\n\n# After NAT hole punching is complete...\n# Server side\nserver = Portal()\nserver.listen(5555)\n\n# Client side \nclient = Portal()\nclient.connect(\"server_ip\", 5555, 5556)\n\n# WebSocket-style messaging\nclient.send(b\"Hello!\")\nresponse = server.recv(timeout_ms=1000)\n```\n\n## API Reference\n\n### Portal Class\n\n#### Static Methods\n\n##### `Portal.create_server(dict, local_port=5555, stun_server=(\"stun.ekiga.net\", 3478), punch_timeout=15)`\n\nCreate a server portal with automatic NAT traversal. **Synchronous operation.**\n\n**Parameters:**\n- `dict` (modal.Dict or dict): Modal Dict or regular dict for peer coordination\n- `local_port` (int): Local port for QUIC server (default: 5555)\n- `stun_server` (tuple): STUN server for NAT discovery (default: (\"stun.ekiga.net\", 3478))\n- `punch_timeout` (int): Timeout in seconds for NAT punching (default: 15)\n\n**Returns:** Connected Portal instance ready for communication\n\n##### `Portal.create_client(dict, local_port=5556, stun_server=(\"stun.ekiga.net\", 3478), punch_timeout=15)`\n\nCreate a client portal with automatic NAT traversal. **Synchronous operation.**\n\n**Parameters:**\n- `dict` (modal.Dict or dict): Modal Dict or regular dict for peer coordination (must be same as server)\n- `local_port` (int): Local port for QUIC client (default: 5556)\n- `stun_server` (tuple): STUN server for NAT discovery (default: (\"stun.ekiga.net\", 3478))\n- `punch_timeout` (int): Timeout in seconds for NAT punching (default: 15)\n\n**Returns:** Connected Portal instance ready for communication\n\n#### Instance Methods\n\n##### `send(data: Union[bytes, str]) -> None`\n\nSend data over QUIC connection (WebSocket-style). **Synchronous operation.**\n\n##### `recv(timeout_ms: Optional[int] = None) -> Optional[bytes]`\n\nReceive data from QUIC connection. Blocks until message arrives or timeout. **Synchronous operation.**\n\n**Parameters:**\n- `timeout_ms` (int, optional): Timeout in milliseconds (None for blocking)\n\n**Returns:** Received data as bytes, or None if timeout\n\n##### `connect(server_ip: str, server_port: int, local_port: int) -> None`\n\nConnect to a QUIC server (for manual NAT traversal). **Synchronous operation.**\n\n**Parameters:**\n- `server_ip` (str): Server IP address\n- `server_port` (int): Server port\n- `local_port` (int): Local port to bind to\n\n##### `listen(local_port: int) -> None`\n\nStart QUIC server and wait for connection (for manual NAT traversal). **Synchronous operation.**\n\n**Parameters:**\n- `local_port` (int): Local port to bind to\n\n##### `is_connected() -> bool`\n\nCheck if connected to peer.\n\n##### `close() -> None`\n\nClose the connection and clean up resources.\n\n## Examples\n\nSee the `examples/` directory for complete working examples:\n\n- `modal_simple.py` - Basic server/client communication\n- `modal_benchmark.py` - Performance benchmarking\n\n## Requirements\n\n- Python 3.9+\n- Modal (for automatic NAT traversal)\n- Rust toolchain (for building from source)\n\n## Third-party Libraries\nThis project uses code from:\n- `pynat` by Ariel Antonitis, licensed under MIT License\n\n## License\n\nMIT License \n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "QUIC library with NAT traversal",
"version": "0.1.12",
"project_urls": {
"Bug Tracker": "https://github.com/gongy/quic-portal/issues",
"Documentation": "https://github.com/gongy/quic-portal#readme",
"Homepage": "https://github.com/gongy/quic-portal",
"Repository": "https://github.com/gongy/quic-portal"
},
"split_keywords": [
"quic",
" networking",
" nat-traversal",
" p2p",
" communication"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "95f00dbb6f9fa5678a9492456caae2f9149e530a8267d0a4201742bc8a797eba",
"md5": "b46ccda029d9ca1f4faeef5223e81913",
"sha256": "5565b9bb6b5b413c4031062d274fd0208c30c401d561cfca4cb8a608a89d3c17"
},
"downloads": -1,
"filename": "quic_portal-0.1.12-cp39-abi3-macosx_11_0_arm64.whl",
"has_sig": false,
"md5_digest": "b46ccda029d9ca1f4faeef5223e81913",
"packagetype": "bdist_wheel",
"python_version": "cp39",
"requires_python": ">=3.9",
"size": 1144081,
"upload_time": "2025-07-22T16:31:21",
"upload_time_iso_8601": "2025-07-22T16:31:21.718063Z",
"url": "https://files.pythonhosted.org/packages/95/f0/0dbb6f9fa5678a9492456caae2f9149e530a8267d0a4201742bc8a797eba/quic_portal-0.1.12-cp39-abi3-macosx_11_0_arm64.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "bbfea18d9ba37618911361695cf01db3bce2ca480ce88e93d15b5908289f995f",
"md5": "cea2ea6c0d24e230d31a5825ab546ffd",
"sha256": "1170684d187508e5d9f08cf2b50cb098550bcec2a6226bcff575a375041563f6"
},
"downloads": -1,
"filename": "quic_portal-0.1.12.tar.gz",
"has_sig": false,
"md5_digest": "cea2ea6c0d24e230d31a5825ab546ffd",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 33778,
"upload_time": "2025-07-22T16:31:23",
"upload_time_iso_8601": "2025-07-22T16:31:23.225964Z",
"url": "https://files.pythonhosted.org/packages/bb/fe/a18d9ba37618911361695cf01db3bce2ca480ce88e93d15b5908289f995f/quic_portal-0.1.12.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-22 16:31:23",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "gongy",
"github_project": "quic-portal",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "quic-portal"
}