← Back to the blog
Operations #agency

Recipe: run a multi-tenant streaming agency

One server, one app per client, a scoped token each, per-tenant quotas and an IP firewall. How to carve a StreamHub node into isolated tenants you can bill, cap and secure independently.

3 min read
Leer en español

Why multi-tenant

If you resell streaming — to clients, to franchisees, to internal departments — you need clean walls between tenants: separate data, separate credentials, separate limits, and a way to offboard one without touching the others. StreamHub is built for exactly this. One node carries many apps, each an isolated tenant, and every seam (tokens, quotas, network rules) is per app.

Architecture in words

Each client gets an app with its own config, S3 bucket, database and room namespace. You mint one app-scoped token per client, so a leaked credential only exposes that client. Quotas are enforced per tenant (apps, concurrent streams, recording minutes, egress, storage), and a network security layer applies IP allow/blocklists and auto-ban before any request reaches auth.

1. Create an app per client

export BASE="https://your-server.example.com/api/v1"
export TOKEN="sk_...."     # a global token

curl -s -X POST $BASE/apps -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"acme","displayName":"ACME Corp"}'

Rooms are namespaced under the app prefix, so acme's room launch is acme-launch — no collision with another client's launch.

2. Mint a scoped token per client

curl -s -X POST $BASE/tokens -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"name":"acme-integration","scope":"app","appId":"acme","allowedIps":["203.0.113.0/24"]}'

The plaintext token is returned once — store it immediately. The optional allowedIps whitelist (exact IP or CIDR) restricts where the token can be used. Hand this token to the client; it can never touch another tenant's data.

3. Set per-tenant quotas

RBAC and quotas share one enforcement switch, STREAMHUB_AUTHZ_ENFORCE (off | log | on; fresh installs default on). The free-plan defaults are deliberately small — maxApps 2, maxConcurrentStreams 2, maxRecordingMinutesMonth 300, maxEgressGbMonth 5, maxStorageGb 5. Lift a paying client to a bigger plan:

curl -s -X PATCH $BASE/tenants/<tenantId>/quotas -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' -d '{"plan":"broadcaster"}'

Over-limit calls are rejected with 429 quota_exceeded (with metric, limit, used), and usage is visible at GET /tenants/<tenantId>/usage and as the Prometheus gauges streamhub_tenant_quota / streamhub_tenant_usage.

4. Lock down the network

Turn on the IP layer and, optionally, auto-ban:

STREAMHUB_IP_ACCESS_MODE=enforce
STREAMHUB_AUTOBAN_ENABLED=true
STREAMHUB_AUTOBAN_MAX_OFFENSES=10
STREAMHUB_AUTOBAN_WINDOW_S=300

Manage rules over the (superadmin-only) security API:

curl -s -X POST $BASE/security/ip-rules -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"cidr":"198.51.100.7/32","action":"block","note":"abusive scraper"}'

Precedence runs allow-before-block, with loopback and private ranges always permitted; an allowlisted IP is also shielded from bans. Repeated auth failures trip a time-boxed ban that persists across restarts.

What you should see

Each client operates in its own app with its own token and bucket, invisible to the others. A client hitting its plan limit gets a clean 429; you see every tenant's usage against quota in one place; and blocked or banned IPs are rejected before they ever reach authentication.

Troubleshooting

  • A client can see another's data — check the token was minted with scope: "app" and the right appId; a global token bypasses tenant scoping by design.
  • Quotas not enforced — confirm STREAMHUB_AUTHZ_ENFORCE=on (or run log first in staging to preview denials); superadmin and unscoped tokens are never limited.
  • Legit traffic getting 403/429 — the security middleware does not cover the /hls, /samples, /sdk and /live static mounts; media-port protection (RTMP, WHIP, WebRTC UDP) is your reverse proxy and OS firewall's job. Behind a proxy, make sure trust proxy is set so the client IP is read correctly.

Closing

A single StreamHub node becomes a clean multi-tenant platform you can bill, cap and secure per client — with none of the media leaving your control. If you are standing up an agency or a reseller offering, our team does the tenant model, the billing hooks and the hardening as a managed-ops or consulting engagement.