Skip to main content

Security

Sukko is built with security as a core design principle, not an afterthought. Every connection is authenticated, every channel is tenant-scoped, and every backend link supports encryption in transit. Whether you're running a single tenant in development or hundreds in production, the same security model applies — multi-layered, enforced by default, and configurable where you need it.

Security at a Glance​

LayerProtection
ConnectionJWT authentication (ES256, RS256, EdDSA), API key identification, per-IP and global connection rate limiting
ChannelTenant-scoped namespacing, public/user-scoped/group-scoped access patterns
TopicTenant-prefixed Kafka topics, isolated consumer groups
DataTenant-scoped database queries, AES-256-GCM credential encryption
TransportTLS encryption for all backend connections (Kafka, Valkey)

Authentication​

Every WebSocket connection is authenticated before it reaches the message layer. Sukko validates JWTs using industry-standard asymmetric algorithms — your backend signs tokens with a private key, and Sukko verifies them with the registered public key. Private keys never touch the platform.

  • ES256 (ECDSA P-256) — the default and recommended algorithm for most deployments
  • RS256 (RSA 2048+) — supported for environments with existing RSA infrastructure
  • EdDSA (Ed25519) — compact keys and fast verification, also used for admin authentication with stricter lifetime controls

Tokens carry claims that define the connection's identity: tenant_id determines which tenant the connection belongs to, sub identifies the user, and roles and groups control channel access. Expired, revoked, or malformed tokens are rejected at the gateway before any resource is consumed.

For scenarios that don't require user identity — public data feeds, market tickers, read-only dashboards — Sukko supports API key authentication. API keys identify the tenant but carry no user claims, restricting access to public channels only.

Learn more about authentication →

Authorization​

Channel access is enforced at the gateway, before any message reaches the server or backend. Sukko supports three access patterns that map directly to JWT claims:

  • Public channels — accessible to any authenticated connection (or API key holder). No additional claims required.
  • User-scoped channels — require the sub claim to match a placeholder in the channel pattern (e.g., inbox.{principal}). Only the owning user can subscribe.
  • Group-scoped channels — require the groups claim to include the group identifier in the channel name (e.g., rooms.{group_id}). Only group members can subscribe.
  • Per-tenant channel rules — the sole channel-authorization source, available in every edition. Access patterns are defined per tenant through the provisioning API and streamed to the gateway. A tenant with no rules configured is denied all channel access (deny-all by default).

Every access decision is evaluated at the gateway. Denied requests never reach the message bus.

Learn more about channels →

Tenant Isolation​

Tenant isolation is enforced as security scoping at every layer of the stack — channels, subscriptions, topics, and database queries are all scoped to the authenticated tenant, and cross-tenant access is rejected at the gateway before processing. Per-tenant admission controls (connection limits and quotas, Pro) bound each tenant's use of shared capacity; tenants do share process and infrastructure resources, so isolation is a data-access boundary, not a runtime resource-fairness guarantee.

  • Connections — the tenant is extracted from the JWT tenant_id claim at connection time. All subsequent operations are scoped to that tenant.
  • Channels — channel names are internally namespaced as {tenant_id}.{suffix}. A subscription to general.chat in tenant A is a completely different channel from general.chat in tenant B.
  • Topics — Kafka topics are prefixed with the tenant ID, ensuring message streams are physically separated. Consumer groups can be shared or dedicated per tenant.
  • Data — all database queries in the provisioning service are scoped to the tenant. Keys, rules, and quotas for one tenant are invisible to another.

Cross-tenant JWT tokens — where the tenant_id claim doesn't match the target resource — are rejected at the gateway with no further processing.

Defense in Depth on the Ingestion Path​

Gateway authorization is the primary control, but the ws-server consumer re-validates tenant ownership on every record it reads from Kafka — a second, independent layer so a bad or misrouted record can never fan out to the wrong tenant, even if it bypasses the gateway.

