Metadata-Version: 2.4
Name: django-portflow
Version: 1.1.0
Summary: Auto-expose your Django dev server through Portflow on runserver (HTTPS + visitor WSS on the same host).
Author: Likeasoft Inc.
License: Proprietary
Project-URL: Homepage, https://portflow.me
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: Django>=4.2

<!--
  Copyright (c) 2026 Likeasoft Inc. All rights reserved.
  Proprietary and confidential. Unauthorized copying or distribution is prohibited.
-->

# django-portflow

Auto-expose your Django dev server through [Portflow](https://portflow.me) on
`manage.py runserver`. Add the app, set an API key, and every dev run prints a
public URL and wires it into `ALLOWED_HOSTS` and `CSRF_TRUSTED_ORIGINS` — no
terminal, no manual `portflow expose`.

The tunnel is owned by the local `portflow-runner` daemon (one live gateway
WebSocket per machine), so the Django process stays thin and reloads cleanly.

## Install

```bash
pip install django-portflow
```

Ensure a recent runner is available (bundled with the Portflow CLI, or
`npx @portflow.me/runner`). Visitor WebSockets need runner **1.1.0**+ and SDK
**1.2.1**+ on the machine.

## Usage

```python
# settings.py
INSTALLED_APPS = [
    "portflow_django",
    # ...
]

PORTFLOW_API_KEY = os.environ["PORTFLOW_API_KEY"]
```

Run the dev server:

```bash
python manage.py runserver
```

You will see:

```
Portflow: https://abc123.portflow.me -> http://127.0.0.1:8000
```

The tunnel host is appended to `ALLOWED_HOSTS` and `CSRF_TRUSTED_ORIGINS`
automatically.

## Visitor WebSockets (WSS)

An HTTP tunnel exposes **both** HTTPS and WSS on the **same origin**. There is
no separate WebSocket tunnel or second subdomain — if your dev server answers
WebSocket upgrades on `http://127.0.0.1:8000/ws/`, browsers can connect to
`wss://abc123.portflow.me/ws/` while the page is served from
`https://abc123.portflow.me`.

Requirements:

| Piece | Minimum |
| --- | --- |
| `portflow-runner` on the machine | 1.1.0 |
| `@portflow.me/sdk` (pulled in by the runner) | 1.2.1 |
| Agent protocol | v4 |

### Browser client

Derive the WebSocket URL from the page origin so the same JavaScript works
locally and through the tunnel:

```javascript
const url = new URL("/ws/", location.href);
url.protocol = location.protocol === "https:" ? "wss:" : "ws:";
const socket = new WebSocket(url);
```

### Django Channels

Nothing extra in `django-portflow` — run Channels on the same port you expose
(typically `8000` with `daphne` or `runserver` + ASGI). Cookies and session auth
on the upgrade are forwarded. If you validate `Origin`, allow your tunnel host:

```python
# settings.py (example)
PORTFLOW_ORIGIN = os.environ.get("PORTFLOW_PUBLIC_ORIGIN", "")
if PORTFLOW_ORIGIN:
    CSRF_TRUSTED_ORIGINS.append(PORTFLOW_ORIGIN)
```

The bootstrapper already adds the tunnel hostname to `CSRF_TRUSTED_ORIGINS` when
`runserver` starts.

### Plain `ws` on the same server

Attach a WebSocket handler to the same HTTP server Portflow forwards to — for
example with [Django Channels](https://channels.readthedocs.io/) or a custom
ASGI stack. Portflow relays the browser upgrade to your local port; your app
sees a local WebSocket with `X-Forwarded-Proto: https` when the visitor used TLS.

More recipes (Socket.IO, close codes, troubleshooting):
[WebSockets cookbook](https://github.com/serjant/portflow.me/blob/main/docs/websockets-cookbook.md).

## Configuration

All settings are optional; each falls back to an environment variable.

| Setting                  | Env                      | Default                        |
| ------------------------ | ------------------------ | ------------------------------ |
| `PORTFLOW_ENABLED`       | `PORTFLOW_ENABLED`       | `DEBUG`                        |
| `PORTFLOW_API_KEY`       | `PORTFLOW_API_KEY`       | —                              |
| `PORTFLOW_DEVICE_ID`     | `PORTFLOW_DEVICE_ID`     | auto (`.portflow/device.json`) |
| `PORTFLOW_LOCAL_PORT`    | —                        | `runserver` port, then `8000`  |
| `PORTFLOW_RUNNER_PORT`   | `PORTFLOW_RUNNER_PORT`   | `7878`                         |
| `PORTFLOW_RUNNER_COMMAND`| `PORTFLOW_RUNNER_COMMAND`| auto-detect `portflow-runner`  |
| `PORTFLOW_WILDCARD_HOSTS`| —                        | `False`                        |

Get an API key from the dashboard under Settings → API keys. Keep it in your
environment; never commit it.

## Notes

- Only runs on `runserver`, and only once (guarded against the autoreloader).
  Management commands like `migrate`, `shell`, and `test` never start a tunnel.
- Disabled outside `DEBUG` by default. Set `PORTFLOW_ENABLED=True` to override.
- Add `.portflow/` to your `.gitignore` — it holds the project's device id.
- Requires `portflow-runner` on the machine (bundled with the Portflow CLI).
  Set `PORTFLOW_RUNNER_COMMAND` if it is not on `PATH` (e.g. `npx @portflow.me/runner`).
- Never blocks startup: if Portflow is unreachable, the server runs on localhost
  and logs a warning.
