Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

python-peerjs-server

An asyncio-based Python port of the PeerJS signalling server, the WebSocket broker that PeerJS clients use to discover each other and exchange WebRTC offers/answers/candidates.

It's feature-complete and wire-compatible with the original: WebSocket session handling, reconnect, and the HTTP API are all implemented, plus a few deployment conveniences, like rate limiting, proxied-IP support, and bounded queues.

Features

  • WebSocket signalling at {path}/peerjs with id/token/key query-param auth
  • Reconnect support (same id + token re-attaches the socket and drains queued messages)
  • Per-peer message queueing for offline destinations, with periodic EXPIRE sweeps
  • Periodic dead-connection reaping based on heartbeat pings
  • HTTP API: GET {path}{key}/id (fresh peer id) and GET {path}{key}/peers (peer list, opt-in via allow_discovery)
  • CORS header support
  • Fixed-window per-IP rate limiting on the HTTP API
  • Optional X-Forwarded-For trust for real client IPs when running behind a reverse proxy

Requirements

  • Python 3.13+
  • websockets>=14 (the only runtime dependency, installed automatically below)

Installation

pip install python-peerjs-server

For local development (editable install with test/lint tooling):

pip install -e ".[dev]"

Usage

Run as a console script:

peerjs-server --port 9000 --key peerjs

or as a module:

python -m python_peerjs_server --port 9000 --key peerjs

CLI options

Flag Env var Default Description
--host PEERJS_HOST :: Bind host
--port PEERJS_PORT 9000 Bind port
--key PEERJS_KEY peerjs Access key clients must supply
--path PEERJS_PATH / Path prefix for HTTP/WS routes
--allow-discovery PEERJS_ALLOW_DISCOVERY off Expose GET /peers
--cors-origin PEERJS_CORS_ORIGIN unset CORS origin for HTTP API responses
--log-level PEERJS_LOG_LEVEL INFO Logging level

Other server behaviour (timeouts, rate limits, queue caps, proxy trust) is configured via python_peerjs_server.config.Config when embedding the server programmatically; see src/python_peerjs_server/config.py for the full set of fields and their defaults.

Embedding in your own asyncio app

import asyncio
from python_peerjs_server.config import Config
from python_peerjs_server.main import peerjs_serve

asyncio.run(peerjs_serve(Config(port=9000, key="peerjs")))

Logging

If you're running via the CLI, --log-level/PEERJS_LOG_LEVEL already configures this for you (it calls logging.basicConfig); the rest of this section is for embedding python_peerjs_server in your own app.

Every module logs under the python_peerjs_server namespace (logging.getLogger(__name__)), so that one logger name is the knob to turn:

import logging

# See python_peerjs_server logs at INFO+ on stderr
logging.getLogger("python_peerjs_server").setLevel(logging.INFO)
logging.getLogger("python_peerjs_server").addHandler(logging.StreamHandler())

# Or, if your app already configured the root logger, just set the level;
# propagation does the rest
logging.getLogger("python_peerjs_server").setLevel(logging.DEBUG)

# Silence a noisy sub-logger specifically
logging.getLogger("python_peerjs_server.services.web_socket_session").setLevel(logging.WARNING)

A NullHandler is attached by default, so the library stays silent unless logging is configured (either on python_peerjs_server or higher up).

Connecting a PeerJS client

This server only implements the signalling protocol; clients connect using the PeerJS client library (docs, GitHub):

const peer = new Peer("some-id", {
    host: "localhost",
    port: 9000,
    path: "/",
    key: "peerjs",
});

Runnable versions of the standalone and FastAPI embedding patterns live in examples/: standalone.py and fastapi_app.py.

CORS

The PeerJS client calls the HTTP API (GET {path}{key}/id) before it opens the WebSocket. If your page is served from a different origin than the broker (e.g. a Vite/CRA dev server on localhost:5173 talking to a broker on localhost:9000), the browser will block that request with a CORS error unless you set --cors-origin / PEERJS_CORS_ORIGIN / Config.cors_origin to the exact origin (scheme + host + port) serving the page:

