KurrentDB Documentation

KurrentDB is a database that's engineered for modern software applications and event-driven architectures. Its event-native design simplifies data modeling...

5,818 stars C# C# 1 file ยท ~25,886 tokens #cqrs#database#event-sourcing#event-store#eventsourcing#eventstore
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy KurrentDB on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name kurrentdb -p 2113:2113 --restart always docker.kurrent.io/kurrent-latest/kurrentdb:latest
Configured Services & Ports
โš™๏ธ kurrentdb.db
image: docker.kurrent.io/kurrent-latest/kurrentdb:latest
๐Ÿ”Œ Ports: 2113:2113
docker-compose.yml
yaml (docker-compose.yml)
services:
  kurrentdb.db:
    image: docker.kurrent.io/kurrent-latest/kurrentdb:latest
    environment:
      - KURRENTDB_CLUSTER_SIZE=1
      - KURRENTDB_RUN_PROJECTIONS=All
      - KURRENTDB_START_STANDARD_PROJECTIONS=true
      - KURRENTDB_NODE_PORT=2113
      - KURRENTDB_INSECURE=true
      - KURRENTDB_ENABLE_ATOM_PUB_OVER_HTTP=true
    ports:
      - "2113:2113"
    volumes:
      - type: volume
        source: kurrentdb-volume-data
        target: /var/lib/kurrentdb
      - type: volume
        source: kurrentdb-volume-logs
        target: /var/log/kurrentdb

volumes:
  kurrentdb-volume-data:
  kurrentdb-volume-logs:

File: README.md

What is Kurrent

Event Store โ€“ the company and the product โ€“ are rebranding as Kurrent.

  • The flagship product will be referred to as โ€œthe Kurrent event-native data platformโ€ or โ€œthe Kurrent platformโ€ or simply โ€œKurrent"
  • EventStoreDB will be referred to as KurrentDB
  • Event Store Cloud will now be called Kurrent Cloud

Read more about the rebrand in the rebrand FAQ.

What is KurrentDB

KurrentDB is a database that's engineered for modern software applications and event-driven architectures. Its event-native design simplifies data modeling and preserves data integrity while the integrated streaming engine solves distributed messaging challenges and ensures data consistency.

Download the latest version.
For more product information visit the website.

What is Kurrent Cloud?

Kurrent Cloud is a fully managed cloud offering that's designed to make it easy for developers to build and run highly available and secure applications that incorporate KurrentDB without having to worry about managing the underlying infrastructure. You can provision KurrentDB clusters in AWS, Azure, and GCP, and connect these services securely to your own cloud resources.

For more details visit the website.

Licensing

View KurrentDB's licensing information.

Docs

For guidance on installation, development, deployment, and administration, see the User Documentation.

Getting started with KurrentDB

Follow the getting started guide.

Getting started with Kurrent Cloud

Kurrent can manage KurrentDB for you, so you don't have to run your own clusters.
See the online documentation: Getting started with Kurrent Cloud.

Client libraries

This guide shows you how to get started with KurrentDB by setting up an instance or cluster and configuring it.
KurrentDB supports the gRPC protocol.

KurrentDB supported clients

Community supported clients

Read more in the documentation.

Legacy clients (support ends with EventStoreDB v23.10 LTS)

Deployment

Communities

Join our global community of developers.

Contributing

Development is done on the master branch.
We attempt to do our best to ensure that the history remains clean and to do so, commits are automatically squashed into a single logical commit when pull requests are merged.

If you want to switch to a particular release, you can check out the release branch for that particular release. For example:
git checkout release/v25.0

Building KurrentDB

KurrentDB is written in a mixture of C# and JavaScript. It can run on Windows, Linux and macOS (using Docker) using the .NET Core runtime.

Prerequisites

Once you've installed the prerequisites for your system, you can launch a Release build of KurrentDB as follows:

text
dotnet build -c Release

To start a single node, you can then run:

text
dotnet run --project ./src/KurrentDB/KurrentDB.csproj -c Release -e ASPNETCORE_ENVIRONMENT=Development -- --dev --db ./tmp/data --index ./tmp/index --log ./tmp/log

Alternatively, you can run the KurrentDB - Dev DB profile pre-configured in launchSettings.json:

text
dotnet run --project ./src/KurrentDB/KurrentDB.csproj -c Release -lp "KurrentDB - Dev DB"

Running the tests

You can launch the tests as follows:

text
dotnet test --solution KurrentDB.slnx

Build KurrentDB Docker image

You can also build a Docker image by running the command:

text
docker build --tag mykurrentdb . \
--build-arg CONTAINER_RUNTIME={container-runtime}
--build-arg RUNTIME={runtime}

For instance:

text
docker build --tag mykurrentdb . \
--build-arg CONTAINER_RUNTIME=noble \
--build-arg RUNTIME=linux-x64

Note: Because of the Docker issue, if you're building a Docker image on Windows, you may need to set the DOCKER_BUILDKIT=0 environment variable. For instance, running in PowerShell:

text
$env:DOCKER_BUILDKIT=0; docker build --tag mykurrentdb . `
--build-arg CONTAINER_RUNTIME=noble `
--build-arg RUNTIME=linux-x64

Currently, we support the following configurations:

  1. Noble:
  • CONTAINER_RUNTIME=noble
  • RUNTIME=linux-x64

You can verify the built image by running:

text
docker run --rm mykurrentdb --insecure --what-if

More resources


File: docs/server/security/protocol-security.md


title: Protocol security order: 2

Protocol security

KurrentDB supports gRPC and the proprietary TCP protocol for high-throughput real-time communication. It
also has some HTTP endpoints for the management operations like scavenging, creating projections and so on.
KurrentDB also uses HTTP for the gossip seed endpoint, both for the cluster gossip, and
for clients that connect to the cluster using discovery mode.

All those protocols support encryption with TLS and SSL. You can only use one set of certificates for both TLS and HTTPS.

The protocol security configuration depends a lot on the deployment topology and platform. We have created an
interactive configuration tool, which also has instructions on how to generate and install
the certificates and configure KurrentDB nodes to use them.

::: note
For non-production use, two run modes skip most of the configuration described on this page: --insecure disables TLS entirely, and --dev generates a self-signed certificate for local use.
:::

Certificates configuration

In this section, you can find settings related to configuring the certificates for protocol security (HTTPS and TLS).

Certificate common name

SSL certificates can be created with a common name (CN), which is an arbitrary string. Usually it contains the DNS name for which the certificate is issued.

When cluster nodes connect to each other, they need to ensure that they indeed talk to another node and not something that pretends to be a node. To achieve that, KurrentDB authenticates a connecting node by ensuring that it supplies a trusted client certificate having a CN that matches exactly with the CN in its own certificate. This essentially means that the CN must be the same across all node certificates by default. For example, when using the certificate generator, the CN is set to eventstoredb-node in all node certificates.

::: note
KurrentDB will automatically read the CN from the node's certificate and use it as the CertificateReservedNodeCommonName by default. However, if you still choose to specify the CertificateReservedNodeCommonName in your configuration, it will take precedence.
:::

In practice, it's not always possible to obtain certificates where the CN is the same across all nodes. For instance, when using a public CA, single-domain certificates are very common and these certificates cannot be used on multiple nodes as they are valid for exactly one host. In this case, the CertificateReservedNodeCommonName setting can be configured with a wildcard as per the following example:

If the domains are node1.kurrentdb.mycompany.org, node2.kurrentdb.mycompany.org and node3.kurrentdb.mycompany.org, then CertificateReservedNodeCommonName must be set to *.kurrentdb.mycompany.org.

Format Syntax
Command line --certificate-reserved-node-common-name
YAML CertificateReservedNodeCommonName
Environment variable KURRENTDB_CERTIFICATE_RESERVED_NODE_COMMON_NAME

::: warning
Server certificates must have the internal and external IP addresses (ReplicationIp and NodeIp respectively) or DNS names as subject alternative names.
:::

Disable client authentication EKU validation

A KurrentDB node currently uses a single TLS certificate for

  1. Authenticating as a server when a client application or another node connects to it.
  2. Authenticating as a client when it connects to another node.

When a node is authenticating another node that is connecting to it, it will by default comply with RFC 5280 section-4.2.1.12 and require that the connecting node's certificate has the clientAuth Extended Key Usage (EKU), or that the certificate does not use EKUs at all.

You can disable the clientAuth EKU requirement by setting DisableClientAuthEkuValidation to true. The authentication will still verify the chain of trust, expiration, and common name of the certificate. Please consider whether this is appropriate for your deployment.

This setting allows KurrentDB to operate using certificates signed by publicly trusted certificate authorities, which, largely driven by changes to the Chrome Root Program policy, are moving away from issuing certificates with the clientAuth EKU.

This setting does not affect authentication of users via user certificates, which will still require the clientAuth EKU.

Format Syntax
Command line --disable-client-auth-eku-validation
YAML DisableClientAuthEkuValidation
Environment variable KURRENTDB_DISABLE_CLIENT_AUTH_EKU_VALIDATION

Default: false

Trusted root certificates

When getting an incoming connection, the server needs to ensure if the certificate used for the connection can be trusted. For this to work, the server needs to know where trusted root certificates are located.

KurrentDB will use the default trusted root certificates location of /etc/ssl/certs when running on Linux only. So if you are running on Windows or a platform with a different default certificates location, you'd need to explicitly tell the node to use the OS default root certificate store. For certificates signed by a private CA, you just provide the path to the CA certificate file (but not the filename).

If you are running on Windows, you can also load the trusted root certificate from the Windows Certificate Store. The available options for configuring this are described below.

Format Syntax
Command line --trusted-root-certificates-paths
YAML TrustedRootCertificatesPath
Environment variable KURRENTDB_TRUSTED_ROOT_CERTIFICATES_PATH

Default: n/a on Windows, /etc/ssl/certs on Linux

