← Back to the blog
Operations #security

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.

2 min read
Leer en español

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_PASS exists for first boot.
  • Public play-token — a narrow, anonymous token that only allows watching a specific room, used by the public /play and /embed pages.
  • 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.