Skip to main content
Plugins extend Noxus without changing core platform code. A single plugin is an installable Python package built with the noxus-sdk that can contribute nodes, integrations with credentials, polling triggers, and knowledge-base data sources — all versioned and deployed together. At install time the platform reads the plugin’s manifest (reflected from your code), provisions an isolated sandbox, and starts a long-lived worker in it. At run time the platform dispatches component calls to that worker over JSON-RPC. You never register anything globally: the manifest is the contract.

What a plugin can provide

A plugin must declare at least one node, integration, trigger, or data source. Each component surfaces in the matching place in the product — nodes in the flow editor’s palette, integrations in workspace settings, triggers in the trigger catalogue, data sources in the knowledge-base source picker.

Nodes — V2 and V1

Nodes are the building blocks of workflows. Plugins provide two kinds, and a single plugin can mix them freely:

V2 nodes (preferred)

Connector-free, mirroring the platform’s native V2 nodes. A node is two schemas: a config schema whose fields are its settings (a field marked bindable=True becomes an input that takes a literal or a :var[...] reference), and an output schema that declares every output. Everything arrives on self.config.

V1 nodes (legacy)

Edge-connector nodes for V1 flows. Inputs and outputs are Connector objects wired by edges; inputs arrive as keyword arguments to call. Use only when you specifically need a V1 flow.
The SDK keeps V1 and V2 as separate entities: BasePlugin.get_manifest() splits the classes returned from nodes() into two manifest lists (nodes and nodes_v2) by issubclass(n, BaseNodeV2). On the platform each kind is backed by its own generic executor, so there is no per-node synthesized class — the executor reads the component’s shape from the stored manifest and dispatches by (plugin, node_name).

Integrations and credentials

An integration manages authentication and credentials for an external service. You declare a BaseCredentials schema (its fields render in workspace settings) and a BaseIntegration; a node lists which credential types it needs and reads them at execution time. Credentials are the one part of the plugin model that is per-workspace — the platform stores and injects them scoped to the calling workspace.

Polling triggers

A trigger starts workflow runs from external events. A BasePollingTrigger answers one poll call on an interval and carries cursor state across polls; the platform keeps owning scheduling, state persistence, and event-to-run routing.

Knowledge-base data sources

A BaseDataSource feeds documents into a Noxus knowledge base. Its fetch returns Files (uploaded through the host callback), letting a KB ingest content from proprietary systems the built-in sources don’t cover.

The plugin class

The core of a plugin is one BasePlugin subclass that declares its components:

A V2 node

Bindable config fields become the manifest’s inputs; the rest is config. call returns a flat dict keyed by the output-schema field names (list[X] fields are list outputs). Configuration forms are rendered by the platform from your schema (the NCL config language) — no frontend code required.
New work should be V2. V1 nodes remain supported for existing V1 flows, but V2 is the model the platform builds around going forward.

The manifest is the contract

BasePlugin.get_manifest() reflects over your plugin class into a PluginManifest — the single artifact the platform depends on. It carries: The manifest is generated from code, not hand-written — regenerate it whenever a component signature or config changes:
The platform stores the manifest in the database and resolves components from it on demand (a read-through lookup), rather than eagerly registering synthesized classes at boot. The manifest kept after install is the one reflected from the live worker, so it always matches the code actually running.

How plugins run

Your code never runs inside a platform process. Each plugin gets its own sandbox — its own filesystem, its own uv virtual environment, its own resource limits — and the platform starts a warm worker (noxus plugin worker) in it that imports your module graph once and stays alive between calls. The platform reaches it over line-delimited JSON-RPC. A workflow hitting a plugin node dispatches node.execute (with the node name, a serialized execution context, and the config) to the worker; the result comes back over the same channel.

Files are host callbacks, not network calls

A jailed sandbox has no route back to internal platform addresses. When your node reads or writes a File, the SDK issues a JSON-RPC callback on the same channel (host.get_content / host.upload_file) that the platform services in-process against its own database and storage. No platform credential ever enters the sandbox, and each callback is scoped by a single-use token tied to the calling workspace — so a plugin cannot reach another tenant’s files. In call, use the execution context:

Isolation means

  • Your plugin can use any Python dependencies without conflicting with the platform.
  • A plugin crash or hang can’t take down the core platform.
  • Plugins reach platform resources (files, credentials) only through the controlled callback surface.

Sandbox providers and browser/JIT plugins

The sandbox provider is a deployment setting (SANDBOX_PROVIDER), but it matters when authoring. The default provider, syd, uses a seccomp filter that SIGSYS-kills V8’s JIT — so any browser or JIT-heavy workload (Playwright, Chromium, a Node driver) dies on startup, even when you only drive a remote browser and the local driver still needs to run. Those plugins must run under the gvisor provider (Linux/EKS) or none (local dev). Know this before shipping a browser-based plugin.

Deployment model

Plugins are deployment-global, not per-workspace: an org admin installs a plugin once and its components are available across the deployment. There is no per-workspace copy of a plugin. The only per-workspace piece is integration credentials — each workspace connects its own.

Runtime gotchas

The warm-worker sandbox imposes rules the local API doesn’t. Keep these in mind:
  • stdout is the protocol. The worker speaks JSON-RPC on stdout; a stray print corrupts a call. Send logs to stderr, and wrap chatty dependencies with contextlib.redirect_stdout(sys.stderr).
  • Import heavy deps lazily, inside call. Manifest generation imports your module in a lightweight environment; a top-level import pandas/playwright makes get_manifest() fail with ModuleNotFoundError even though the sandbox has the dependency.
  • Degrade, don’t raise, for expected external failures. A raised exception fails the whole run. Catch flaky network/parsing errors and return an empty typed result; reserve exceptions (IntegrationFailedError) for genuine misconfiguration.
  • Zip only source. Exclude .venv, __pycache__, .git from the package; a stray .venv can balloon the archive from KB to tens of MB.
  • Keep noxus-sdk>= aligned with the platform. The sandbox resolves noxus-sdk alongside your deps; pinning an old floor can make the worker fail to start.

SDK and CLI

The noxus-sdk package provides the authoring surface:
noxus plugin serve runs a plugin as a local FastAPI app for development, but file operations raise there — there is no local host to service the callbacks. Nodes that read or write platform files can only be exercised under the platform runtime.

Plugin lifecycle

1

Create

Scaffold with noxus plugin create, or set up the package structure by hand.
2

Develop

Implement nodes, integrations, triggers, and configuration.
3

Validate

Run noxus plugin validate to reflect the manifest and check your definitions.
4

Package & install

Push to Git or upload the .tar.gz, then install from the Noxus UI. The platform provisions the sandbox, installs your dependencies, and warms the worker.
5

Operate

Monitor status, view worker logs, and update by restarting with a new source version.

Your First Plugin

Step-by-step tutorial from zero to deployed plugin

Configurable Plugins

Plugin-level config, dynamic node config, and validation