Certificate file

The CertificateFile setting needs to point to the certificate file, which will be used by the cluster node.

Format Syntax
Command line --certificate-file
YAML CertificateFile
Environment variable KURRENTDB_CERTIFICATE_FILE

If the certificate file is protected by password, you'd need to set the CertificatePassword value accordingly, so the server can load the certificate.

Format Syntax
Command line --certificate-password
YAML CertificatePassword
Environment variable KURRENTDB_CERTIFICATE_PASSWORD

If the certificate file doesn't contain the certificate private key, you need to tell the node where to find the key file using the CertificatePrivateKeyFile setting. The private key can be in RSA, or PKCS8 format.

Format Syntax
Command line --certificate-private-key-file
YAML CertificatePrivateKeyFile
Environment variable KURRENTDB_CERTIFICATE_PRIVATE_KEY_FILE

If the private key file is an encrypted PKCS #8 file, then you need to provide the password with the CertificatePrivateKeyPassword option.

Format Syntax
Command line --certificate-private-key-password
YAML CertificatePrivateKeyPassword
Environment variable KURRENTDB_CERTIFICATE_PRIVATE_KEY_PASSWORD

Certificate store (Windows)

The certificate store location is the location of the Windows certificate store, for example CurrentUser.

Format Syntax
Command line --certificate-store-location
YAML CertificateStoreLocation
Environment variable KURRENTDB_CERTIFICATE_STORE_LOCATION

The certificate store name is the name of the Windows certificate store, for example My.

Format Syntax
Command line --certificate-store-name
YAML CertificateStoreName
Environment variable KURRENTDB_CERTIFICATE_STORE_NAME

You can load a certificate using either its thumbprint or its subject name.
If using the thumbprint, the server expects to only find one certificate file matching that thumbprint in the cert store.

Format Syntax
Command line --certificate-thumbprint
YAML CertificateThumbprint
Environment variable KURRENTDB_CERTIFICATE_THUMBPRINT

The subject name matches any certificate that contains the specified name. This means that multiple matching certificates could be found.
To match any certificate made by the es-gencert-cli tool, you can set the subject name to eventstoredb-node.

If multiple matching certificates are found, then the certificate with the latest expiry date will be selected.

Format Syntax
Command line --certificate-subject-name
YAML CertificateSubjectName
Environment variable KURRENTDB_CERTIFICATE_SUBJECT_NAME

When you are loading your node certificates from the Windows cert store, you are likely to want to load the trusted root certificate from the cert store as well.
The options to configure this are similar to the ones for node certificates.

The trusted root certificate store location is the location of the Windows certificate store in which the trusted root certificate is installed, for example CurrentUser.

Format Syntax
Command line --trusted-root-certificate-store-location
YAML TrustedRootCertificateStoreLocation
Environment variable KURRENTDB_TRUSTED_ROOT_CERTIFICATE_STORE_LOCATION

The trusted root certificate store name is the name of the Windows certificate store in which the trusted root certificate is installed, for example Root.

Format Syntax
Command line --trusted-root-certificate-store-name
YAML TrustedRootCertificateStoreName
Environment variable KURRENTDB_TRUSTED_ROOT_CERTIFICATE_STORE_NAME

Trusted root certificates can also be loaded using either its thumbprint or its subject name.
If using the thumbprint, the server expects to only find one trusted root certificate file matching that thumbprint in the cert store.

Format Syntax
Command line --trusted-root-certificate-thumbprint
YAML TrustedRootCertificateThumbprint
Environment variable KURRENTDB_TRUSTED_ROOT_CERTIFICATE_THUMBPRINT

The subject name matches any certificate that contains the specified name. This means that multiple matching certificates could be found.
To match any root certificate made through the es-gencert-cli tool, you can set the Subject Name to EventStoreDB CA.

If multiple matching root certificates are found, then the root certificate with the latest expiry date will be selected.

Format Syntax
Command line --trusted-root-certificate-subject-name
YAML TrustedRootCertificateSubjectName
Environment variable KURRENTDB_TRUSTED_ROOT_CERTIFICATE_SUBJECT_NAME

Certificate generation tool

Kurrent provides the interactive Certificate Generation CLI, which creates certificates signed by a private, auto-generated CA for KurrentDB. You can use the configuration wizard, that will provide you exact CLI commands that you need to run to generate certificates matching your configuration.

Getting started

The CLI is available from its GitHub Repository. The latest release can be found under the GitHub releases page.

We're releasing binaries for Windows, Linux and macOS. We also publish the tool as a Docker image.

Basic usage for the Certificate Generation CLI:

bash
./es-gencert-cli [options] <command> [args]

Getting help for a specific command:

bash
./es-gencert-cli -help <command>

::: warning
If you are running KurrentDB on Linux, remember that all certificate files should have restrictive rights, otherwise the OS won't allow using them.
Usually, you'd need to change rights for each certificate file to prevent the "permissions are too open" error.

You can do it by running the following command:

bash
chmod 600 [file]

:::

Generating the CA certificate

As the first step CA certificate needs to be generated. It'll need to be trusted for each of the nodes and client environment.

By default, the tool will create the ca directory in the certs directory you created. Two keys will be generated:

  • ca.crt - public file that need to be used also for the nodes and client configuration,
  • ca.key - private key file that should be used only in the node configuration. Do not copy it to client environment.

CA certificate will be generated with pre-defined CN eventstoredb-node.

To generate CA certificate run:

bash
./es-gencert-cli create-ca

You can customise generated cert by providing following params:

Param Description
-days The validity period of the certificate in days (default: 5 years)
-out The output directory (default: ./ca)

Example:

bash
./es-gencert-cli create-ca -out ./kurrentdb-ca

Generating the Node certificate

You need to generate certificates signed by the CA for each node. They should be installed only on the specific node machine.

By default, the tool will create the ca directory in the certs directory you created. Two keys will be generated:

  • node.crt - the public file that needs to be also used for the nodes and client configuration,
  • node.key - the private key file that should be used only in the node's configuration. Do not copy it to client environment.

To generate node certificate run command:

bash
./es-gencert-cli -help create_node

You can customise generated cert by providing following params:

Param Description
-ca-certificate The path to the CA certificate file (default: ./ca/ca.crt)
-ca-key The path to the CA key file (default: ./ca/ca.key)
-days The output directory (default: ./nodeX where X is an auto-generated number)
-out The output directory (default: ./ca)
-ip-addresses Comma-separated list of IP addresses of the node
-dns-names Comma-separated list of DNS names of the node

::: warning
While generating the certificate, you need to remember to pass internal end external:

  • IP addresses to -ip-addresses: e.g. 127.0.0.1,172.20.240.1 and/or
  • DNS names to -dns-names: e.g. localhost,node1.kurrentdb
    that will match the URLs that you will be accessing KurrentDB nodes.
    :::

Sample:

text
./es-gencert-cli-cli create-node \
    -ca-certificate ./kurrentdb-ca/ca.crt \
    -ca-key ./kurrentdb-ca/ca.key \
    -out ./node1 \
    -ip-addresses 127.0.0.1,172.20.240.1 \
    -dns-names localhost,node1.kurrentdb

Running with Docker

You could also run the tool using Docker interactive container:

bash
docker run --rm -i eventstore/es-gencert-cli <command> <options>

One useful scenario is to use the Docker Compose file tool to generate all the necessary certificates before starting cluster nodes.

Sample:

yaml
version: "3.5"

services:
  setup:
    image: eventstore/es-gencert-cli:1.0.2
    entrypoint: bash
    user: "1000:1000"
    command: >
      -c "mkdir -p ./certs && cd /certs
      && es-gencert-cli create-ca
      && es-gencert-cli create-node -out ./node1 -ip-addresses 127.0.0.1,172.20.240.1 -dns-names localhost,node1.kurrentdb
      && es-gencert-cli create-node -out ./node1 -ip-addresses 127.0.0.1,172.20.240.2 -dns-names localhost,node2.kurrentdb
      && es-gencert-cli create-node -out ./node1 -ip-addresses 127.0.0.1,172.20.240.3 -dns-names localhost,node3.kurrentdb
      && find . -type f -print0 | xargs -0 chmod 666"
    container_name: setup
    volumes:
      - ./certs:/certs

See more in the complete sample of docker-compose secured cluster configuration.

Certificate installation on a client environment

To connect to KurrentDB, you need to install the auto-generated CA certificate file on the client machine (e.g. machine where the client is hosted, or your dev environment).

::: tabs#os
@tab Linux

  1. Copy auto-generated CA file to dir /usr/local/share/ca-certificates/, e.g. using command:
bash
sudo cp ca.crt /usr/local/share/ca-certificates/event_store_ca.crt
  1. Update the CA store:
bash
sudo update-ca-certificates

@tab Windows

  1. You can manually import it to the local CA cert store through Certificates Local Machine Management Console. To do that select Run from the Start menu, and then enter certmgr.msc. Then import certificate to Trusted Root Certification.
  2. You can also run the PowerShell script instead:
powershell
Import-Certificate -FilePath ".\certs\ca\ca.crt" -CertStoreLocation Cert:\CurrentUser\Root

@tab macOS

  1. In the Keychain Access app on your Mac, select either the login or System keychain. Drag the certificate file onto the Keychain Access app. If you're asked to provide a name and password, type the name and password for an administrator user on this computer.
  2. You can also run the bash script:
bash
sudo security add-certificates -k /Library/Keychains/System.keychain ca.crt

:::

Intermediate CA certificates

Intermediate CA certificates are supported by loading them from a PEM or PKCS #12 bundle specified by the CertificateFile configuration parameter. To make sure that the configuration is correct, the certificate chain is validated on startup with the node's own certificate.

If you've used the certificate generation tool with the default settings to generate your CA and node certificates, then you're not using intermediate CA certificates.

