Skip to content

Security

Authentication, encryption, and access control in Punch.


Threat model

Punch operates in a specific threat landscape:

ThreatSeverityMitigation
Unauthorised session accessHighToken-based authentication
SRT stream interceptionHighAuto-generated AES passphrases
Session hijackingMediumShort-lived tokens, role-based access
Denial of serviceLowRate limiting, session TTL
IP address exposureInherentRequired for SRT — peers must know each other’s IP

Explicit non-goal: Hiding peer IP addresses. SRT is a direct peer-to-peer protocol — both sides must know each other’s public IP to connect. Punch facilitates this exchange. If IP privacy is required, a relay (future feature) masks both peers behind the relay’s IP.

Authentication

Session tokens

All API and WebSocket requests require a bearer token. Tokens are HMAC-SHA256 signed, base64url-encoded, and prefixed with p_.

Authorization: Bearer p_eyJzZXNzaW9uIjoiY2FtMSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTc0MTIzNH0.HMAC_SIGNATURE

Token payload:

{
"session": "nab-floor-cam1",
"role": "admin",
"stream": null,
"exp": 1741234567
}
FieldDescription
sessionSession ID this token is valid for
roleadmin or peer
streamStream slot restriction (null = all streams)
expExpiry timestamp (Unix seconds)

Token lifecycle

  1. Session creation returns an admin token (full session control)
  2. Admin generates peer tokens for individual operators (scoped to specific streams)
  3. Tokens embed in QR codes and session URLs for frictionless sharing
  4. Tokens expire with the session TTL (default 30 minutes, configurable)

Roles

Permissionadminpeer
Create sessionYesNo
Close sessionYesNo
Generate peer tokensYesNo
Register as peerYesOwn stream only
View all stream healthYesNo
View own stream healthYesYes
Set tally stateYesNo
Receive tally stateYesYes

Token security

  • Signing key is stored as a Cloudflare Workers secret (wrangler secret put PUNCH_SECRET)
  • Never stored in KV or Durable Object storage — validated statelessly via HMAC
  • Short-lived by default — tokens expire with the session, limiting window of abuse
  • Scoped by design — a peer token for cam-wide cannot access cam-close

Encryption

SRT stream encryption

SRT encrypts payload data using AES in CTR mode. Punch manages this transparently:

  1. Session creation → Punch generates a 32-character random passphrase
  2. Peer registration → Punch sends the passphrase to each peer via authenticated WebSocket
  3. SRT connection → Both peers use the passphrase for AES key derivation (PBKDF2)
  4. Key rotation → SRT rotates derived session keys every 2^24 packets automatically

Passphrase characteristics:

PropertyValue
Length32 characters
Character setAlphanumeric (avoids shell quoting issues)
Generationcrypto.getRandomValues() with rejection-free modular sampling
DistributionTLS-only — over WSS to authenticated peers in peer messages, and over HTTPS in GET /api/session/:name/connect responses to admin tokens (so the dashboard can render copy-paste FFmpeg/OBS strings)
StorageDurable Object memory (persisted in DO storage for hibernation recovery)

Signalling encryption

All Punch communication is over HTTPS/WSS (TLS 1.3, managed by Cloudflare). The signalling channel itself is encrypted in transit.

ChannelEncryption
HTTP APITLS 1.3 (Cloudflare edge)
WebSocketWSS (TLS 1.3)
SRT mediaAES-128/256 (passphrase from Punch)

Key separation

The SRT passphrase and the Punch authentication token are independent credentials:

  • Punch token authenticates the peer to the signalling service
  • SRT passphrase encrypts the media stream between peers
  • Compromising one does not compromise the other

Access control patterns

Single operator (simplest)

Producer creates session → gets admin token → uses it everywhere

One person controls both ends. Admin token used for both registration and monitoring.

Multi-operator production

Producer creates session → admin token
→ generates peer token for cam-1 operator → scoped to "cam-wide" stream
→ generates peer token for cam-2 operator → scoped to "cam-close" stream
→ generates peer token for MCR receiver → scoped to all streams (read-only)

Each operator gets a token that grants access only to their stream. The producer retains full control via the admin token.

QR code sharing

QR codes encode the session URL with an embedded peer token:

https://punch.thåst.se/s/nab-floor-cam1?t=p_eyJ...

Security consideration: Anyone with the QR code can access the session. This is by design — the QR is the credential. For sensitive productions, use short TTLs and generate new tokens per stream slot.

Rate limiting

EndpointLimitWindowPurpose
POST /api/session10per minute per IPPrevent session spam
GET /api/session/:id60per minute per IPPrevent polling abuse
WebSocket connect20per minute per IPPrevent connection flooding
WebSocket messages100per second per connectionPrevent message flooding

Rate limits are enforced at the Worker edge before requests reach the Durable Object.

Session isolation

Each session runs in its own Durable Object instance:

  • No shared state between sessions
  • No cross-session access — tokens are scoped to a single session
  • Independent TTL — one session expiring does not affect others
  • Memory isolation — Durable Object instances run in separate V8 isolates

Operational security

Secret management

Terminal window
# Set the signing secret (do this once)
wrangler secret put PUNCH_SECRET
# Enter a random 64+ character string
# Rotate the secret (invalidates all existing tokens)
wrangler secret put PUNCH_SECRET
# Enter a new random string
# All active sessions will require new tokens

Logging

Punch logs:

  • Session creation and closure (session ID, timestamp)
  • Peer registration (session ID, peer role — not IP addresses in logs)
  • Authentication failures (IP, endpoint, timestamp)
  • Rate limit violations (IP, endpoint)

Punch does not log:

  • SRT passphrases
  • Token values
  • Media content or metadata
  • Peer IP addresses (except on auth failure)

Data retention

DataRetentionStorage
Session stateUntil TTL expiryDO memory (volatile)
Health metricsUntil TTL expiryDO SQLite (ephemeral)
SRT passphraseUntil TTL expiryDO memory (volatile)
TokensStateless (HMAC)Not stored
LogsCloudflare Workers defaultCloudflare (configurable)

When a session expires, all associated data is deleted. There is no persistent record of past sessions unless explicitly configured.

Known limitations

IP address exposure

SRT requires direct peer-to-peer communication. Both peers learn each other’s public IP address through Punch. This is inherent to the protocol and cannot be mitigated without a relay.

Token in URL

QR-code URLs contain the authentication token as a query parameter. This means:

  • The token appears in browser history
  • The token may be logged by intermediate proxies (but is encrypted in transit via TLS)
  • Screenshot sharing may leak tokens

Mitigation: tokens are short-lived and scoped to a single session. Expired tokens are useless.

No certificate-based authentication

SRT uses pre-shared key (passphrase) authentication only. There is no certificate chain, no mutual TLS, no PKI. This is a limitation of SRT itself, not Punch. RIST addresses this with certificate support — noted for users with strict security requirements.