System Architecture
Nessie is built around three layers: the API layer (shared models), the platform layer (runtime & plugin orchestration), and the plugin layer (all features). Understanding this separation is key to extending the system.
High-Level Overview
High-level layer diagram: Web Servers → Plugins → Platform → API
The Plugin System
At the heart of Nessie is the PluginManager. When the platform starts, it calls
discover_plugins() which uses Python entry points (nessie_plugins group)
to find and register all installed plugins automatically — no manual wiring needed.
Each plugin is a Python function decorated with @plugin("Plugin Name") that returns a dictionary:
@plugin("My Plugin")
def my_plugin():
return {
"handlers": {
"load_graph": handle_load_graph, # action_name: handler_fn
},
"requires": ["filter_graph"], # actions this plugin needs
"setup_requires": {
"File Path": SetupRequirementType.STRING
},
}
The handlers dict maps action names to handler functions. When an action is dispatched, the PluginManager finds the appropriate plugin and calls its handler with (action, context).
Core Data Models (nessie-api)
The nessie-api package defines the shared models that all plugins and the platform rely on. These are the only imports your plugin needs.
Core data models from nessie-api
Request Lifecycle
When a user performs an action in the browser (e.g., opens a workspace), here is the full flow:
Action request lifecycle: Browser → Server → Platform → Plugin → UI Render
Context Protocol
Plugins never access the platform directly. Instead, every handler receives a Context object that exposes a controlled interface for reading and writing shared state:
| Method Group | Key Methods | Description |
|---|---|---|
| Workspaces | get_workspace_count(), add_workspace(graph), close_workspace_at(i) | Manage workspace tabs |
| Graphs | get_graph_at(i), set_graph_at(i, g), get_full_graph_at(i) | Read/write workspace graphs |
| Filters | add_filter_at(i, expr), clear_filters_at(i) | Manage active filters |
| Console | add_console_message_at(i, msg), clear_console_messages_at(i) | Log messages to the UI console |
| Search | get_search_at(i), set_search_at(i, q) | Read/write search query state |
| Actions | perform_action(action, plugin_name?) | Dispatch additional actions from within a handler |
| Visualization | get_visualised_graph_at(i), set_visualiser_at(i, name) | Render graph HTML or switch active visualizer |
Plugin Discovery
Nessie uses Python's entry points mechanism. Any installed Python package that declares a nessie_plugins entry point is automatically discovered when the platform starts.
# In pyproject.toml
[project.entry-points."nessie_plugins"]
my_plugin = "my_package:my_plugin_function"