The StreamHub security model: tokens, JWT, RBAC
Four auth planes, role-based access control with Casbin, and a network layer of IP allow/blocklists and auto-ban. How StreamHub gates every request.
Four planes of authentication
StreamHub separates credentials by purpose, so a leaked one is contained:
sk_API tokens — bearer tokens for the REST API, either global or scoped to one app. This is what your servers and scripts use.- JWT dashboard login — human operators sign in and get a JWT, with 2FA (TOTP) and session management. A break-glass
ADMIN_USER/ADMIN_PASSexists for first boot. - Public play-token — a narrow, anonymous token that only allows watching a specific room, used by the public
/playand/embedpages. - Plugin ingest token — a per-worker-start token so a plugin's worker can push live overlay data without touching the Bearer plane.
RBAC with Casbin
On top of authentication sits authorization. Every route declares the Casbin permission it needs — a resource:action pair like ingress:create or recording:start. Roles bundle permissions; tenants and apps scope them. A global API token or a superadmin bypasses tenant scoping; everyone else is confined to what their role and tenant allow.
Enforcement is phased so you can adopt it safely, controlled by one env var:
STREAMHUB_AUTHZ_ENFORCE=off # no checks
STREAMHUB_AUTHZ_ENFORCE=log # allow, but log every denial
STREAMHUB_AUTHZ_ENFORCE=on # enforce (fresh installs default here)
Run log in staging to see exactly what would be denied before you turn enforcement on in production.
The network layer sits in front
Before authentication even runs, a separate network security module applies IP allow/blocklists and auto-ban as global middleware. You can:
- allow only known office/CDN ranges,
- block specific abusive addresses,
- let repeated auth failures trip an automatic, time-boxed ban.
Because it runs first, an address on the blocklist never reaches the auth or RBAC layers at all — cheap and early rejection of noise.
Layered by design
The planes compose: a request from a blocked IP dies at the network layer; a request with a bad token dies at authentication; a valid token without the right role dies at RBAC. Each layer is narrow, and each failure is logged and countable in the streamhub_errors_total metric.