Skip to main content

Production-oriented unified runtime for FastAPI + Streamlit

Project description

FluxLit (fluxlit)

Documentation Status PyPI version Python versions CI Release License

Production-oriented unified runtime for FastAPI and Streamlit: one public port, one CLI, and a single FluxLit app object for APIs plus UI pages.

Documentation (hosted): https://fluxlit.readthedocs.io/en/stable/

Security: Vulnerability reporting and dependency audit notes are in SECURITY.md. CI runs pip-audit after installing the auth extra (core + PyJWT/cryptography).

Topic Read the Docs
Quick start Quick start
Architecture & routing Architecture
Config & fluxlit.toml Configuration
Security & threat model Security architecture
Auth patterns (JWT, OIDC BFF, Streamlit) Auth recipes
Adopting auth in an existing app Auth migration
CLI (dev, run, doctor, build) CLI
Testing Testing
Python API API reference
Contributing Contributing
Changelog Changelog
Roadmap Roadmap

Also in the repo: Product & architecture plan (source for long-form product context; overview is on Read the Docs under Architecture).


Overview

FluxLit targets teams that want FastAPI for HTTP APIs and Streamlit for interactive UIs without wiring separate ports, ad hoc reverse proxies, and brittle dev scripts. The runtime uses a sidecar model: Streamlit runs in a subprocess; a Starlette ASGI gateway fronts both the API and the UI.


Requirements

  • Python 3.10+
  • Dependencies are declared in pyproject.toml (FastAPI, Uvicorn, Streamlit, Typer, httpx, websockets, etc.).

Install

pip install fluxlit

JWT validation, JWKS, and OIDC/BFF helpers require the optional extra:

pip install "fluxlit[auth]"

With the extra installed you get JWT validation (HS256 and JWKS/RS256), OIDC discovery, and BFF-style login/token-exchange helpers. High-level entry points:

  • FastAPI: app.make_jwt_bearer() from FLUXLIT_JWT_* env vars, or app.attach_oidc_login(oidc_client) with FLUXLIT_PUBLIC_BASE_URL and FLUXLIT_OIDC_BFF_SECRET.
  • Streamlit: the page client is unauthenticated (for public routes and bootstrapping). For secured /api/... routes, use ApiClient.for_fluxlit(bearer_token=...) or auth_header_factory so every request sends Authorization. After an OIDC redirect, prepare_streamlit_api_client(st) performs the server-side code exchange and returns a client that carries the session bearer.

See Auth recipes, Security architecture, and Auth migration. A minimal runnable demo lives under examples/reference_auth/.

For local development of FluxLit itself:

git clone https://github.com/eddiethedean/fluxlit.git
cd fluxlit
pip install -e ".[dev]"

Quick start

Create a project (optional):

fluxlit new my-app
cd my-app

Define a FluxLit app (e.g. app.py):

from fluxlit import FluxLit

app = FluxLit(title="Admin Portal")

@app.api.get("/users")
def users():
    return [{"name": "Ada"}]

@app.page("/")
def home(st, client):
    st.title("Dashboard")
    st.write(client.get("/users").json())

Run the unified server (default import path app:app, or target from fluxlit.toml / [tool.fluxlit]):