For every consumed record, the consumer resolves the topic's owning tenant from the routing snapshot and confirms the record's target channel is prefixed with that same tenant ({tenant_id}.{suffix}). A record whose channel-tenant does not match its topic-tenant — or that carries no channel header at all — is quarantined to the dead-letter queue and never broadcast. This guarantee holds on both the shared and dedicated consumer paths.

Learn more about multi-tenancy →

Key Management​

Sukko never stores private keys. Operators register public keys through the provisioning API, and Sukko uses them exclusively for JWT signature verification. This means a compromised Sukko instance cannot forge tokens — it can only verify them.

  • Per-tenant key registry — each tenant registers their own signing keys, identified by kid (Key ID). Keys are looked up in constant time during token validation.
  • Automatic cache refresh — public keys are cached in memory with automatic background refresh from the database, ensuring low-latency validation without stale keys.
  • Key lifecycle — keys support expiration dates and explicit revocation. Revoked keys are rejected immediately with no grace period. This enables safe key rotation: register a new key, let the old one expire, then revoke it.
  • Algorithm enforcement — each key is registered with its algorithm (ES256, RS256, EdDSA). If a JWT header declares a different algorithm than the registered key, validation fails — preventing algorithm confusion attacks.
  • Admin key isolation — admin keys use a separate registry with stricter controls: EdDSA or RS256 only, maximum 24-hour token lifetime, and a dedicated sukko-admin issuer to prevent cross-contamination with tenant tokens.

Learn more about authentication → | REST API →

Rate Limiting & Abuse Protection​

Sukko protects against abuse at multiple levels, using token bucket rate limiting that handles both burst traffic and sustained load.

  • Per-client message rate limiting — each connected client has its own token bucket. Burst capacity allows short spikes (default: 100 messages), while the sustained rate caps long-term throughput (default: 10 messages/sec).
  • Per-IP connection rate limiting — prevents a single IP address from opening excessive connections. Stale IP entries are automatically cleaned up to prevent memory leaks.
  • Global connection rate limiting — a system-wide rate limit protects against distributed connection floods, even when no single IP exceeds its limit.
  • Publish rate and size limits — separate rate limits for publish operations, plus a maximum message size (default: 64KB) that prevents oversized payloads from consuming resources.
  • Per-tenant connection limits PRO — set maximum concurrent connections per tenant, preventing any single tenant from consuming disproportionate resources.

All rate limit violations are tracked via Prometheus metrics, giving operators visibility into abuse patterns.

Learn more about the gateway →

Transport Security​

All backend connections between Sukko services and infrastructure support TLS encryption, ensuring data is protected in transit across your network.

  • Kafka / Redpanda — TLS encryption with optional SASL authentication (SCRAM-SHA-256, SCRAM-SHA-512). CA certificate verification prevents man-in-the-middle attacks.
  • Valkey — TLS with CA certificate verification for the broadcast bus.

Each backend connection supports configurable CA certificate paths and an insecure mode for development environments with self-signed certificates.

See configuration reference →

Secrets Handling​

Sensitive credentials are never stored in plaintext configuration files or logged to output.

  • Environment variables only — all secrets (database URLs, encryption keys, SASL passwords, authentication tokens) are injected via environment variables. No secrets in config files, no secrets in code.
  • AES-256-GCM encryption — credentials that must be stored (such as push notification provider keys) are encrypted at rest using AES-256-GCM, an authenticated encryption algorithm that provides both confidentiality and integrity.
  • No secret logging — passwords, tokens, and encryption keys are excluded from configuration dumps and debug output. Even in verbose logging mode, secrets are never emitted.

See configuration reference →

Edition-Gated and Planned Security Features​

Available today:

  • Token Revocation PRO — revoke individual JWT tokens (not just signing keys) for immediate session termination
  • Audit Logging ENTERPRISE — audit trail of all provisioning actions with actor attribution (records are always written; the query API is Enterprise)

In active development:

  • Per-Tenant IP Allowlisting ENTERPRISE — restrict tenant connections to approved IP ranges
  • End-to-End Encryption ENTERPRISE — encrypted message delivery where Sukko cannot read message payloads

See the full roadmap →

Next Steps​