If Apache Iggy is useful to you, give it a star on GitHubStar apache/iggy
Apache Iggy
Server

Security

Authentication, authorization, transport encryption and encryption at rest in the Iggy server.

Iggy provides multiple layers of security covering authentication, authorization, transport encryption, and data encryption at rest.

Authentication

Iggy supports two authentication mechanisms:

Username and password

Users authenticate with a username and password, for example via the Rust SDK's login_user(). Passwords are hashed using Argon2id (a memory-hard hashing algorithm). On first single-node startup, the server generates a random password for the initial root account (username iggy by default) and writes it to the logs. A first cluster boot requires explicit root credentials. You can override this by setting environment variables:

export IGGY_ROOT_USERNAME=iggy
export IGGY_ROOT_PASSWORD=my-secret-password

Or use the --with-default-root-credentials flag for development (sets root credentials to iggy/iggy).

These variables initialize the root account only when it is first created. Supplied values are still validated on later starts, but they do not replace the stored credentials. Use the password-change API or iggy user password with the current password to change an existing account. The --fresh flag deletes all data; it is not needed for a password change.

The root user cannot be deleted, and its permissions are fixed: it always holds every permission.

Personal Access Tokens (PAT)

PATs provide programmatic access with optional expiry. Each user can have up to max_tokens_per_user (default 100) stored tokens, including expired tokens until the cleaner removes them. Tokens are hashed before storage and can be revoked at any time.

# Create a PAT via CLI
IGGY_TOKEN=$(iggy --quiet -u iggy -p my-secret-password pat create my-token 7d)

# Use the PAT for authentication
iggy -t "$IGGY_TOKEN" stream list

An automatic cleaner removes expired tokens at a configurable interval.

Broker API commands require an authenticated session or bearer credential, except for ping and the login/refresh flows that establish or prove a credential. The Prometheus /metrics scrape requires a JWT or personal access token. HTTP CORS preflight responses and embedded /ui static assets are public; the UI's broker API calls still require authentication.

Authorization

Iggy provides granular permissions at three levels:

  1. Global permissions - apply to the whole server and all streams
  2. Stream permissions - scoped to one stream and all its topics
  3. Topic permissions - scoped to one topic within a stream

The root user has full access to everything and bypasses all permission checks. Other users can be assigned specific permissions using the CLI, HTTP API, SDK, or Web UI.

Permission matrix

The global permissions and the operations they unlock:

OperationRequired global permission
Get server statsread_servers
List clients, get clientread_servers
Create, update, delete users; update permissions; change passwordsmanage_users
Get user, list usersread_users
Create, update, delete streamsmanage_streams
Get stream, list streamsread_streams
Create, update, delete topicsmanage_topics
Get topic, list topicsread_topics
Consumer groups: create, delete, join, leave, getread_topics
Poll messagespoll_messages
Send messagessend_messages

Two kinds of implication apply on top of the table:

  • Supersets: every manage_* permission includes its read_* counterpart. In addition manage_streams includes manage_topics, read_streams includes read_topics, and read_topics includes poll_messages. manage_topics also includes send_messages, so manage_streams permits sending too.
  • Self-service: an authenticated user can always read their own account, change their own password, and manage their own personal access tokens, without any of the user permissions above.

Scoped permissions

Instead of (or in addition to) global grants, permissions can be scoped per stream and per topic:

  • Stream scope: manage_stream, read_stream, manage_topics, read_topics, poll_messages, send_messages, plus an optional per-topic map. read_stream includes read_topics and poll_messages within that stream.
  • Topic scope: manage_topic, read_topic, poll_messages, send_messages. read_topic includes poll_messages on that topic.

How permissions cascade

Permissions are checked from top to bottom: global, then stream, then topic. A permission granted at a higher level covers everything below it:

  • Global poll_messages lets the user poll messages from all streams and topics.
  • Stream-scoped poll_messages on stream 1 lets the user poll from all topics in stream 1, but not from other streams.
  • Topic-scoped poll_messages on topic 5 of stream 1 lets the user poll only from that topic.

If a stream has no entry in the user's stream permissions, only global permissions apply to it. The same holds for topics within a stream.

For example, a user that may only consume from stream 42 needs no global permissions at all: grant stream-scoped read_stream and poll_messages on stream 42, and the user can read that stream, list its topics, and poll messages from any topic in it. The read grant also permits consumer-group operations in that stream. For polling without those read and group permissions, grant only poll_messages.

Transport encryption (TLS)

Each transport configures TLS in its own section, and the self_signed semantics differ per transport:

TransportSectionBehavior
TCP[tcp.tls]self_signed = true generates an ephemeral certificate only while cert_file does not exist; an existing PEM pair is loaded instead.
WebSocket[websocket.tls]Same load-or-generate rule as TCP.
QUIC[quic.certificate]TLS is mandatory (part of the QUIC spec). self_signed = true always generates an ephemeral certificate and ignores cert_file/key_file, logging a warning if the files exist.
HTTP[http.tls]No self_signed option: HTTPS requires certificate and key files in cert_file/key_file.

For production deployments, provide proper certificates via cert_file and key_file. Ephemeral certificates change on every start, so a trust configuration pinned to one generated certificate does not survive a restart. See Networking for the surrounding transport configuration.

Data encryption at rest

Iggy supports optional AES-256-GCM encryption for message payloads and user headers. They are encrypted before storage and decrypted for polling. Metadata journals, metadata snapshots, and structural record headers remain unencrypted. This option does not provide whole-directory encryption. The encryption key must decode from base64 to 32 bytes.

[encryption]
enabled = false
key = ""  # 32-byte base64-encoded key

JWT (HTTP API)

The HTTP API uses JWT (JSON Web Tokens) for session management. Tokens are signed with HS256 by default and have configurable expiry, clock skew tolerance, and audience/issuer validation:

[http.jwt]
algorithm = "HS256"
issuer = "iggy.apache.org"
audience = "iggy.apache.org"
access_token_expiry = "1 h"
clock_skew = "5 s"

Further keys (valid_issuers, valid_audiences, not_before, use_base64_secret, the signing secrets) are covered in Configuration.

Signing secrets: encoding_secret and decoding_secret default to empty. Without cluster.auth, the server generates a secure random secret on every start. That's a safe single-node default with two consequences: issued tokens die on restart, and in a cluster each node signs with its own key, so bearers are node-local and follower-to-primary request forwarding stays disabled. For clusters, configure an identical secret on every node (prefer the IGGY_HTTP_JWT_ENCODING_SECRET/IGGY_HTTP_JWT_DECODING_SECRET environment variables over on-disk config), or enable cluster.auth so the JWT key derives from the cluster's shared PSK.

Refresh tokens: POST /users/refresh-token re-issues an access token from a still-valid one presented in the request body, answering the same identity shape as login, so HTTP clients can extend a session without re-sending credentials.

Federated issuers: the opt-in [[http.jwt.trusted_issuers]] list accepts tokens minted by external identity providers for application-to-application flows. Each entry names an issuer, audience, and jwks_url. Signatures are verified against the issuer's JWKS (fetches are rate-limited), and every token from that issuer is remapped onto the configured non-root user_id. With no entries configured, the listener accepts only self-issued tokens. See Configuration for the key reference.

Cluster security

In cluster mode, replica-to-replica traffic can require an authenticated handshake based on a pre-shared key (with a documented rolling rotation procedure) and can be wrapped in TLS 1.3. With cluster.auth enabled and no JWT secrets configured, the PSK also becomes the cluster-wide JWT key source. See Clustering security.

On this page