fluxlit dev
# or explicitly:
fluxlit dev app:app
  • Browser: open the URL shown by Uvicorn (default http://127.0.0.1:8000).
  • API: routes are mounted under /api (e.g. GET /api/users).
  • OpenAPI / docs: http://127.0.0.1:8000/api/docs (Swagger) and /api/openapi.json.
  • Health: http://127.0.0.1:8000/api/healthz (hidden from OpenAPI).

From Streamlit code, ApiClient calls the API using a base URL that includes /api (set automatically as FLUXLIT_INTERNAL_API_BASE when using fluxlit dev / fluxlit run). Use paths like client.get("/users"), not client.get("/api/users").

For typed JSON responses, use ApiClient.get_model / post_model with Pydantic models.

If routes use Depends(JWTBearer) (or similar), build an authenticated client—see Calling secured APIs from Streamlit below.


How routing works

Browser
   │
   ▼
┌──────────────────────────────────────┐
│  FluxLit gateway (Uvicorn, one port)  │
├──────────────────────────────────────┤
│  /api/*     → FastAPI (path prefix    │
│               stripped inside the app) │
│  /*         → Streamlit (HTTP + WS    │
│               proxy to subprocess)     │
└──────────────────────────────────────┘

Anything that is not under /api is forwarded to Streamlit (including WebSockets used by Streamlit). Reserve /api for your HTTP API.

Note: the API prefix is configurable via FluxlitSettings.api_mount_path (default /api).


Calling secured APIs from Streamlit

The client passed into @app.page handlers is a plain ApiClient with no Authorization header. That is intentional: use it for public endpoints, token issuance, or OIDC exchange, then create a client that attaches credentials for protected routes.

from fluxlit.client import ApiClient

@app.page("/")
def home(st, client):
    token = st.session_state.get("access_token")  # set after your login / BFF exchange
    if not token:
        st.info("Sign in first.")
        return
    with ApiClient.for_fluxlit(bearer_token=token) as api:
        st.write(api.get("/me").json())

For OIDC + BFF flows, prepare_streamlit_api_client(st) (from fluxlit) combines the one-time auth_code exchange with an authenticated client—details and variants (auth_header_factory, env-driven JWT) are in the Auth recipes doc.

Install fluxlit[auth] for JWT/OIDC helpers used in those guides.


CLI

Command Description
fluxlit dev [target] Development server: Streamlit subprocess + gateway. Default target: CLI arg → fluxlit.toml / pyproject.toml [tool.fluxlit]app:app.
fluxlit run [target] Same stack without auto-reload.
fluxlit doctor [target] Environment checks (import, bind, deps, FLUXLIT_INTERNAL_API_BASE). Exits non-zero on failures unless --warnings-only.
fluxlit build [target] Write a starter Dockerfile and .dockerignore (use --output / -o, --force to overwrite).
fluxlit new <name> Scaffold a minimal app.py in a new directory.
python -m fluxlit Equivalent entry to the fluxlit console script.

Options for dev / run include --host, --port, --log-level, --proxy-headers, --forwarded-allow-ips. fluxlit dev --reload reloads the API gateway process only via Uvicorn; the Streamlit subprocess is not restarted. Use --reload-scope=gateway (default) or restart fluxlit to pick up Streamlit UI changes.


Configuration

Precedence: CLI flags override environment variables; environment overrides project file defaults; then FluxlitSettings field defaults.

Project file

  • fluxlit.toml in the current working directory (top-level keys), or
  • [tool.fluxlit] in pyproject.toml if fluxlit.toml is absent.

If both exist, fluxlit.toml wins. Supported keys include target, gateway_host, gateway_port, log_level, api_mount_path, and root_path.

Example fluxlit.toml:

target = "app:app"
gateway_host = "127.0.0.1"
gateway_port = 8000
log_level = "info"

Environment (FluxlitSettings)

See Configuration and the API reference (fluxlit.config). Variables use the FLUXLIT_ prefix and optional .env file.

Variable Role
FLUXLIT_TITLE App title (default for FastAPI / UX).
FLUXLIT_GATEWAY_HOST / FLUXLIT_GATEWAY_PORT Defaults for binding (also used in settings; CLI overrides bind for dev/run).
FLUXLIT_ROOT_PATH ASGI root path behind a reverse proxy (passed through to FastAPI).
FLUXLIT_INTERNAL_API_BASE Set by the runtime for Streamlit-side ApiClient (includes /api).
FLUXLIT_ENABLE_REQUEST_LOGGING If true, log each API request (method, path, status) at INFO with request id context.

Suggested project layout

my_app/
├── app.py              # FluxLit instance, @app.api routes, @app.page handlers
├── pkg/                # Optional: Python package for discover_pages
│   ├── __init__.py
│   └── pages/          # call discover_pages("pages", package="pkg")
│       ├── __init__.py
│       └── reports.py  # def register(app): @app.page(...) ...
├── services/
├── static/
├── fluxlit.toml        # Optional defaults for CLI
└── .env                # FLUXLIT_* and secrets (do not commit)

Optional page packages: each pkg/pages/*.py module may define register(app: FluxLit) -> None that attaches @app.page handlers; call app.discover_pages("pages", package="pkg") after constructing FluxLit.


Package layout (src/fluxlit)

Autogenerated detail: Python API reference. Summary:

Module Purpose
app FluxLit application object
cli Typer CLI
client ApiClient (httpx) for server-side API calls
config FluxlitSettings
gateway ASGI router + HTTP/WebSocket proxy (request id context + debug logs)
project_config fluxlit.toml / [tool.fluxlit] loading
logging_context Request id ContextVar for gateway / API
runtime Subprocess orchestration, Uvicorn entry
streamlit_main Streamlit entry script (FLUXLIT_APP)
testing FluxLitTestClient (FluxLit-native API + Streamlit test helpers)
api Optional APIRouter helpers
auth Forward-auth / trusted-proxy user helpers
jwt_auth JWT bearer dependencies, JWKS, scopes/roles (fluxlit[auth])
oidc OIDC client + BFF routes (fluxlit[auth])
streamlit_auth Streamlit helpers, prepare_streamlit_api_client (fluxlit[auth])
security_middleware Optional security headers on FluxLit.api

Testing

FluxLit uses the same “built-in” testing platforms you likely already know:

  • FastAPI: starlette.testclient.TestClient
  • Streamlit: streamlit.testing.v1.AppTest (version-dependent)

FluxLit also ships a small wrapper: FluxLitTestClient.

from fluxlit import FluxLit, FluxLitTestClient

app = FluxLit(title="Test")
client = FluxLitTestClient(app)

assert client.api_get("/healthz").status_code == 200

For Streamlit, you can run FluxLit’s Streamlit entrypoint via AppTest:

at = client.streamlit(target="my_app:app", extra_sys_path=".")

Development (contributors)

See Contributing on Read the Docs for setup, docs builds, and PR expectations.

pip install -e ".[dev]"
ruff check src tests && ruff format src tests
python -m pytest
python -m mypy src/fluxlit

Features: today vs planned

Available in current alphas

  • Single public port; managed Streamlit subprocess
  • Gateway: /api → FastAPI; HTTP + WebSocket proxy to Streamlit
  • @app.page + st.navigation integration; optional discover_pages for package layouts
  • fluxlit new, dev, run, doctor, build
  • fluxlit.toml / [tool.fluxlit] project defaults; ApiClient.get_model / post_model
  • Request id propagation (X-Request-ID) and optional API access logging
  • Optional fluxlit[auth]: JWT (HS256 + JWKS/RS256), OIDC + BFF login routes, Streamlit code exchange + ApiClient helpers, FluxLit.make_jwt_bearer / attach_oidc_login, forward-auth helpers, opt-in security headers (see Auth recipes)
  • Typed package; Ruff + Mypy + Pytest in-tree; CI on GitHub Actions

Planned (see Roadmap on Read the Docs)

  • Hardened reload (Streamlit lifecycle), first-class health/metrics
  • Deeper session/cookie presets and deployment cookbooks
  • Official Docker/Kubernetes examples beyond fluxlit build templates
  • OpenAPI-generated client (optional)

Philosophy

FluxLit should stay Pythonic (explicit, typed, easy to reason about), production-minded (proxy-safe, observable, deployable), and honest about Streamlit’s process/WebSocket model until a native ASGI path is proven.


License

MIT — see pyproject.toml metadata.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fluxlit-0.3.0.tar.gz (60.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fluxlit-0.3.0-py3-none-any.whl (43.9 kB view details)

Uploaded Python 3

File details

Details for the file fluxlit-0.3.0.tar.gz.

File metadata

  • Download URL: fluxlit-0.3.0.tar.gz
  • Upload date:
  • Size: 60.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fluxlit-0.3.0.tar.gz
Algorithm Hash digest
SHA256 6c99740fc66fe81a02e0e1332ada97ce8396fa94ba1ebcd80b391c97224be2a0
MD5 3efd2c964378bcb7c12eb1f8c1d094ae
BLAKE2b-256 2aad189c2b01db000d158f1315c03c486464b92550255193b6c0f39d2fac114b

See more details on using hashes here.

File details

Details for the file fluxlit-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: fluxlit-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 43.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for fluxlit-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 73d7bf8227ff8b79220b973e787dc70e271bba1190e319d4e1b14d85ca0eb9eb
MD5 b8354035cd99e9e895f6dad5738b36d6
BLAKE2b-256 d39a800c640b9b8398d137a0494f94d30fa02dcb9f7d523143e681f12dc91a8e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page