peerjs-server --port 9000 --key peerjs --cors-origin http://localhost:5173

It's unset by default (no Access-Control-Allow-Origin header sent), which is fine for same-origin deployments.

Running alongside another web framework

peerjs_serve() is just a coroutine; you can run it inside whatever process already hosts your app instead of standing up a separate service. Pass handle_signals=False so it doesn't fight your app for SIGINT/SIGTERM handling, and pick a port distinct from your main app's.

Runnable versions of both patterns below live in examples/: tornado_app.py and flask_app.py.

Tornado

Tornado runs on the standard asyncio event loop, so the broker can simply be scheduled as another task on it:

import asyncio
import tornado.ioloop
import tornado.web

from python_peerjs_server.config import Config
from python_peerjs_server.main import peerjs_serve


class MainHandler(tornado.web.RequestHandler):
    def get(self) -> None:
        self.write("hello from tornado")


async def main() -> None:
    app = tornado.web.Application([(r"/", MainHandler)])
    app.listen(8888)

    asyncio.create_task(peerjs_serve(Config(port=9000, key="peerjs"), handle_signals=False))

    await asyncio.Event().wait()  # run forever


if __name__ == "__main__":
    asyncio.run(main())

Flask

Flask's dev server is sync/WSGI and blocks its own thread, so the broker has to run on an event loop in a separate thread:

import asyncio
import threading

from flask import Flask

from python_peerjs_server.config import Config
from python_peerjs_server.main import peerjs_serve

app = Flask(__name__)


@app.get("/")
def index() -> str:
    return "hello from flask"


def run_python_peerjs_server() -> None:
    asyncio.run(peerjs_serve(Config(port=9000, key="peerjs"), handle_signals=False))


if __name__ == "__main__":
    threading.Thread(target=run_python_peerjs_server, daemon=True).start()
    app.run(port=8888)

For an ASGI app (FastAPI, Starlette) you can instead start it from a lifespan/startup hook with asyncio.create_task, the same way as the Tornado example: both share a loop with no extra thread needed.

Development

pip install -e ".[dev]"  # install with dev/test/lint dependencies
pytest                 # run tests
ruff check src         # lint
ruff format src        # format
mypy src                # type check
bandit -r src           # security scan

License

MIT, see LICENSE.

Release files for python-peerjs-server 1.0.0b2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for python-peerjs-server 1.0.0b2
File Size Uploaded
python_peerjs_server-1.0.0b2.tar.gz 24.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for python-peerjs-server 1.0.0b2
File Interpreter ABI Platform
python_peerjs_server-1.0.0b2-py3-none-any.whl Python 3 none any Details

Total release size: 44.8 kB

Release files / python_peerjs_server-1.0.0b2.tar.gz

Download URL python_peerjs_server-1.0.0b2.tar.gz
Size 24.5 kB
Tags Source
SHA-256 checksum
How to use checksums
3b9930db0443c509f917389e37c6e7634efc6c9f31e14a2272c110f41b62f9dd
BLAKE2b-256 checksum
How to use checksums
26d5947adc6814dda682261e597dca1b674057b0c0e6d99752a775f250bd5009
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.5

Release files / python_peerjs_server-1.0.0b2-py3-none-any.whl

Download URL python_peerjs_server-1.0.0b2-py3-none-any.whl
Size 20.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3fcb8c2f5ba583b7e8cccd6ee8144f4e5dd37313096e2926a4da20e068dd0a65
BLAKE2b-256 checksum
How to use checksums
2c30b73646f6747ec2e1a63fb8b7206af096d9dacf3b4a71f6946ce550095dfe
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.5

Release history Release notifications | RSS feed

This release

1.0.0b2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page