However, if you're using a public certificate authority (e.g Let's Encrypt) to generate your node certificates there is a chance that you're using intermediate CA certificates without knowing. This is due to the Authority Information Access (AIA) extension which allows intermediate certificates to be fetched from a remote server.

To verify if your certificate is using the AIA extension, you need to verify if there is a section named: Authority Information Access in the certificate.

::: tabs#os
@tab Linux

Use openssl to find the section in the certificate file:

bash
openssl x509 -in /path/to/node.crt -text | grep 'Authority Information Access' -A 1

@tab Windows
Open the certificate, go to the Details tab and look for the Authority Information Access field.

If the extension is present, you can manually download the intermediate certificate from the URL present under the CA Issuers entry.
Note that you will usually need to convert the downloaded certificate from the DER to the PEM format.

This can be done with the following openssl command:

powershell
openssl x509 -inform der -in /path/to/cert.der > /path/to/cert.pem

or with an online service if you don't have openssl installed.
:::

It's possible that there are more than one intermediate CA certificates in the chain - so you need to verify if the certificate you've just downloaded also uses the AIA extension. If yes, you need to download the next intermediate CA certificate in the chain by repeating the same process above until you eventually reach a publicly trusted root certificate (i.e. the Subject and Issuer fields will match). In practice, there'll usually be at most two intermediate certificates in the chain.

Bundling the intermediate certificates

The node's certificate should be first in the bundle, followed by the intermediates. Intermediates can be in any order but it would be good to keep it from leaf to root, as per the usual convention. The root certificate should not be bundled.

In the examples below, intermediate certificates are numbered from 1 to N starting from the leaf and going up.

PEM format

If your node's certificate and the intermediate CA certificates are both PEM formatted, that is they begin with -----BEGIN CERTIFICATE----- and end with -----END CERTIFICATE----- then you can simply append the contents of the intermediate certificate files to the end of the node's certificate file to create the bundle.

::: tabs#os
@tab Linux

bash
cat /path/to/intermediate1.crt >> /path/to/node.crt
...
cat /path/to/intermediateN.crt >> /path/to/node.crt

@tab Windows

powershell
type C:\path\to\intermediate1.crt >> C:\path\to\node.crt
...
type C:\path\to\intermediateN.crt >> C:\path\to\node.crt

:::

PKCS #12 format

If you want to generate a PKCS #12 bundle from PEM formatted certificate files, please follow the steps below.

bash
cat /path/to/intermediate1.crt >> ./ca_bundle.crt
...
cat /path/to/intermediateN.crt >> ./ca_bundle.crt

openssl pkcs12 -export -in /path/to/node.crt -inkey /path/to/node.key -certfile ./ca_bundle.crt -out /path/to/node.p12 -passout pass:

Adding intermediate certificates to the certificate store

Intermediate certificates also need to be added to the current user's certificate store.

This is required for two reasons:
i) For the full certificate chain to be sent when TLS connections are established
ii) To improve performance by preventing certificate downloads if your certificate uses the AIA extension

::: tabs#os
@tab Linux
The following script assumes KurrentDB is running under the kurrent account.

bash
sudo su kurrent --shell /bin/bash
dotnet tool install --global dotnet-certificate-tool
 ~/.dotnet/tools/certificate-tool add -s CertificateAuthority -l CurrentUser --file /path/to/intermediate.crt

@tab Windows
To import the intermediate certificate in the Intermediate Certification Authorities certificate store, run the following PowerShell command under the same account as KurrentDB is running:

powershell
Import-Certificate -FilePath .\path\to\intermediate.crt -CertStoreLocation Cert:\CurrentUser\CA

Optionally, to import the intermediate certificate in the Local Computer store, run the following as Administrator:

powershell
Import-Certificate -FilePath .\ca.crt -CertStoreLocation Cert:\LocalMachine\CA

:::


File: docs/server/security/security-options.md


title: Security options order: 1

Security

For production use, it is important to configure KurrentDB security features to prevent unauthorised access
to your data.

Security features of KurrentDB include:

Configuration options

Below you can find more details about each of the available security options.

Running without security

KurrentDB is secure by default. It means that you have to supply valid certificates and configuration for the database node to work.

TLS, authentication, and authorization can all be disabled by setting the Insecure option:

Format Syntax
Command line --insecure
YAML Insecure
Environment variable KURRENTDB_INSECURE

Default: false

::: warning
When running with protocol security disabled, everything is sent unencrypted over the wire. KurrentDB does not use any authentication or authorization when running insecure.
:::

Running without TLS

If you want to disable transport security but keep authentication and authorization active, for example to run a dev/test cluster without generating certificates, or to deploy on an internal trusted network, use the DisableTls option.

Unlike Insecure, DisableTls does not disable authentication or authorization. Users and nodes still need valid credentials, and authorization policies still apply.

Format Syntax
Command line --disable-tls
YAML DisableTls
Environment variable KURRENTDB_DISABLE_TLS

Default: false

::: warning
When TLS is disabled, all traffic, including authentication credentials, is transmitted in cleartext. Do not enable DisableTls on networks where untrusted parties can observe traffic.
:::

Inter-node authentication: `ClusterSecret`

Nodes normally authenticate inter-node traffic (gossip, elections, replication, and internal administrative calls) using their TLS certificates. When TLS is disabled there are no client certificates to inspect, so you must configure a shared cluster secret that nodes can use instead.

Format Syntax
Command line --cluster-secret <value>
YAML ClusterSecret: <value>
Environment variable KURRENTDB_CLUSTER_SECRET

Default: "" (empty)

In a cluster, the same value must be set on every node. KurrentDB will refuse to start with DisableTls enabled if ClusterSecret is empty.

ClusterSecret has no effect when TLS is enabled: nodes authenticate by mutual TLS in that case.

Development mode

For development purposes, the Dev option starts a single node with TLS, authentication and authorization, but without needing to be provided with certificates. KurrentDB generates a self-signed certificate valid for one month and (on Windows) adds the certificate to the trusted root store automatically.

Dev mode also enables AtomPub over HTTP, and enables system projections.

Format Syntax
Command line --dev
YAML Dev
Environment variable KURRENTDB_DEV

Default: false

bash
kurrentdb --dev

::: warning
Dev mode is not for production use.
:::

Persisting the dev certificate

By default the dev certificate is stored in the user certificate store. In containers or hardened environments where the home directory is not writable, KurrentDB falls back to an ephemeral in-memory key โ€” a fresh certificate is generated on every startup, which breaks any client that has pinned the previous certificate.

Use the DevCertPath option to persist the certificate to a file:

Format Syntax
Command line --dev-cert-path
YAML DevCertPath
Environment variable KURRENTDB_DEV_CERT_PATH
bash
kurrentdb --dev --dev-cert-path /var/lib/kurrentdb/certs/dev.pfx

On first start, KurrentDB generates the certificate, writes it as a PFX at the given path, and writes a PEM-encoded public certificate alongside it with a .crt extension (in the example above, /var/lib/kurrentdb/certs/dev.crt). Distribute the .crt file to clients for trust configuration. On subsequent starts, the existing certificate is loaded from the PFX; if it is expired or invalid, a new one is generated and saved over it.

When running in a container, mount a volume at the certificate directory so the certificate survives container restarts:

::: tabs
@tab kurrent-latest

bash
docker run --name kurrentdb-dev -it -p 2113:2113 \
    -v $(pwd)/dev-certs:/var/lib/kurrentdb/certs \
    docker.kurrent.io/kurrent-latest/kurrentdb:latest \
    --dev --dev-cert-path /var/lib/kurrentdb/certs/dev.pfx

@tab kurrent-lts

bash
docker run --name kurrentdb-dev -it -p 2113:2113 \
    -v $(pwd)/dev-certs:/var/lib/kurrentdb/certs \
    docker.kurrent.io/kurrent-lts/kurrentdb:latest \
    --dev --dev-cert-path /var/lib/kurrentdb/certs/dev.pfx

:::

Removing dev certificates

To remove dev certificates KurrentDB has installed in the user certificate store without starting the server:

Format Syntax
Command line --remove-dev-certs
YAML RemoveDevCerts
Environment variable KURRENTDB_REMOVE_DEV_CERTS
bash
kurrentdb --remove-dev-certs

This does not delete certificates written to a --dev-cert-path file - remove those manually.

Set initial passwords

You can set the admin and ops passwords on the first run of the database. This will not impact the existing credentials, the user can log into their accounts with existing passwords. Additionally, this will only take effect if you are using basic authentication.

Set the password for the admin user with the DefaultAdminPassword option:

Format Syntax
Environment variable KURRENTDB_DEFAULT_ADMIN_PASSWORD

Default: changeit

And set the password for the ops user with the DefaultOpsPassword option:

Format Syntax
Environment variable KURRENTDB_DEFAULT_OPS_PASSWORD

Default: changeit

::: warning
Due to security reasons the DefaultAdminPassword and DefaultOpsPassword options can only be set through environment variables. The user will receive the error message if they try to pass the options using command line or config file.
:::

Anonymous access to streams

Historically, anonymous users with network access have been allowed to read/write streams that do not have access control lists.

This is now disabled by default but can be enabled by setting AllowAnonymousStreamAccess to true.

Format Syntax
Command line --allow-anonymous-stream-access
YAML AllowAnonymousStreamAccess
Environment variable KURRENTDB_ALLOW_ANONYMOUS_STREAM_ACCESS

Default: false

Anonymous access to endpoints

Similarly to streams above, anonymous access has historically been available to some http endpoints.

Anonymous access to /gossip, /stats and the HTTP OPTIONS method can now be configured with the following two options. By default /gossip is still accessible anonymously but the others are not. Some clients currently rely on anonymous access to /gossip. This will likely change in the future.

The AllowAnonymousEndpointAccess option controls anonymous access to these endpoints. Setting OverrideAnonymousEndpointAccessForGossip to true allows anonymous access to /gossip specifically, overriding the other option.

