Questions about joining and controlling Omniverse Live Session from a headless Kit-based microservice

Hello everyone,

I am currently developing a Kit-based microservice that connects to an Omniverse Nucleus Server, with the goal of joining and controlling a Live Session programmatically (without a GUI).

I have a few questions regarding the supported mechanisms and runtime requirements:


1. Joining a Live Session via omni.kit.collaboration.channel_manager

Is it supported to join a Live Session using omni.kit.collaboration.channel_manager from a microservice?

For example:

import omni.kit.collaboration.channel_manager as channel_manager

channel = await channel_manager.join_channel_async(base_url, False)

If so:

  • Does this implicitly join an existing Live Session?

  • Are there any additional steps required (e.g., session discovery, permissions, or context setup)?


2. Joining a Live Session via HTTP POST

Is it possible to join a Live Session purely via an HTTP POST API (e.g., /open_live_session)?

Or:

  • Is a persistent python process required to maintain the Live Session connection?

  • Is the POST request only intended to trigger a session that is then managed by a running Kit instance?


3. Error: no DISPLAY environment variable specified

When testing:

POST /open_live_session

from a terminal in a headless environment, I encounter the following error:

Error: no DISPLAY environment variable specified

Does this imply that:

  • A windowing system (X11) is required for Live Session initialization?

  • Some part of the Live Session workflow still requires rendering or UI context, even if no actual drawing is needed?

If there is a recommended way to run Live Sessions in fully headless mode (e.g., Docker, server-side microservice), I would greatly appreciate any guidance or documentation references.


Thank you very much for your help and insights.

Best regards,
Johnny Hsiao

Hello and sorry for the delay in this answer. This is a section that is best suited for just actual tutorials and guides we provide, so my response is delayed.

You can join and control Live Sessions programmatically from a Kit‑based microservice, but you still need a running Kit process (with the Live stack loaded), and you must either provide a headless display environment or run a truly headless Kit build. Pure “one‑shot HTTP POST with no Kit process” is not enough.

Below I’ll walk through each of your questions.


1. Using omni.kit.collaboration.channel_manager in a microservice

Yes, omni.kit.collaboration.channel_manager is intended to let code join a Live channel and send/receive messages; it works from Python code running inside a Kit app (including a microservice built on Kit). docs.omniverse.nvidia

A typical flow is:

import omni.kit.collaboration.channel_manager as cm

channel = await cm.join_channel_async(channel_url, create_if_missing=False)
# channel is a Channel object; you can listen for messages, send messages, etc.

Key points:

  • join_channel_async(base_url, create_if_missing) connects to the Live channel for a given URL and starts listening. docs.omniverse.nvidia
  • It does not by itself create or open the USD stage or LiveSession; you usually:
    • Open the base USD layer.
    • Create or get a LiveSession for that layer (omni.kit.usd.layers.LiveSession). docs.omniverse.nvidia
    • Join the LiveSession, which adds the .live sublayer and sets the edit target. docs.omniverse.nvidia
    • Use the channel only for Live messaging (user list, chat, etc.).

The connect-samples repo has a liveSession.py script that shows the more complete, non‑GUI flow: it opens a stage, finds/creates a LiveSession, joins it, and then attaches to the LiveSessionChannel to process messages in a loop. github

So, for a microservice:

  1. Start Kit in headless mode with the needed extensions (USD, omni.kit.usd.layers, omni.connect.core, omni.kit.collaboration.channel_manager, your own service ext).
  2. In your extension’s startup:
    • Open the USD stage (or wait for it to exist).
    • Create or open the LiveSession (LiveSession(stage_or_identifier)).
    • Call live_session.join(session_name) to get the .live layer attached. github
    • Use channel_manager.join_channel_async(live_session.getInfo().getChannelUri(), False) if you also need to interact with the Live message channel (e.g., user list, custom messages). docs.omniverse.nvidia

Permissions and discovery:

  • “Joining” a Live Session is basically joining its .live sublayer and its channel; you must have read/write access to the Nucleus path (via your Nucleus credentials / tokens).
  • There is no automatic “discover sessions” HTTP index; in practice you either:
    • Already know the Nucleus path and session name you want to join, or
    • Use Nucleus browsing APIs or a custom service to enumerate .live layers on a given USD file.

2. Joining / controlling a Live Session via HTTP POST only

The /open_live_session endpoint you tested is not a self‑contained Live client. It is typically an HTTP endpoint exposed by a running Kit app that:

  • Receives a POST, and
  • In response, tells that Kit app to open/join a Live Session on a given stage. github

Therefore:

  • You cannot join and drive a Live Session purely by calling HTTP against Nucleus alone; you always need a Kit‑based process that:
    • Maintains the WebSocket / Live channel connection.
    • Holds the USD stage and .live layer in memory.
  • A persistent Python/Kit process is required; the HTTP POST is just a trigger into that process, not a substitute for it.

If you want a pure HTTP interface to Live from other services, the usual pattern is:

  • Build a Kit microservice that runs headless and exposes your own REST or gRPC endpoints.
  • Those endpoints call into omni.kit.usd.layers.LiveSession, channel_manager, and USD APIs inside the long‑running Kit process. docs.omniverse.nvidia

3. Error: no DISPLAY environment variable specified

This error indicates that the Kit binary you’re invoking expects an X/GLFW windowing environment, even if you don’t plan to draw anything. Some standard Composer/Viewer configs try to load windowing plugins, which check DISPLAY on Linux and fail if there is no X server. github

Implications:

  • It doesn’t mean Live itself “requires a GUI,” but your chosen Kit config likely includes UI and windowing extensions. Those try to initialize a display as part of startup, and that fails in a bare headless environment.
  • That’s why you see no DISPLAY when you hit /open_live_session from a terminal: behind that endpoint, a UI‑configured Kit is trying to open or change a stage and hits windowing init.

Headless options:

  • Use a headless Kit configuration (no omni.appwindow, carb.windowing-glfw, etc.). Omniverse docs emphasize that Kit can be run headless to create microservices. docs.omniverse.nvidia
  • Or, provide a virtual X server (e.g., Xvfb) and set DISPLAY=:99 so the existing config can initialize windowing without a physical monitor. This is a common workaround when you must reuse a UI‑oriented kit file in a headless environment. stackoverflow

Recommended for a clean microservice:

  1. Start from the Kit App Template or a minimal headless .kit file provided in the docs, which omits UI/windowing extensions and is designed for service use. docs.omniverse.nvidia
  2. Add only what you need: Nucleus client, USD, LiveSession, collaboration channel, any message/HTTP service extension.
  3. Run it in Docker or on a server as a long‑lived service process. No X server needed if you avoid windowing extensions.

Putting it together: a workable microservice pattern

For your use case (“join and control Live Session programmatically, no GUI”):

  • Yes – use omni.kit.usd.layers.LiveSession and omni.kit.collaboration.channel_manager from Python running in a headless Kit process. docs.omniverse.nvidia
  • No – you can’t rely on a single /open_live_session POST to Nucleus without any Kit process; that endpoint must be handled by Kit.
  • Headless – either:
    • Use a Kit config built explicitly for headless/microservice (preferred), or
    • Run an Xvfb/similar virtual display and set DISPLAY so that a UI‑centric config can still start.