Code With Me Help

Code With Me administration guide

Since the Code With Me plugin lets you share your project with other participants, you can secure the connection and sharing with others by configuring on-premises servers.

You can use several approaches for your configuration, such as Docker-compose, Helm, offline storage, or use manual configuration.

Docker-compose setup

Before you start, make sure you have the ssl certificates for lobby.YOUR_DOMAIN and relay.YOUR_DOMAIN.

Configure docker-compose

  • YOUR_DOMAIN equals to cwm.internal in this setup.

  • YOUR_HOST is the IP address of machine which running docker-compose.

Instance reachability
Default domains for this setup are lobby.cwm.internal and relay.cwm.internal.

Configure your DNS to respond with YOUR_HOST for:

  • relay.cwm.internal
  • lobby.cwm.internal

Alternatively, search and replace lobby.cwm.internal and relay.cwm.internal with your domain name or host (for example, ).

Put your license.key in lobby/license.key. To obtain the license, visit the JetBrains website.

Lobby and Relay:

openssl ecparam -name secp384r1 -genkey -noout -out lobby/lobby_private.pem openssl ec -in lobby/lobby_private.pem -pubout -out relay/lobby_public.pem
Out of scope of this guide
Look into docker-compose.yaml and tweak lobby and relay parameters to your needs. Make sure you have at least 1.27 docker-compose version. For parameter reference visit Code With Me administration guide.
Whenever you see error like this - make sure you generated certificates for lobby and relays
lobby_1 | java.lang.NullPointerException: PEMParser(StringReader(keyText)).readObject() must not be null lobby_1 | at com.a.a.b.O6.a( lobby_1 | at com.a.a.b.O6.a( lobby_1 | at com.a.a.b.O6.b( lobby_1 | at com.a.a.b.O6.a( lobby_1 | at com.a.a.b.O6.main(

Kubernetes setup

For convenient Kubernetes deployment we provide organized Helm charts for Lobby and Relay respectively.

Before you start you must have the following:

  1. Kubernetes cluster and use CertManager along with ACME issuer type for managing the HTTPS certificates.

  2. kubectl
  3. helm at least of version 3.x.x

General information

There are two helm charts repos:

  • Lobby:

  • Relay:

Before first setup

Run next commands to configure helm repos:

helm repo add code-with-me-lobby helm repo add code-with-me-relay

Generate certificates for Lobby and Relay for authentication of users on relays:

openssl ecparam -name secp384r1 -genkey -noout -out relay_auth_private.pem openssl ec -in relay_auth_private.pem -pubout -out relay_auth_public.pem

Relay setup

First, pull relay helm chart:

helm pull code-with-me-relay/code-with-me-relay tar -xvf code-with-me-relay-0.0.1103.tgz cp code-with-me-relay/values.yaml my_relay_values.yaml

Now you have an unpacked relay helm chart with my_relay_values.yaml in your working directory.


Main entries that you should configure in your my_relay_values.yaml are:

  • clusterGlobalMainDomain: your cluster main domain

  • services_per_region: the number of relays on cluster

  • jwtPublicKey: insert your relay_auth_public.pem

For example:

clusterGlobalMainDomain: services_per_region: 2 jwtPublicKey: |- -----BEGIN PUBLIC KEY----- MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEfB9kX03d22JjL0Az0y1g008PYhadEAW6 mu+eLqYNCmn7HNtgM0MLtsjTsk82NsceORKBkRqHodO3y+76Cq9nJw0Q9rVpCNvE fwIkHbxOg2jsua2XdwNXaxjsTK+txM6J -----END PUBLIC KEY-----

Now, after you are finished with tuning, install the chart on your cluster.

helm install -f my_relay_values.yaml code-with-me-relay ./code-with-me-relay
The relays should be working fine.

Lobby setup

First, pull lobby helm chart

helm pull code-with-me-lobby/code-with-me-lobby tar -xvf code-with-me-lobby-0.0.1550.tgz cp code-with-me-lobby/values.yaml my_lobby_values.yaml

Now you have an unpacked relay helm chart with my_lobby_values.yaml in your working directory.

Configure lobby server

You should configure the following main entries in your my_lobby_values.yaml:

  1. usePersistentRedisVolume- true, if you want to preserve your sessions between lobby maintenance periods, requires Kubernetes PersistentVolumeClaim.

  2. Obtain you license file licenseFiles. To obtain it visit the Code With Me website.

    Check the following example:

    licenseFiles: licenze1: |- CONTENT_OF LICENSE_FILE_1 licenze2: |- CONTENT_OF ANOTHER_LICENSE FILE_2
  3. relaysConfigJson is a json with configuration of your relays, which was completed in Relay setup section.
    { "stunTurnServers": [ { "uri": "" }, { "uri": "" } ], "relays": [ { "regionName": "my_region", "latitude": 0, "longitude": 0, "servers": [ "wss://<YOUR-RELAY-DOMAIN-1.CLUSTER_DOMAIN>", "wss://<YOUR-RELAY-DOMAIN-2.CLUSTER_DOMAIN>" ] } ] }

    Check the following example:

    relaysConfigJson: |- { "stunTurnServers": [ { "uri": "" }, { "uri": "" } ], "relays": [ { "regionName": "my_region", "latitude": 0, "longitude": 0, "servers": [ "wss://<>", "wss://<>" ] } ] }

    If you don't know what to put in YOUR_RELAY_DOMAIN and CLUSTER_DOMAIN, look for ingressHostPattern in my_relay_values.yaml.

  4. Configure lobby settings:
    • lobby: main lobby settings.

    • lobby.config.relayPrivateKey: use this option to add the content of your relay_auth_private.pem.

    • lobby.config.jitsi_url and lobby.config.jitsi_private_key are optional and used to configure on-premise audio/video calls. Refer to JITSI setup.

    • lobby.ingresses: is an array of parameters for Kubernetes Ingress. Main entry here is host (for example, ""), others are optional. Most likely host it will contain only one ingress with content will contain your main cluster domain and array.

  5. base_url- is the domain which will be used in links generation, most of the time is the same as lobby.ingresses[0].host

Install the chart

  • After you are finished with tuning, install the chart on your cluster.

    helm install -f my_lobby_values.yaml code-with-me-lobby ./codewithme-lobby

    The lobby server is set and should work successfully.

Manual configuration

There are two key components for hosting the Code With Me on-premises: a lobby server and relay servers. Currently, the supported configuration is that the servers provide an HTTP/WS endpoint which should be wrapped as an SSL traffic by the reverse proxy (such as nginx, apache, caddy ).


A simple configuration consists of the following components:

  • One lobby server - follow this link to download the lobby server distribution

  • One relay server - follow this link to download the relay server distribution

  • Redis server, which is used for persisting the session data in case the lobby server goes offline

  • Nginx acting as an SSL frontend for the lobby and relay servers

  • License file

  • Docker-compose version 1.28+

Let's use the following assumptions:

  • SSL URI of the relay server is wss://relay.cwm.internal

  • You have the distribution of the relay server in the relay folder (for example, ws-relayd1.0)

  • SSL URI of the lobby server is https://lobby.cwm.internal

  • You have the distribution of the lobby server in the lobby folder (for example, lobby-server-linux-x64.1.0.tar.gz)

Configure servers

  1. Obtain your license file and save it in the same directory as a Dockerfile for your lobby server.

  2. Generate an ECDSA private key without the passphrase to prevent unauthorized to access the relay server. Lobby server expects an ECDSA private key file with 384-bit length in PEM format which can be generated with openssl.

    openssl ecparam -name secp384r1 -genkey -noout -out lobby/lobby_private.pem openssl ec -in lobby/lobby_private.pem -pubout -out relay/lobby_public.pem

  3. Create a Dockerfile for the relay server in the relay directory.

    The contents of the Dockerfile are as follows:

    FROM alpine:latest ARG DISTRIBUTION_VERSION="" ADD ws-relayd-linux-x64.${DISTRIBUTION_VERSION}.tar.gz /tmp/ws-relayd RUN mv /tmp/ws-relayd/ws-relayd-linux-x64.${DISTRIBUTION_VERSION} /ws-relayd && chmod +x /ws-relayd/ws-relayd COPY lobby_public.pem /ws-relayd/lobby_public.pem CMD /ws-relayd/ws-relayd -addr relay:3274 -jwt-key-type ecdsa -jwt-key-file /ws-relayd/lobby_public.pem
  4. Create a config.json file for providing the lobby server with proper relay URIs.

    { "relays": [ { "regionName": "internal", "latitude": 0, "longitude": 0, "servers": [ "wss://relay.cwm.internal" ] } ] }

  5. Create a Dockerfile for the lobby server in the lobby directory and add the license key file to it.
    FROM debian:buster-slim ARG DISTRIBUTION_VERSION="" ADD lobby-server-linux-x64.${DISTRIBUTION_VERSION}.tar.gz /home/lobby-server COPY lobby_private.pem /home/lobby-server/lobby_private.pem COPY config.json /home/lobby-server/config.json COPY license.key /home/work/lobby-server/license.key WORKDIR /home/lobby-server ENV JAVA_HOME /home/lobby-server/jbr ENV SERVER_PORT 2093 ENV BASE_URL https://lobby.cwm.internal ENV ENABLED_FEATURES direct_tcp,ws_relay ENV CONFIG_JSON /home/lobby-server/config.json ENV RELAYS_ECDSA_JWT_KEY_FILE /home/lobby-server/lobby_private.pem ENV REDIS_HOST redis ENV REDIS_PORT 6379 ENV LICENSE_BUNDLES /home/lobby-server/license.key ENTRYPOINT ["bin/lobby-server"]

    In case you deploy the docker image for lobby-server to Kubernetes, pay attention to the names given to services to avoid names collision with environment variables already set for the lobby server (for example, REDIS_PORT ). For more information, refer to the Kubernetes Service documentation.

  6. Write the nginx.conf file in the nginx directory.

    events {} http { server { listen 443 ssl; server_name relay.cwm.internal; ssl_certificate /etc/ssl/nginx/relay.cwm.internal.crt; ssl_certificate_key /etc/ssl/nginx/relay.cwm.internal.key; location / { proxy_pass http://relay:3274; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header Host $host; } } server { listen 443 ssl; server_name lobby.cwm.internal; ssl_certificate /etc/ssl/nginx/lobby.cwm.internal.crt; ssl_certificate_key /etc/ssl/nginx/lobby.cwm.internal.key; location / { proxy_pass http://lobby:2093; } } }

  7. Copy the certificates and keys to the nginx/ssl directory.
  8. Create a docker-compose file. We're assuming the following:
    • Lobby server Dockerfile and distribution are in the lobby directory

    • Lobby server version is 1.0

    • Relay server Dockerfile and distribution are in the relay directory

    • Relay server version is 1.0

    • nginx.conf is in the nginx directory

    • Certificates and keys are in the nginx/ssl folder

    • Redis data is persisted in /redis/data. If you don't need that, remove the command and volumes subsections from the redis container configuration.

    The content of the docker-compose.yaml file is as follows:

    version: "3.8" services: nginx: image: nginx:latest volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./nginx/ssl:/etc/ssl/nginx:ro ports: - 443:443 relay: build: context: ./relay args: DISTRIBUTION_VERSION: 1.0 lobby: build: context: ./lobby args: DISTRIBUTION_VERSION: 1.0 redis: image: redis:latest command: ["redis-server", "--appendonly", "yes"] volumes: - ./redis/data:/data
  9. $ docker-compose up and make sure your host is listening on the relay.cwm.internal and lobby.cwm.internal at the 443 port.

Lobby server

A lobby server is responsible for the following:

  • Generating a link that can be used to join the Code With Me session

  • Reporting a list of supported features to a client (for example, whether P2P is allowed or not)

  • Selecting a relay server in case P2P does not work or is forbidden

The lobby server is distributed in the binary form for the linux-x64 platform with all the dependencies (for example, the runtime) included:


Lobby server configuration

You can configure the lobby server with the following environment variables:

Environment variableDescription
SERVER_PORTUse this option as the port at which the server will listen for incoming request.
SERVER_LISTEN_ONUse this option as the interface at which the server will listen.

By default, it listens on all interfaces. For example, set it to to listen on the localhost only.

BASE_URLUse this option as base URL at which the server is hosted.

For example, This will be used for creating the Code With Me session URLs.

ROOT_PREFIXUse this option as a prefix for which to listen for HTTP requests (for example, /$ROOT_PREFIX/version /). Must start with the / and must not end with the /.
DIAGNOSTICS_PORTThis is optional. If this variable is set, the following port can be used by Prometheus to access the various metrics of the server.
CONFIG_JSONUse this option as a path to the json file that contains the list of relays from which the lobby server can choose if there's no P2P.

The following is the sample configuration with the geolocation enabled (json ):

{ "relays": [ { "regionName": "eu", "latitude": 60.571442, "longitude": 27.187427, "servers": [ "wss://", "wss://" ] }, { "regionName": "na", "latitude": 33.220572, "longitude": -80.008131, "servers": [ "wss://", "wss://" ] } ] }

The following is a sample configuration with the geolocation disabled (json ):

{ "relays": [ { "regionName": "internal", "latitude": 0, "longitude": 0, "servers": [ "wss://relay-1.internal", "wss://relay-2.internal" ] } ] }
RELAYS_ECDSA_JWT_KEY_FILEUse this option as the ECDSA private key file that is used to prevent unathroized access to the relay servers. Must have 384-bit key length and be in the PEM format.
REDIS_HOSTThis variable is optional. If it is set, it will use the specified Redis host for persisting the session data. It is strongly recommended using one instead of relying on the in-memory approach.
REDIS_PORTThis variable is optional. This is the port used with the REDIS_HOST.

The port defaults to 6379.

GEOLITE_MMDB_FILEThis variable is optional. If it is set, the Geolite2 file will be read and used for selecting the relays on the proximity basis to the user.
ENABLED_FEATURESUse this option as the comma-separated list of the following features that are supported by this server instance.
  • ws_relay:

    use relays in case if the P2P connection does not work or is forbidden

  • p2p_quic:

    use the QUIC protocol for the P2P connections

  • direct_tcp:

    use the P2P connection between the clients using TCP

  • jitsi_telephony:

    use this flag for the video/audio conference functionality

  • project_names:

    use this flag to allow clients to see the name of the project that they are joining

  • user_names:

    use this flag to allow clients to see the user of the project that they are joining. It is also shows user names on the lobby session page.

LICENSE_BUNDLESUse this option for specifying the license.key for your lobby server. To obtain the license, visit the JetBrains website.

If you have more than one license key, specify each license key separating them with comma ,.

The information about the license expiration dates and a number of concurrent sessions are available during the server start.

Generate a key for the lobby server

The lobby server expects the ECDSA private key file with the 384-bit length in the PEM format.

It can be generated using the $ openssl tool as follows:

$ openssl ecparam -name secp384r1 -genkey -noout -out jwtES384key.pem $ openssl ec -in jwtES384key.pem -pubout -out jwtES384pubkey.pem

The public key counterpart should be used on the relay server for verification.

Relay server

A relay server is responsible for relaying the traffic between the host and guests in cases when the P2P connection does not work or is forbidden. The relay server has a functionality of verifying whether the incoming request is coming from the genuine lobby server via a JWT token.

The relay server is distributed as a single binary and is available for the linux-x64 platform:


The relay server configuration

You can configure the relay server with the following command-line arguments:

-addrUse this argument as the address on which to listen for incoming connections.

For example,

Use the :port syntax to listen on all available interfaces. For example, :8099 to listen on all available interfaces at port 8099.

-prometheus-addrUse this argument as the address on which to listen for the Prometheus metrics request.

For example,

Use the :port syntax to listen on all available interfaces. For example, :4422 to listen on all available interfaces at port 4422.

-jwtKeyTypeUse this argument as the type of key provided for the verification of requests.

The supported values are as follows:

  • hmac
  • rsa
  • ecdsa

Note that the lobby server only supports the 384-bit ECDSA keys.

-jwtKeyFileUse this argument as a file from which to read the JWT public key. This must be in the PEM format.
-allow-server-without-authenticationUse this argument as an explicit flag that allows the relay server to run in a mode which skips the request verification.

Guest local storage setup

Lobby Guests Local Storage is an on-premises lobby server feature that lets you download required Code With Me guest files from JetBrains to a dedicated local storage in your network and distribute them among users.

This mode lets you host your on-premise setup in the isolated network environment without connections between Code With Me user machines and JetBrains sites. This also allows you to avoid the use of external URLs when requesting guests from the lobby server.

Set up the guest local storage

  1. Use the mirror-guests command-line tool. It is a file provided as part of the lobby distribution archive starting with build 1593. Obtain the required files from the JetBrains website and exit after saving.

    It accepts the following filter as parameters:

    • products

    • versions

    • platforms

  2. Specify a path to a target storage.

    Check the following example:

    bin/mirror-guests --verbose --products-filter IU --platforms-filter linux-x64 --versions-filter 2021.1 /home/ubuntu/sources/

    For a full list of available options use the “help” option:

    mirror-guests --help
  3. When required files are saved locally, you need to place them somewhere on the server where the main lobby will work with them in the isolated environment.

    To point the lobby server to the Code With Me files storage, use the following environment variable:


    Check the following example:

    ENV GUESTS_LOCAL_STORAGE_DIRECTORY /home/CWM/storage (in lobby Dockerfile)

    After successful setup, the guests' distribution should be available at the following URL:


    Alternatively, you may publish this folder on any other HTTP server and configure lobby to get files from there:

    CWM_GUEST_DOWNLOAD_LOCATION=https://some-local-site.intranet/code-with-me-clients JRE_DOWNLOAD_LOCATION=https://some-local-site.intranet/code-with-me-clients

After the download, guests should be available at URL: <lobby_url>/clients.

GUESTS_LOCAL_STORAGE_DIRECTORYPathA path to a local directory where guests should be downloaded. A new directory is created if there is no existing directory available. Ignore all existing files inside the existing directory. The environment variable is required when running in the guest local storage mode.
INTELLIJ_PRODUCTS_JSON_LOCATIONURLURL location for IntelliJ IDEA products to process. The default value is set to public IntelliJ IDEA products JSON URL if no other value is specified through the ENV variable.


You can specify filters to limit the guest download scope.

GUESTS_LOCAL_STORAGE_FILTER_PRODUCTSStringSpecify a list of products to check and download. Use the product codes separated with comma, such as AC,IU,IC.
GUESTS_LOCAL_STORAGE_FILTER_VERSIONStringA product version to track.

There are two formats available:

  • Exact version match, such as 2021.1

  • Extended format: 2021.1+ which stands for the 2021.1 version or higher.

GUESTS_LOCAL_STORAGE_FILTER_BUILDStringAn exact build number to download, such as 211.6222.4. All products with the existing build are downloaded.
GUESTS_LOCAL_STORAGE_FILTER_PLATFORMSStringA platform name to download. Available values include: windows, mac, macM1, linux, linuxWithoutJBR. Multiple values are separated by comma.
GUESTS_LOCAL_STORAGE_FILTER_INCLUDE_EAPBooleanA flag indicating whether downloads should include EAP builds.

Troubleshooting potential problems

Check the following solutions for potential problems you might encounter when setting up local storage:

  • No files are downloaded to local storage after running the downloader script:

  • Downloader script fails on the attempt to create the storage folder:

    • Check the system permissions - it should be allowed to write to a specified location

  • Users cannot download the appropriate client version from the internal storage during the connection attempt:

    • Check if the IDE version used by the host has the corresponding client file at the corporate storage

Audio/Video chat setup

Code With Me uses Jitsi as a main framework for audio/video group calls. Jitsi is an open source solution, and seems to be an industry standard for those who seek such a framework. It is actively developed and improved by the community and used by various large corporations.

The Jitsi client has a web-based user interface, so its UI is integrated with JCEF technology. It is not anticipated that Code With Me users will require video so often - it’s more likely one would need to just have a voice communication during a session, so call management is wrapped with simple toolbar actions.

Server side is more interesting. Jitsi needs to create a virtual call room - this part of logic perfectly aligns with the Code With Me lobby server which generates a link. However, lobby-server has nothing to do with Jitsi services, instead it gets the username, generates the room’s name, determines the geo-region for a video bridge service, signs it with a private key and sends this back to the host. Received data can now be used from the host’s machine to enter the Jitsi’s virtual room.

Prosody, Jicofo and Web Frontend form the “Core” package, while Videobridge is a standalone scalable unit.

Free public Jitsi

You can use It's free, but it has some limitations. There are no guarantees that all Code With Me features will work with this type of distribution as well as there will be no guarantees that your meetings are safe because they are not protected by security JWT token.

Enable free public Jitsi

On-premise Audio/Video calls

Jitsi backend is represented with four main components:

  • Prosody: xmpp server for communication between components and clients

  • Jicofo: component for conference management

  • Web frontend: which serves Jitsi client web application and allows clients to communicate to prosody and other components

  • Videobridge: component which represents scalable and performant SFU implementation

Self-hosted Jitsi instance

Jitsi can be hosted in one of the following ways:

  • Everything hosted on a single machine: you can use Jitsi Quick Setup

    Some AWS hints and stats can be found here.

  • Single "Core" package + multiple Videobridges

  • Multiple "Core" packages + Videobridges

    This setup does not cover this case. Also, you can have problems with integrating this distribution to lobby due to only one link limitation in JITSI_URL parameter. This can be supported in the future.

Lobby configuration

Once you have your Jitsi instance - put its URL into JITSI_URL environment variable.

From now on you should have audio/video calls during the Code With Me sessions, but it is highly recommended to add some security to your meetings. Read about Secure setup.

Secure setup

To guarantee a secure video chat experience each client provides a JWT token signed by the lobby server that allows them to join a particular room in the system. JWT credentials are also time limited to prevent repetition attacks.

To make a signed JWT token Lobby server requires a private key.

To check validity of JWT token Jitsi requires associated public key.

Generate RSA256 for JWT tokens

  • To generate private and public key you can use these commands:

    ssh-keygen -t rsa -b 4096 -m PEM -f jwtRS256.key # Don't add passphrase openssl rsa -in jwtRS256.key -pubout -outform PEM -out

Configure lobby Jitsi certificate

  • Set the lobby server JITSI_RSA_JWT_KEY_FILE ENV variable file path of the jwtRS256.key.

    From now on lobby will generate links with jwt tokens.

    Next, we must configure jitsi to accept tokens forged by Lobby server.

Configure prosody

  1. The configuration is based on this guide.

    To make prosody capable of working with jwt tokens install jitsi-meet-tokens package:

    apt-get install jitsi-meet-tokens
  2. Now we have to tweak some parameters manually. Prosody config is located at /etc/prosody/prosody.cfg.lua.

    Make sure that /etc/prosody/prosody.cfg.lua contains the line below:

    Include "conf.d/*.cfg.lua"
  3. Also, make sure that client to server encryption is not enforced.

    Otherwise, token authentication won't work:

  4. For cases where JWT tokens signed by certificate, prosody provides asap_key_server config entry. asap_key_server is a server which serves public part of JWT token certificate.

    asap_key_server should serve our certificate for path containing sha256 of kid claim of incoming JWT token.

    kid claim of our JWT token equals CWMKey:

    sha256("CWMKey") is 8791edb7d806447dd0aaa98a2b568ad792696ed4d885f23b89f6beb7f7c4f44e

    The app_id setting of your VirtualHost in prosody config should equal to CodeWithMe.

    Check the following code example:

    ... c2s_require_encryption = false ... VirtualHost "" authentication = "token"; app_id = "CodeWithMe"; asap_key_server = ""; allow_empty_token = false; ...

asap_key_server nginx configuration (could also be the one, which serves Jitsi frontend):

... location ~ ^/asap/8791edb7d806447dd0aaa98a2b568ad792696ed4d885f23b89f6beb7f7c4f44e(.pem)?$ { alias /path/to/; } ...
Last modified: 13 May 2021