Format Syntax
Command line --allow-anonymous-endpoint-access
YAML AllowAnonymousEndpointAccess
Environment variable KURRENTDB_ALLOW_ANONYMOUS_ENDPOINT_ACCESS

Default: false

Format Syntax
Command line --override-anonymous-endpoint-access-for-gossip
YAML OverrideAnonymousEndpointAccessForGossip
Environment variable KURRENTDB_OVERRIDE_ANONYMOUS_ENDPOINT_ACCESS_FOR_GOSSIP

Default: true

::: tip
Anonymous access is still always granted to /ping, /info, the static content of the UI, and http redirects.
:::

FIPS 140-2

KurrentDB runs on FIPS 140-2 enabled operating systems.

The Federal Information Processing Standards (FIPS) of the United States are a set of publicly announced standards that the National Institute of Standards and Technology (NIST) has developed for use in computer systems of non-military United States government agencies and contractors.

The 140 series of FIPS (FIPS 140) are U.S. government computer security standards that specify requirements for cryptography modules.

To run KurrentDB on FIPS 140-2 enabled operating systems, the following is required:

  1. The node's certificates & keys must be FIPS 140-2 compatible
    • Our certificate generation tool, es-gencert-cli, generates FIPS 140-2 compatible certificates & keys as from version 1.2.0 (only the linux build).
    • If you want to manually generate your certificates or keys with openssl, you must use openssl 3 or later.
    • If you want to use PKCS #12 bundles (.p12/.pfx extension), you must use a FIPS 140-2 compatible encryption algorithm (e.g AES-256) & hash algorithm (e.g SHA-256) to encrypt the bundle's contents.

Note that KurrentDB will also likely run properly on FIPS 140-3 compliant operating systems with the above steps but this cannot reliably be confirmed at the moment as FIPS 140-3 certification is still ongoing for the operating systems themselves.

Encryption-At-Rest

The Encryption-At-Rest feature allows users to encrypt their KurrentDB database. Currently, only chunk files are encrypted - the indexes are not. The primary objective is to protect against an attacker who obtains access to the physical disk. In contrast to volume or filesystem encryption, file level encryption provides some degree of protection for attacks against the live system or remote exploits as the plaintext data is not directly readable. Protecting against memory-dump based attacks is out of the scope of this feature.

You require a license key to use this feature.

Encryption Algorithm

Data is encrypted using a symmetric-key encryption algorithm.

The feature is designed to support different encryption algorithms. Currently, the following encryption algorithms are implemented:

  • AesGcm - Advanced Encryption Standard (AES) Galois Counter Mode (GCM)
    • The default key size is 256 bits, but 128-bit and 192-bit keys are also supported.

Master Key

The master key is the topmost key in the key hierarchy.

The size of the master key is not defined but it needs to be long enough (e.g at least 128 bits) to provide reasonable security.

Using a key-derivation function (HKDF in our case), the master key is used to derive multiple data keys. Each data key is used to encrypt a single chunk file. If the master key is compromised, the whole database can be decrypted. However, if a data key is compromised, only one chunk file can be decrypted.

Usually, only one master key should be necessary. However, to cater for scenarios where a master key is compromised, the feature supports loading multiple master keys. The latest master key is actively used to encrypt/decrypt new chunks, while old master keys are used only to decrypt old chunks - thus the new data is secure.

Master Key Source

The function of a master key source is to load master keys.

Master keys are loaded from the source only once at process startup. They are then kept in memory throughout the lifetime of the process.

The feature is designed to support different master key sources. Currently, the following master key sources are implemented:

  • File - loads master keys from a directory
    • The master key can be generated using es-cli with the command: es-cli encryption generate-master-key
    • For proper security, the master key files must be located on a drive other than where the database files are.
    • This master key source is not recommended to be used in production as it provides minimal protection.

You can contact Kurrent if you want us to support a particular master key source.

Configuration

Refer to the configuration guide for configuration mechanisms other than YAML.

Sample configuration:

yaml
Transform: aes-gcm

EncryptionAtRest:
  Enabled: true
  MasterKey:
    File:
      KeyPath: /path/to/keys/
  Encryption:
    AesGcm:
      Enabled: true
      KeySize: 256 # optional. supported key sizes: 128, 192, 256 (default)

When using the File master key source, a master key must be generated using the es-genkey-cli tool and placed in the configured KeyPath directory. The master key having the highest ID will be chosen as the active one.

Once the feature is enabled, the server should log messages similar to the following:

text
...
[141828, 1,11:42:45.325,INF] Encryption-At-Rest: Loaded master key source: "File"
[141828, 1,11:42:45.340,INF] Encryption-At-Rest: (File) Loaded master key: 2 (256 bits)
[141828, 1,11:42:45.340,INF] Encryption-At-Rest: (File) Loaded master key: 1 (256 bits)
[141828, 1,11:42:45.345,INF] Encryption-At-Rest: Active master key ID: 2
[141828, 1,11:42:45.345,INF] Encryption-At-Rest: Loaded encryption algorithm: "AesGcm"
[141828, 1,11:42:45.347,INF] Encryption-At-Rest: (AesGcm) Using key size: 256 bits
[141828, 1,11:42:45.401,INF] Loaded the following transforms: Identity, Encryption_AesGcm
[141828, 1,11:42:45.402,INF] Active transform set to: Encryption_AesGcm
...

New and scavenged chunks will be encrypted.

Compatibility

When encryption is enabled, it's no longer possible to revert back to an unencrypted database if:

  • at least one new chunk file has been created or
  • at least one chunk has been scavenged

The encrypted chunks would first need to be decrypted before a user can revert to the identity transform. Special tooling or endpoints are not currently available to do this.

Tutorial

Learn how to set up and use Encryption-At-Rest in KurrentDB through a tutorial.


File: docs/server/security/user-authentication.md


title: User authentication order: 3

Authentication

KurrentDB provides the following means to authenticate users connecting to the database:

  • Basic authentication is enabled by default, and allows you to authenticate users with a username and password. Users are created and managed in KurrentDB.
  • User X.509 certificates to authenticate users with an X.509 certificate. This is in addition to basic authentication.
  • LDAP authentication to authenticate users against an LDAP server such as OpenLDAP or Acive Directory.
  • OAuth authentication to authenticate users against an identity provider such as Auth0 or Identity Server

Once a user has been authenticated, they must be authorized before they can perform actions on the database.

Basic authentication

KurrentDB supports authentication based on usernames and passwords out of the box.

These users are created and managed within KurrentDB. So if you back up and restore a database with configured users, the same users will exist on the restored node.

Default users

KurrentDB provides two default users, $ops and $admin.

$admin has full access to everything in KurrentDB. It can read and write to protected streams, which is any stream that starts with $, such as $projections-$all or $users. Protected streams are usually system streams, for example, $projections-$all manages which projections exist in KurrentDB.

The $admin user can also run operational commands, such as scavenges and shutdowns on KurrentDB.

An $ops user can do everything that an $admin can do except manage users and read from system streams.

New users

New users can be created through the following means:

Users are created with a username, password, and optional set of groups or roles. These roles are used for authorization.

::: tip
Users managed in KurrentDB are always granted a role that matches their username.
:::

Externalised authentication

You can also use the trusted intermediary header for externalized authentication that allows you to integrate
almost any authentication system with KurrentDB.

The trusted intermediary header helps KurrentDB to support a common security architecture. There are
thousands of possible methods for handling authentication and it is impossible for us to support them all. The
header allows you to configure a trusted intermediary to handle the authentication instead of KurrentDB.

A sample configuration is to enable OAuth2 with the following steps:

  • Configure KurrentDB to run on the local loopback.
  • Configure nginx to handle OAuth2 authentication.
  • After authenticating the user, nginx rewrites the request and forwards it to the loopback to KurrentDB
    that serves the request.

The header has the form of {user}; group, group1 and the KurrentDB ACLs use the information to handle
security.

http:no-line-numbers
Kurrent-TrustedAuth: "root; admin, other"

Use the following option to enable this feature:

Format Syntax
Command line --enable-trusted-auth
YAML EnableTrustedAuth
Environment variable KURRENTDB_ENABLE_TRUSTED_AUTH

User X.509 Certificates

The User Certificates feature allows authentication through an X.509 user certificate in addition to username and password. User certificates work across any cluster that shares a trusted root Certificate Authority (CA) with the user's certificate. This means that you can have a single user certificate that is valid across multiple clusters.

Configuration steps

You require a license key to use this feature.

Refer to the configuration guide for configuration mechanisms other than YAML.

Sample configuration:

yaml
UserCertificates:
  Enabled: true

If the feature is enabled, the server will log the following on startup:

text
UserCertificatesPlugin: user X.509 certificate authentication is enabled

Connecting with a user certificate

Use the following command as an example to connect using curl:

bash
curl -i https://localhost:2113/streams/%24all --cert user-admin.crt --key user-admin.key

For using X.509 user certificate with KurrentDB client from an application, refer to the client's documentation.

Creating a user certificate

The user certificate must adhere to the following requirements:

  • The certificate has a root CA in common with the node certificate.
  • The root CA that they have in common is trusted by the node.
  • The certificate has the ClientAuth EKU, and not the ServerAuth EKU.
  • The certificate must be in date.
  • The CN is the username of a user that exists in the database.

::: tip
Certificates can also authenticate admin and ops users.
:::

You can generate a user certificate with the es-gencert-cli:

For example, to generate a user certificate with the username ouro:

bash
./es-gencert-cli.exe create-user -username ouro -ca-certificate ca.crt -ca-key ca.key -days 10

::: details Click to view a sample user certificate

text
/* Detailed source-code truncated for AI context efficiency. */

:::

