Connection Manager [omni.kit.widget.connection_manager]#
Unified server connection management for Nucleus, storage discovery, and HTTP servers.
Purpose#
This extension is the single authority for managing server connections across the Kit ecosystem. It replaces the previous omni.kit.widget.nucleus_connector (now a deprecated shim) and consolidates connection handling for:
Nucleus servers (
omniverse://) — authentication, sign-in, reconnectDiscovery servers — automatic storage service discovery via
omni.client.register_storage_from_discovery_with_callbackHTTP/HTTPS servers — manual S3, Azure, GCS, or custom HTTP storage connections
Architecture#
omni.kit.widget.connection_manager
├── extension.py Singleton lifecycle + module-level convenience functions
├── manager.py ConnectionManager: ensure_connection, auth, add-server dialogs
├── registry.py ConnectionRegistry: discovery + manual server state, events, persistence
├── toml_store.py All TOML I/O (omniverse.toml), file watcher, migration
├── url_utils.py URL normalization and validation
└── ui/
├── add_server_dialog.py Unified add-server dialog (input -> connecting -> success/error)
├── auth_dialog.py Standalone Nucleus browser sign-in prompt
├── alert_pane.py Reusable alert widget
├── progress_bar.py Reusable progress bar widget
└── style.py UI styles
Key Design Decisions#
ConnectionRegistry owns all state (bookmarks, discovery servers, manual servers) and emits events on changes. It is the source of truth — UI components read from it, never the reverse.
ConnectionManager wraps the registry with auth handling, add-server dialogs, and the
ensure_connection()coroutine.Module-level functions (
ensure_connection,disconnect,reconnect,show_add_*_dialog) delegate to the singleton so callers never need to import the manager class directly.Events use the
omni.kit.widget.connection_manager.*namespace and are registered as aliases for integer event IDs viaomni.kit.app.register_event_alias.
Data Flow#
┌──────────────────────┐
│ External Callers │
│ (filepicker, usd, │
│ file_window, etc.) │
└────────┬─────────────┘
│
ensure_connection(url)
│
┌────────▼─────────────┐
│ ConnectionManager │
│ (manager.py) │
│ │
│ - auth callbacks │
│ - add-server UX │
│ - stat_with_retry │
└────────┬─────────────┘
│
┌────────▼─────────────┐
│ ConnectionRegistry │
│ (registry.py) │
│ │
│ - bookmarks │
│ - discovery servers │──── omni.client discovery API
│ - manual servers │
│ - events │──── event bus (carb.eventdispatcher)
│ - persistence │──── omniverse.toml (via toml_store)
└──────────────────────┘
Threading Model#
The omni.client discovery callback runs on a background thread. To avoid races with UI-thread reads, the callback uses asyncio.get_event_loop().call_soon_threadsafe() to schedule all state mutations on the main event loop. This means:
All
DiscoveryServerStatemutations happen on the main thread.The
asyncio.Eventused forensure_connectionawaiting is set on the main thread.UI code can safely read registry state without locking.
Usage#
Ensuring a Server is Reachable#
The primary entry point. Call this before accessing a URL to guarantee connectivity:
from omni.kit.widget.connection_manager import ensure_connection
# Returns True if reachable, False on failure
success = await ensure_connection("omniverse://my-server/path/to/file.usd")
# With a progress dialog for callers without their own UI
success = await ensure_connection(url, show_connecting_dialog=True)
ensure_connection handles all server types:
Discovery — if the URL matches a known discovery server’s cached addresses, ensures discovery is complete.
Nucleus — for
omniverse://URLs, performs stat with auth/reconnect handling.HTTP/HTTPS — considered always reachable, returns True immediately.
Adding Servers Programmatically#
from omni.kit.widget.connection_manager import get_instance
cm = get_instance()
# Discovery server (triggers omni.client storage discovery)
sid = cm.registry.add_discovery_server(
name="My Storage",
discovery_url="https://discovery.example.com/",
)
# Manual Nucleus server
sid = cm.registry.add_manual_server(
name="my-server",
url="omniverse://my-server/",
)
# Manual HTTP server
sid = cm.registry.add_manual_server(
name="S3 Bucket",
url="https://mybucket.s3.amazonaws.com/",
)
Showing Add-Server Dialogs#
from omni.kit.widget.connection_manager import (
show_add_discovery_server_dialog,
show_add_nucleus_server_dialog,
show_add_http_server_dialog,
)
# Each accepts optional on_success and on_failed callbacks
show_add_discovery_server_dialog(
on_success=lambda name, url, sid: print(f"Added {name}"),
on_failed=lambda: print("Cancelled"),
)
Only one dialog can be active at a time. A second call while a dialog is open will invoke on_failed and log a warning.
Disconnecting / Reconnecting#
from omni.kit.widget.connection_manager import disconnect, reconnect
disconnect("omniverse://my-server/") # Signs out from Nucleus
reconnect("omniverse://my-server/") # Reconnects asynchronously
Both are no-ops for non-Nucleus URLs.
Managing Bookmarks#
from omni.kit.widget.connection_manager import (
get_bookmarks,
add_bookmark,
remove_bookmark,
rename_bookmark,
)
# Get all bookmarks (name -> url)
bookmarks = get_bookmarks()
# Add a bookmark
add_bookmark("My Project", "omniverse://my-server/Projects/MyProject/")
# Remove by name
remove_bookmark("My Project")
# Rename (old_name, new_name, url)
rename_bookmark("Old Name", "New Name", "omniverse://my-server/Projects/")
Listening for Events#
from carb import eventdispatcher
from omni.kit.widget.connection_manager import (
BOOKMARK_ADDED_EVENT,
BOOKMARK_DELETED_EVENT,
BOOKMARK_RENAMED_EVENT,
CONNECTION_SUCCEEDED_EVENT,
DISCOVERY_SERVER_ADDED_EVENT,
DISCOVERY_SERVER_REMOVED_EVENT,
DISCOVERY_SERVER_UPDATED_EVENT,
MANUAL_SERVER_ADDED_EVENT,
MANUAL_SERVER_REMOVED_EVENT,
)
sub = eventdispatcher.get_eventdispatcher().observe_event(
event_name=DISCOVERY_SERVER_UPDATED_EVENT,
on_event=lambda event: print(f"Discovery updated: {event['url']}"),
)
Event Payloads#
All events include at minimum a "url" key. Full payload details:
Event |
Payload Keys |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Persistence (omniverse.toml)#
All data is persisted in omniverse.toml (resolved via ${omni_global_config}/omniverse.toml). This file is global and survives app updates.
[bookmarks]
Astronaut = "https://...amazonaws.com/Samples/Astronaut/"
"user@example.com" = "omniverse://content.ov.nvidia.com/Users/user@example.com/"
[servers]
"omniverse://my-server" = "my-server"
"https://mybucket.s3.amazonaws.com" = "S3 Bucket"
[[discovery_servers]]
name = "My Storage"
url = "https://discovery.example.com/"
cached_addresses = ["omniverse://storage.example.com/", "https://bucket.s3.amazonaws.com/"]
[bookmarks]isname = url.[servers]isurl = name(URL is the key; each server listed once).[[discovery_servers]]uses TOML array-of-tables forcached_addresses.Foreign sections (e.g.
[paths]) are preserved across read-modify-write cycles._persist()writes all three sections via a singlesave_sections()call totoml_store.
File Watching#
TomlFileWatcher uses omni.client.stat_subscribe_with_callback with a 1-second debounce to detect external edits to omniverse.toml. On change, _apply_toml_reload() diffs against in-memory state and emits add/remove events for changes. Self-triggered writes are naturally a no-op (diff finds nothing to change).
Migration#
On first startup with old-style TOML, if [bookmarks] exists but both [servers] and [[discovery_servers]] are absent, servers are derived from [bookmarks] by extracting unique scheme://host roots (Azure: scheme://host/container). Bookmarks that point exactly at a server root are removed from [bookmarks] after their server entry is created, matching the legacy behavior where root bookmarks appeared in the server list instead of the bookmark list.
Migration from nucleus_connector#
omni.kit.widget.nucleus_connector is now a thin compatibility shim. All calls delegate to this extension with deprecation warnings. See the migration guide in the nucleus_connector extension (docs/MIGRATION.md) for details.