Limitations

  1. Requests authenticated with user certificates cannot be forwarded between nodes. This affects Writes, Persistent Subscription operations, and Projections operations. These requests will need to be performed on the Leader node only.

  2. The X.509 authentication cannot be used in conjunction with any of the other authentication plugins, such as the LDAP plugin.

Troubleshooting

Error Solution
Feature not enabled The feature has to be enabled in order to authenticate user requests. The following log indicates that the feature was not enabled: UserCertificatesPlugin is not enabled.
Feature enabled and user not authenticated If the feature has been enabled but there are still access denied errors, check the following:
  • The user exists and is enabled in the KurrentDB database. Can you log in with the username and password?
  • The user certificate is valid, and has a valid chain up to a trusted root CA.
  • The user certificate and node certificate share a common root CA.
  • Use 'requires leader' (which is the default) in your client configuration to rule out issues with forwarding requests.
  • LDAP authentication

    The LDAP Authentication feature enables KurrentDB to use LDAP-based directory services for authentication.

    Configuration

    You require a license key to use this feature.

    Refer to the configuration guide for configuration mechanisms other than YAML.

    1. Change the authentication type to ldaps.
    2. Add LDAP-specific settings in a section named LdapsAuth, either inline or in a separate file.

    :::tabs
    @tab kurrentdb.conf

    yaml
    AuthenticationType: ldaps
    LogFailedAuthenticationAttempts: true # for testing
    LdapsAuth:
      Host: 13.88.9.49
      Port: 636 #to use plaintext protocol, set Port to 389 and UseSSL to false
      UseSSL: true
      ValidateServerCertificate: false #set this to true to validate the certificate chain
      AnonymousBind: false
      BindUser: cn=binduser,dc=mycompany,dc=local
      BindPassword: p@ssw0rd!
      BaseDn: ou=Lab,dc=mycompany,dc=local
      ObjectClass: organizationalPerson
      Filter: sAMAccountName
      RequireGroupMembership: false #set this to true to allow authentication only if the user is a member of the group specified by RequiredGroupDn
      GroupMembershipAttribute: memberOf
      RequiredGroupDn: cn=Kurrent-Users,dc=mycompany,dc=local
      PrincipalCacheDurationSec: 60
      LdapGroupRoles:
          'cn=Kurrent-Accounting,ou=Staff,dc=mycompany,dc=local': accounting
          'cn=Kurrent-Operations,ou=Staff,dc=mycompany,dc=local': it
          'cn=Kurrent-Admins,ou=Staff,dc=mycompany,dc=local': '$admins'

    :::

    Alternatively you can provide a path to a separate config file with the AuthenticationConfig option. For example:

    :::tabs
    @tab kurrentdb.conf

    yaml
    AuthenticationType: ldaps
    LogFailedAuthenticationAttempts: true # for testing
    AuthenticationConfig: ./kurrentdb-ldaps.conf

    @tab kurrentdb-ldaps.conf

    yaml
    LdapsAuth:
      Host: 13.88.9.49
      Port: 636 #to use plaintext protocol, set Port to 389 and UseSSL to false
      UseSSL: true
      ValidateServerCertificate: false #set this to true to validate the certificate chain
      AnonymousBind: false
      BindUser: cn=binduser,dc=mycompany,dc=local
      BindPassword: p@ssw0rd!
      BaseDn: ou=Lab,dc=mycompany,dc=local
      ObjectClass: organizationalPerson
      Filter: sAMAccountName
      RequireGroupMembership: false #set this to true to allow authentication only if the user is a member of the group specified by RequiredGroupDn
      GroupMembershipAttribute: memberOf
      RequiredGroupDn: cn=Kurrent-Users,dc=mycompany,dc=local
      PrincipalCacheDurationSec: 60
      LdapGroupRoles:
          'cn=Kurrent-Accounting,ou=Staff,dc=mycompany,dc=local': accounting
          'cn=Kurrent-Operations,ou=Staff,dc=mycompany,dc=local': it
          'cn=Kurrent-Admins,ou=Staff,dc=mycompany,dc=local': '$admins'

    :::

    Upon successful LDAP authentication, users are assigned roles based on their domain group memberships, as specified in the LdapGroupRoles section.

    Troubleshooting

    If you encounter issues, check the server's log. Common problems include:

    Error Solution
    Invalid Bind Credentials Specified Confirm the BindUser and BindPassword.
    Exception During Search - 'No such Object' or 'The object does not exist' Verify the BaseDn.
    'Server Certificate Error' or 'Connect Error - The authentication or decryption has failed' Verify that the server certificate is valid. If it is a self-signed certificate, set ValidateServerCertificate to false.
    LDAP Server Unavailable Verify connectivity to the LDAP server from a KurrentDB node (e.g. using netcat or telnet). Verify the Host and Port parameters.
    Error Authenticating with LDAP server Verify the ObjectClass and Filter parameters. If you have set RequireGroupMembership to true, verify that the user is part of the group specified by RequiredGroupDn and that the LDAP record has GroupMembershipAttribute set to memberOf.
    No Errors in Server Logs But Cannot Login
  • Verify that the user is part of the group specified by RequiredGroupDn and that the LDAP record has GroupMembershipAttribute set to memberOf.
  • Verify the ObjectClass and Filter parameters.
  • If you have set RequireGroupMembership to true, verify that the user is part of the group specified by RequiredGroupDn and that the LDAP record has GroupMembershipAttribute set to memberOf.
  • OAuth Authentication

    The OAuth feature allows KurrentDB to connect to an identity server and authenticate users based on a JWT rather than username and password.

    Access tokens can contain a "role" claim, which will be used for authorization.

    ::: tip
    With the default basic authentication, KurrentDB treats the username as a "role" in addition to the groups they are in. If you want to have the same behaviour with OAuth authentication, you will need to add the user's username as an additional role claim.
    :::

    Configuration

    You require a license key to use this feature.

    Refer to the configuration guide for configuration mechanisms other than YAML.

    You can enable the OAuth plugin by setting the following options in your KurrentDB config file:

    • Set the AuthenticationType to oauth
    • Provide oauth configuration in a section named OAuth

    For example:

    :::tabs
    @tab kurrentdb.conf

    yaml
    AuthenticationType: oauth
    LogFailedAuthenticationAttempts: true # for testing
    OAuth:
      Audience: {audience}
      Issuer: {identity_server_endpoint}
      ClientId: {client_id}
      ClientSecret: {client_secret}

    :::

    Alternatively you can provide a path to a separate config file with the AuthenticationConfig option. For example:

    :::tabs
    @tab kurrentdb.conf

    yaml
    AuthenticationType: oauth
    LogFailedAuthenticationAttempts: true # for testing
    AuthenticationConfig: ./kurrentdb-oauth.conf

    @tab kurrentdb-oauth.conf

    yaml
    OAuth:
      Audience: {audience}
      Issuer: {identity_server_endpoint}
      ClientId: {client_id}
      ClientSecret: {client_secret}

    :::

    The configuration options are:

    Name Type Required? Default Description
    Audience string Y - The audience used in your identity provider.
    Issuer string Y - The issuer endpoint for your identity provider.
    ClientId string N "" The id of the client configured in the identity provider.
    ClientSecret string N "" The client secret configured in the identity provider.
    DisableIssuerValidation bool N false Disable issuer validation for testing purposes.
    DisableCodeChallengeMethodsSupportedValidation bool N false Disable validation of the code_challenge_methods_supported field in the identity provider's discovery document. Enable this when using an identity provider such as Microsoft Entra that supports PKCE but does not advertise it in the discovery document.
    Insecure bool N false Whether to validate the certificates for the identity provider. This is not related to Insecure in the KurrentDB configuration.

    Testing with a local identity server

    You can try out the OAuth feature locally with this Identity Server 4 docker container. This container is not intended to be used in production.

    You will first configure and run the identity server, and then configure and run KurrentDB.

    1. Create a users.conf.json file to configure the users for your test.

    ::: details users.conf.json

    json
    [
      {
        "subjectId": "1",
        "username": "admin",
        "password": "password",
        "claims": [{
            "type": "role",
            "value": "$admins"
          }, {
            "type": "role",
            "value": "$ops"
          }]
      }, {
        "subjectId": "2",
        "username": "operator",
        "password": "password",
        "claims": [{
          "type": "role",
          "value": "$ops"
        }]
      }, {
        "subjectId": "3",
        "username": "ouro",
        "password": "password",
        "claims": [{
          "type": "role",
          "value": "custom"
        }]
      }, {
        "subjectId": "4",
        "username": "user",
        "password": "password",
        "claims": []
      }
    ]

    :::

    This creates the following users:

    Username Password Roles
    admin password $admins, $ops
    operator password $ops
    ouro password custom
    user password None
    1. Create an identity server config file, idsrv4.conf.json.

    You need to configure the following:

    • A role claim (http://schemas.microsoft.com/ws/2008/06/identity/claims/role) must be added to the token.
    • The grant types of password and authorization_code must be allowed.
    • A redirect uri of /signin-oidc must be allowed for the embedded web UI to function.

    ::: details idsrv4.conf.json

    json
    {
    	"IdentityResources": [
    		{
    			"Name": "openid",
    			"DisplayName": "Your user identifier",
    			"Required": true,
    			"UserClaims": [
    				"sub",
    				"role"
    			]
    		},
    		{
    			"Name": "profile",
    			"DisplayName": "User profile",
    			"Description": "Your user profile information (first name, last name, etc.)",
    			"Emphasize": true,
    			"UserClaims": [
    				"name",
    				"given_name",
    				"middle_name",
    			]
    		}
    	],
    	"ApiResources": [
    		{
    			"Name": "kurrentdb-client",
    			"Scopes": [
    				"streams",
    				"openid",
    				"profile"
    			]
    		}
    	],
    	"ApiScopes": [
    		{
    			"Name": "streams",
    			"UserClaims": [
    				"http://schemas.microsoft.com/ws/2008/06/identity/claims/role",
    				"role"
    			]
    		}
    	],
    	"Clients": [
    		{
    			"ClientId": "kurrentdb-client",
    			"AllowedGrantTypes": [
    				"password",
    				"authorization_code",
                    "client_credentials"
    			],
    			"ClientSecrets": [
    				{
    					"Value": "{client_secret}"
    				}
    			],
    			"AllowedScopes": [
    				"streams",
    				"openid",
    				"profile"
    			],
    			"RedirectUris": ["https://localhost:2113/signin-oidc","https://127.0.0.1:2113/signin-oidc","https://localhost:2113/oauth/callback","https://127.0.0.1:2113/oauth/callback"],
    			"AlwaysIncludeUserClaimsInIdToken": true,
    			"RequireConsent": false,
    			"AlwaysSendClientClaims": true,
    			"AllowOfflineAccess": true,
    			"RequireClientSecret": false,
    			"AllowAccessTokensViaBrowser": true
    		}
    	]
    }

    :::

    ::: tip
    In this example we set the RedirectUris to both https://localhost:2113 and https://127.0.0.1:2113 so that you can log into the UI from either address.
    :::

    1. Pull and run the kurrent-io/idsrv4 docker container:
    bash
    docker pull ghcr.io/kurrent-io/idsrv4/idsrv4
    
    docker run \
        --rm -it \
        -p 5000:5000 \                                            # HTTP port
        -p 5001:5001 \                                            # HTTPS port
        --volume $PWD/users.conf.json:/etc/idsrv4/users.conf \    # mount users file; required
        --volume $PWD/idsrv4.conf.json:/etc/idsrv4/idsrv4.conf \  # mount configuration file
        ghcr.io/kurrent-io/idsrv4/idsrv4

    You should see the identity server start up and start listening on the ports 5000 and 5001:

    text
    info: Microsoft.Hosting.Lifetime[14]
          Now listening on: https://[::]:5000
    info: Microsoft.Hosting.Lifetime[14]
          Now listening on: https://[::]:5001
    1. Add the following configuration to your KurrentDB config file kurrentdb.conf:

    :::tabs
    @tab kurrentdb.conf

    yaml
    IntIp: 127.0.0.1
    ExtIp: 127.0.0.1
    
    # Licensing and certificates configuration omitted
    
    LogFailedAuthenticationAttempts: true # for testing
    AuthenticationType: oauth
    OAuth:
      Audience: kurrentdb-client
      Issuer: https://localhost:5001/
      ClientId: kurrentdb-client
      ClientSecret: {client_secret}
      # For testing
      DisableIssuerValidation: true
      Insecure: true

    :::

    1. Start KurrentDB. You should see the following logs indicating that OAuth is in use:
    text
    [INF] OAuthAuthenticationProvider    OAuthAuthentication 25.0.0.1673 plugin enabled.
    ...
    [INF] OAuthAuthenticationPlugin      Obtaining auth token signing key from https://localhost:5001/
    ...
    [INF] OAuthAuthenticationPlugin      Issuer signing keys have been retrieved. Key IDs: ["{key_id}"]

    You should now be able to log into the UI with the configured user names and passwords.


    File: docs/server/security/user-authorization.md


    title: User authorization order: 4

    Authorization

    Authorization governs what an authenticated user can access and what actions they can perform based on that user's roles.

    Roles

    Users are granted roles by the authentication mechanism. These roles determine what operations, endpoints, and streams a user can access.

    KurrentDB provides the $admins and $ops roles by default, granted respectively to the admin and ops default users. Custom roles can also be created.

    Admins role

    The $admins role is granted to the admin user by default and authorizes the user to do the following:

    • Read all streams, including $all and system streams.
    • Manage users and reset their passwords.
    • Perform all administrative operations on a node or cluster, such as scavenges, restarts, and triggering leader elections.
    • Manage projections and persistent subscriptions.
    • View statistics for the node, subscriptions, or projections

    You can create additional admin users by assigning the $admins group to a user.

    ::: note
    The $admins role bypasses all stream access checks such as ACLs or stream policies.
    :::

    Ops role

    The $ops role is granted to the ops user by default. This role grants similar permissions to the $admins role, but without the user management and stream access.

    The $ops role authorizes the user to do the following:

    • Perform maintenance operations on a node or cluster, such as scavenges, restarts, and triggering leader elections.
    • Manage projections and persistent subscriptions.
    • View statistics for the node, subscriptions, or projections
    • Stream access is determined by the authorization mechanism used

    You can create additional ops users by assigning the $ops group to a user.

    Custom roles

    Custom roles are any role assigned to an authenticated user outside of the $admins and $ops roles.
    These can be groups assigned to the user, their username, or a mapping from the authentication mechanism used. These roles can be anything, but typically describe the group or tenant a user is part of. Examples of custom roles are accounting, sales, or tenant-123.

    Users with custom (or no) roles are authorized to do the following:

    ::: tip
    Users managed in KurrentDB are always granted a role that matches their username.
    :::

    Stream access

    Stream access covers what actions a user can perform on a stream within KurrentDB. These actions are:

    Name Key Description
    Read $r The permission to read from a stream.
    Write $w The permission to write to a stream.
    Delete $d The permission to delete a stream.
    Metadata read $mr The permission to read the metadata associated with a stream.
    Metadata write $mw The permission to write the metadata associated with a stream.

    KurrentDB supports these methods of authorizing user access to streams:

    Access control lists

    By default, authenticated users can access all non-system streams in the KurrentDB database. You can use Access Control Lists (ACLs) to set up more granular access control. In fact, the default access
    level is also controlled by a special ACL called the default ACL.

    Stream ACL

    KurrentDB keeps the ACL of a stream in the stream metadata as JSON with the below definition:

    json
    {
      "$acl": {
        "$w": "$admins",
        "$r": "$all",
        "$d": "$admins",
        "$mw": "$admins",
        "$mr": "$admins"
      }
    }

    These fields represent the following:

    • $w The permission to write to this stream.
    • $r The permission to read from this stream.
    • $d The permission to delete this stream.
    • $mw The permission to write the metadata associated with this stream.
    • $mr The permission to read the metadata associated with this stream.

    You can update these fields with either a single string or an array of strings representing users or
    groups ($admins, $all, or custom groups). It's possible to put an empty array into one of these fields,
    and this removes all users from that permission.

    ::: tip
    We recommend you don't give people access to $mw as then they can change the ACL.
    :::

    Default ACL

    The $settings stream uses a special ACL as the default ACL. This stream controls the default ACL for
    streams without an ACL and who can create streams in the system. The default state of these is
    shown below:

    json
    {
      "$userStreamAcl": {
        "$r": "$all",
        "$w": "$all",
        "$d": "$all",
        "$mr": "$all",
        "$mw": "$all"
      },
      "$systemStreamAcl": {
        "$r": "$admins",
        "$w": "$admins",
        "$d": "$admins",
        "$mr": "$admins",
        "$mw": "$admins"
      }
    }

    You can rewrite these to the $settings stream with the following request:

    @code{curl}

    The $userStreamAcl controls the default ACL for user streams, while all system streams use
    the $systemStreamAcl as the default.

    ::: tip
    Members of $admins always have access to everything. You cannot remove this permission.
    :::

    When you set a permission on a stream, it overrides the default values. However, it's not necessary to specify
    all permissions on a stream. It's only required to specify those which differ from the default.

    Here is an example of the default ACL that has been changed:

    json
    {
      "$userStreamAcl": {
        "$r": "$all",
        "$w": "ouro",
        "$d": "ouro",
        "$mr": "ouro",
        "$mw": "ouro"
      },
      "$systemStreamAcl": {
        "$r": "$admins",
        "$w": "$admins",
        "$d": "$admins",
        "$mr": "$admins",
        "$mw": "$admins"
      }
    }

    This default ACL gives ouro and $admins create and write permissions on all streams while everyone else
    can read from them. Be careful when allowing default access to system streams to non-admins, as they would also have
    access to $settings unless you specifically override it.

    Refer to the documentation of the HTTP API or SDK of your choice for more information about changing ACLs
    programmatically.

    Stream policy authorization

    This allows administrators to define stream access policies for KurrentDB based on stream prefixes.

    Enabling

    Using this feature requires a license key.

    The last event in the $authorization-policy-settings stream configures the stream access policy.

    Enable stream policies by appending an event of type $authorization-policy-changed to the $authorization-policy-settings stream:

    ::: tabs
    @tab HTTP

    http
    POST https://localhost:2113/streams/$authorization-policy-settings
    Authorization: Basic admin changeit
    Content-Type: application/json
    Kurrent-EventId: 11887e82-9fb4-4112-b937-aea895b32a4a
    Kurrent-EventType: $authorization-policy-changed
    
    {
        "streamAccessPolicyType": "streampolicy"
    }

    @tab Powershell

    powershell
    $JSON = @"
    {
        "streamAccessPolicyType": "streampolicy"
    }
    "@ `
    
    curl.exe -X POST `
      -H "Content-Type: application/json" `
      -H "Kurrent-EventId: 11887e82-9fb4-4112-b937-aea895b32a4b" `
      -H "Kurrent-EventType: `$authorization-policy-changed" `
      -u "admin:changeit" `
      -d $JSON `
      https://localhost:2113/streams/%24authorization-policy-settings

    @tab Bash

    bash
    JSON='{
        "streamAccessPolicyType": "streampolicy"
    }'
    
    curl -X POST \
      -H "Content-Type: application/json" \
      -H "Kurrent-EventId: 814f6d67-515c-4bd6-a6d6-8a0235ec719a" \
      -H "Kurrent-EventType: \$authorization-policy-changed" \
      -u "admin:changeit" \
      -d "$JSON" \
      https://localhost:2113/streams/%24authorization-policy-settings

    :::

    And disable stream policies by setting the stream access policy type back to acl:

    ::: tabs
    @tab HTTP

    http
    ### Configure cluster to use acls
    POST https://localhost:2113/streams/$authorization-policy-settings
    Authorization: Basic admin changeit
    Content-Type: application/json
    Kurrent-EventId: d07a6637-d9d0-43b7-8f48-6a9a8800af12
    Kurrent-EventType: $authorization-policy-changed
    
    {
        "streamAccessPolicyType": "acl"
    }

    @tab Powershell

    powershell
    $JSON = @"
    {
        "streamAccessPolicyType": "acl"
    }
    "@ `
    
    curl.exe -X POST `
      -H "Content-Type: application/json" `
      -H "Kurrent-EventId: 3762c89e-d12e-4a01-83bb-781459c93ebc" `
      -H "Kurrent-EventType: `$authorization-policy-changed" `
      -u "admin:changeit" `
      -d $JSON `
      https://localhost:2113/streams/%24authorization-policy-settings

    @tab Bash

    bash
    JSON='{
        "streamAccessPolicyType": "acl"
    }'
    
    curl -X POST \
      -H "Content-Type: application/json" \
      -H "Kurrent-EventId: d6b3a316-2493-4bcd-9019-e02b5ae4eec3" \
      -H "Kurrent-EventType: \$authorization-policy-changed" \
      -u "admin:changeit" \
      -d "$JSON" \
      https://localhost:2113/streams/%24authorization-policy-settings

    :::

    When stream policies are enabled, you should see the following logs:

    You can check that the feature is enabled by searching for the following log at startup:

    text
    [22860,28,17:30:42.247,INF] StreamBasedAuthorizationPolicyRegistry New Authorization Policy Settings event received
    [22860,28,17:30:42.249,INF] StreamBasedAuthorizationPolicyRegistry Starting factory streampolicy
    ...
    [22860,28,17:30:42.264,INF] StreamBasedPolicySelector      Existing policy found in $policies stream (0)
    [22860,28,17:30:42.269,INF] StreamBasedPolicySelector      Successfully applied policy
    [22860,28,17:30:42.271,INF] StreamBasedPolicySelector      Subscribing to $policies at 0

    Stream policies will log the following error and not start if you do not have a valid license. KurrentDB will continue to run with the current policy type:

    text
    [22928, 1,17:31:56.879,INF] StreamPolicySelector           Stream Policies plugin is not licensed, stream policy authorization may not be enabled.
    ...
    [22928,28,17:31:57.030,ERR] StreamPolicySelector           Stream Policies plugin is not licensed, cannot enable Stream policies
    [22928,28,17:31:57.030,ERR] StreamBasedAuthorizationPolicyRegistry Failed to enable policy selector plugin streampolicy. Authorization settings will not be applied

    ::: note
    If you enable the Stream Policy feature, KurrentDB will not enforce stream ACLs.
    :::

    Overriding the default

    If the $authorization-policy-settings stream is empty and there is no configuration, KurrentDB will default to using ACLs.

    If you would rather default to stream policies, you can set the Authorization:DefaultPolicyType option to streampolicy.

    ::: note
    Any events in the $authorization-policy-settings stream take precedence over the Authorization:DefaultPolicyType setting.
    :::

    You can do this by adding the following to the server configuration file:

    yaml
    Authorization:
      DefaultPolicyType: streampolicy

    Refer to the configuration guide for configuration mechanisms other than YAML.

    ::: warning
    If the policy type configured in Authorization:DefaultPolicyType is not present at startup, KurrentDB will not start.
    :::

    Fallback stream access policy

    If none of the events in the $authorization-policy-settings stream are valid, or if the stream policy plugin cannot be started (for example, due to it being unlicensed), KurrentDB will fall back to restricted access.

    This locks down all stream access to admins only to prevent KurrentDB from returning to a more permissive policy such as ACLs.

    To recover from this, either fix the issue preventing the plugin from loading or set the stream access policy type to a different policy such as ACL:

    json
    {
        "streamAccessPolicyType": "acl"
    }

    Stream policies and rules

    Stream policies and Stream Rules are configured by events written to the $policies stream.

    Default stream policy

    KurrentDB creates default stream policies by adding a default policy event in the $policies stream when the feature is enabled for the first time. The default policies specified in that event include:

    • Restricts system streams to the $admins group.
    • Grants users outside of the $ops group access to user streams.
    • Grants users outside of the $ops group read access to the default projection streams ($ce, $et, $bc, $category, $streams) and their metadata.

    KurrentDB will log when the default policy is created:

    text
    [31124,16,11:18:15.099,DBG] StreamBasedPolicySelector Successfully wrote default policy to stream $policies.

    ::: details Click here to see the default policy

    json
    {
      "streamPolicies": {
        "publicDefault": {
          "$r": ["$all"],
          "$w": ["$all"],
          "$d": ["$all"],
          "$mr": ["$all"],
          "$mw": ["$all"]
        },
        "adminsDefault": {
          "$r": ["$admins"],
          "$w": ["$admins"],
          "$d": ["$admins"],
          "$mr": ["$admins"],
          "$mw": ["$admins"]
        },
        "projectionsDefault": {
          "$r": ["$all"],
          "$w": ["$admins"],
          "$d": ["$admins"],
          "$mr": ["$all"],
          "$mw": ["$admins"]
        }
      },
      "streamRules": [
        {
          "startsWith": "$et-",
          "policy": "projectionsDefault"
        },
        {
          "startsWith": "$ce-",
          "policy": "projectionsDefault"
        },
        {
          "startsWith": "$bc-",
          "policy": "projectionsDefault"
        },
        {
          "startsWith": "$category-",
          "policy": "projectionsDefault"
        },
        {
          "startsWith": "$streams",
          "policy": "projectionsDefault"
        }
      ],
      "defaultStreamRules": {
        "userStreams": "publicDefault",
        "systemStreams": "adminsDefault"
      }
    }

    :::

    ::: note
    Operations users in the $ops group are excluded from the $all group and do not have access to user streams by default.
    :::

    Custom stream policies

    You can create a custom stream policy by writing an event with event type $policy-updated to the $policies stream.

    We recommend that you start with the default policy shown above and add your own policies and stream rules to it.

    Define policies by adding one or more entries to the streamPolicies JSON object, with the key being your policy name and the value being an access policy:

    json
    "streamPolicies": {
      "{your_policy_name}": {
        "$r": ["{roles_with_read_access}"],
        "$w": ["{roles_with_write_access}"],
        "$d": ["{roles_with_delete_access}"],
        "$mr": ["{roles_with_metadata_read_access}"],
        "$mw": ["{roles_with_metadata_write_access}"],
      },
      // Default policies truncated
    }

    For example, this policy, named customPolicy, grants read, write, delete, metadata read, and metadata write access to users with the ouro role, and only grants read access to users in the readers role:

    json
    "streamPolicies": {
        "customPolicy": {
            "$r": ["ouro", "readers"],
            "$w": ["ouro"],
            "$d": ["ouro"],
            "$mr": ["ouro"],
            "$mw": ["ouro"]
        },
        // Default policies truncated (full version shown below)
    }

    Define which policies apply to which stream prefix by adding a stream rule entry to the streamRules object:

    json
    "streamRules": [{
      "startsWith": "{stream_prefix}",
      "policy": "{your_policy_name}"
    },
    // Default stream rules truncated
    ]

    For example, these stream rules apply the customPolicy defined above to streams starting with account and customer:

    json
    "streamRules": [{
        "startsWith": "account",
        "policy": "customPolicy"
    }, {
        "startsWith": "customer",
        "policy": "customPolicy"
    }
    // Default stream rules truncated (full version shown below)
    ]

    ::: tip
    You can apply the same policy to multiple streams by defining multiple stream rules.
    :::

    Define which policies apply to user and system streams by default by updating the defaultStreamRules object:

    json
    "defaultStreamRules": {
      "userStreams": "{default_users_policy_name}",
      "systemStreams": "{default_system_policy_name}"
    }

    For example, to keep using the system defaults:

    json
    "defaultStreamRules": {
      "userStreams": "publicDefault",
      "systemStreams": "adminsDefault"
    }

    ::: tip
    Ensure the default policies are included in the streamPolicies object.
    :::

    To update the policies, append an event containing the JSON created in the above steps to the $policies stream with the $policy-updated event type:

    ::: tabs
    @tab HTTP

    http
    POST https://localhost:2113/streams/$policies
    Authorization: Basic admin changeit
    Content-Type: application/json
    Kurrent-EventId: b04a64bf-4ac2-4f36-a856-6da9b63e64b7
    Kurrent-EventType: $policy-updated
    
    < ./customPolicy.json

    @tab Powershell

    powershell
    curl.exe -X POST `
      -H "Content-Type: application/json" `
      -H "Kurrent-EventId: 8bc7c581-0f07-4987-bfcb-ab8f9404ca34" `
      -H "Kurrent-EventType: `$policy-updated" `
      -u "admin:changeit" `
      -d @customPolicy.json `
      https://localhost:2113/streams/%24policies

    @tab Bash

    bash
    curl -X POST \
      -H "Content-Type: application/json" \
      -H "Kurrent-EventId: cb6a722d-c775-4831-929b-86870560c68e" \
      -H "Kurrent-EventType: \$policy-updated" \
      -u "admin:changeit" \
      -d @customPolicy.json \
      https://localhost:2113/streams/%24policies

    @tab customPolicy.json

    json
    {
      "streamPolicies": {
        "customPolicy": {
          "$r": ["ouro", "readers"],
          "$w": ["ouro"],
          "$d": ["ouro"],
          "$mr": ["ouro"],
          "$mw": ["ouro"]
        },
        "publicDefault": {
          "$r": ["$all"],
          "$w": ["$all"],
          "$d": ["$all"],
          "$mr": ["$all"],
          "$mw": ["$all"]
        },
        "adminsDefault": {
          "$r": ["$admins"],
          "$w": ["$admins"],
          "$d": ["$admins"],
          "$mr": ["$admins"],
          "$mw": ["$admins"]
        },
        "projectionsDefault": {
          "$r": ["$all"],
          "$w": ["$admins"],
          "$d": ["$admins"],
          "$mr": ["$all"],
          "$mw": ["$admins"]
        }
      },
      "streamRules": [
        {
          "startsWith": "account",
          "policy": "customPolicy"
        },
        {
          "startsWith": "customer",
          "policy": "customPolicy"
        },
        {
          "startsWith": "$et-",
          "policy": "projectionsDefault"
        },
        {
          "startsWith": "$ce-",
          "policy": "projectionsDefault"
        },
        {
          "startsWith": "$bc-",
          "policy": "projectionsDefault"
        },
        {
          "startsWith": "$category-",
          "policy": "projectionsDefault"
        },
        {
          "startsWith": "$streams",
          "policy": "projectionsDefault"
        }
      ],
      "defaultStreamRules": {
        "userStreams": "publicDefault",
        "systemStreams": "adminsDefault"
      }
    }

    :::

    ::: note
    If a policy update is invalid, it will not be applied, and an error will be logged. KurrentDB will continue running with the previous valid policy in place.
    :::

    Stream policy schema

    Policy

    Key Type Description Required
    streamPolicies Dictionary<string, AccessPolicy> The named policies which can be applied to streams. Yes
    streamRules StreamRule[] An array of rules to apply for stream access. The ordering is important, and the first match wins. Yes
    defaultStreamRules DefaultStreamRules The default policies to apply if no other policies are defined for a stream Yes

    AccessPolicy

    Key Type Description Required
    $r string[] User and group names that are granted stream read access. Yes
    $w string[] User and group names that are granted stream write access. Yes
    $d string[] User and group names that are granted stream delete access. Yes
    $mr string[] User and group names that are granted metadata stream read access. Yes
    $mw string[] User and group names that are granted metadata stream write access. Yes

    ::: note
    Having metadata read or metadata write access to a stream does not grant read or write access to that stream.
    :::

    DefaultStreamRules

    Key Type Description Required
    userStreams AccessPolicy The default policy to apply for non-system streams. Yes
    systemStreams AccessPolicy The default policy to apply for system streams. Yes

    StreamRule

    Key Type Description Required
    startsWith string The stream prefix to which the rule is applied. Yes
    policy string The name of the policy to enforce for streams that match this prefix. Yes

    Tutorial

    Learn how to set up and use Stream Policy Authorization in KurrentDB through a tutorial.

    Troubleshooting

    License is invalid or not found

    This feature requires a license to use. If a license is not found or the provided license is invalid, you will see the following log:

    text
    [INF] StreamPolicySelector           Stream Policies plugin is not licensed, stream policy authorization cannot be enabled.

    Trying to enable the feature will give you the following errors, and the previous or default policy authorization settings will be used:

    text
    [ERR] StreamPolicySelector           Stream Policies plugin is not licensed, cannot enable Stream policies
    [ERR] StreamBasedAuthorizationPolicyRegistry Failed to enable policy selector plugin streampolicy. Authorization settings will not be applied.

    If the DefaultPolicyType is set to streampolicy, the fallback policy will be used, and stream access will be restricted to admins only:

    text
    [WRN] StreamBasedAuthorizationPolicyRegistry Could not load authorization policy settings. Restricting access to admins only.

    The `$authorization-policy-settings` stream has been deleted

    If the $authorization-policy-settings stream has been deleted (including hard deleted), the default policy type will be used, and KurrentDB will log the following:

    text
    [WRN] StreamBasedAuthorizationPolicyRegistry Authorization policy settings stream $authorization-policy-settings has been deleted.
    [INF] KurrentDB                     No existing authorization policy settings were found in $authorization-policy-settings. Using the default

    New authorization settings could not be applied

    If an invalid update is made to the $authorization-policy-changed stream, you will see the following log and the settings will not be updated.

    text
    [WRN] StreamBasedAuthorizationPolicyRegistry New authorization settings could not be applied. Settings were not updated.

    This can happen because:

    1. The event type is not $authorization-policy-changed:
    text
    [ERR] StreamBasedAuthorizationPolicyRegistry Invalid authorization policy settings event. Expected event type $authorization-policy-changed but got {invalid_event_type}
    1. The event is not valid JSON:
    text
    [ERR] StreamBasedAuthorizationPolicyRegistry Could not parse authorization policy settings
    1. The stream access policy type was not found. Ensure that the policy type is set to acl or streampolicy:
    text
    [ERR] StreamBasedAuthorizationPolicyRegistry Could not find policy not-found in registered authorization policy plugins.

    Could not parse policy

    If an invalid update is made to the $policies stream, you will see the following log and the policies will not be updated.

    text
    [ERR] StreamBasedPolicySelector      Could not parse policy

    This can happen because:

    1. A stream rule references a policy that does not exist. Ensure that the policy is defined in the streamPolicies json object:
    text
    [ERR] StreamPolicySelector           Stream rule for prefix: {stream_prefix} refers to an undefined policy: {not_found_policy}
    1. An access policy is missing a key. Ensure that each access key ($r, $w, $d, $mr, $md) is defined:
    text
    [ERR] StreamPolicySelector           Error while deserializing new policy
    System.ArgumentNullException: Value cannot be null. (Parameter 'Deleters cannot be null')
    1. A stream rule has an empty prefix:
    text
    [ERR] StreamPolicySelector           Error while deserializing new policy
    System.ArgumentNullException: Value cannot be null. (Parameter 'StartsWith cannot be null or empty')
    1. The event type is not $policy-updated:
    text
    [ERR] StreamPolicySelector           Expected event type: $policy-updated but was {invalid_event_type}

    File: docs/server/release-schedule/previous-versions.md


    order: 99

    Previous versions

    Version Release Date End of Support Status Release Notes
    26.0 16 January 2026 > January 2028 LTS, Latest Release Notes
    25.1 15 October 2025 January 2026 Out of support Release Notes
    25.0 25 March 2025 October 2025 Out of support Release Notes
    24.10 20 November 2024 > November 2026 LTS Release Notes
    24.6 26 June 2024 October 2024 Out of support Release Notes
    24.2 29 February 2024 June 2024 Out of support Release Notes
    23.10 13 October 2023 October 2025 Out of support Release Notes
    23.6 11 August 2023 October 2023 Out of support Release Notes
    22.10 14 November 2022 October 2024 Out of support Release Notes
    22.6 10 August 2022 October 2022 Out of support Release Notes
    21.10 03 November 2021 October 2023 Out of support Release Notes
    21.6 29 June 2021 October 2021 Out of support Release Notes
    21.2 25 February 2021 June 2021 Out of support Release Notes
    20.10 16 December 2020 October 2022 Out of support Release Notes
    20.6 09 June 2020 October 2020 Out of support Release Notes

    File: docs/server/release-schedule/README.md


    dir: text: "Release schedule" order: 7

    Release schedule

    KurrentDB production release version numbers are of the form Major.Minor.Patch.

    • Major
      • Corresponds to the year in which a release was released.
    • Minor
      • Indicates whether the release is an LTS or an STS.
      • Starting with 26.0, LTS releases have minor number 0. Previously they had minor number 10 (e.g. 23.10, 24.10).
    • Patch
      • Is incremented for bug fixes and occasionally small features.

    Note that the version scheme is not SemVer.

    Examples:

    Version Type Description
    26.0.0 LTS 2026 LTS release.
    26.0.1 LTS A patch to 26.0.0.
    26.1.0 STS First 2026 STS release.
    26.2.0 STS Second 2026 STS release.
    26.2.1 STS A patch to 26.2.0.
    27.0.0 LTS 2027 LTS release.

    Long term support releases

    There will be one LTS release of KurrentDB per year, released in or around January.

    These versions will be supported for a minimum of two years, with a two month grace period for organizing upgrades when the LTS goes out of support.

    Short term support releases

    STS releases will be published as new features are added to KurrentDB.

    These versions will be supported until the next STS or LTS release of KurrentDB.

    There are typically one or two STS releases per year.


    File: docs/server/release-schedule/release-notes.md


    order: 1

    Release notes

    This page contains the release notes for KurrentDB v26.2.

    [26.2.0](https://github.com/kurrent-io/KurrentDB/releases/tag/v26.2.0)

    Day Month 2026

    What's new

    Find out what's new in this release.


    File: docs/server/quick-start/default-directories.md


    order: 4

    Default directories

    The default directories used by KurrentDB vary by platform to fit with the common practices each platform.

    Linux

    When you install KurrentDB on Linux, the following locations apply:

    • Application: /usr/bin
    • Content: /usr/share/kurrentdb
    • Configuration: /etc/kurrentdb/
    • Data: /var/lib/kurrentdb
    • Server logs: /var/log/kurrentdb
    • Test client logs: ./testclientlog
    • Web content: ./clusternode-web then {Content}/clusternode-web
    • Projections: ./projections then {Content}/projections
    • Prelude: ./Prelude then {Content}/Prelude

    Windows

    • Configuration: ./
    • Data: ./data
    • Server logs: ./logs
    • Test client log: ./testclientlogs
    • Web content: ./clusternode-web
    • Projections: ./projections
    • Prelude: ./Prelude

    Local binaries

    When running KurrentDB using local binaries, either downloaded or built from source, the server will use its location and place the necessary files inside it:

    • Configuration: ./
    • Data: ./data
    • Server logs: ./logs
    • Test client log: ./testclientlogs
    • Web content: ./clusternode-web
    • Projections: ./projections
    • Prelude: ./Prelude

    Depending on the platform and installation type, the location of KurrentDB executables, configuration and other necessary files vary.