Skip to main content

Plexus

Last updated for Plexus 0.81.1

An async Python plugin framework with multi-node mTLS networking and pub/sub event routing. PyPI package: plexus-core.

Plexus loads small, single-responsibility Python classes — plugins — from disk, drives a deterministic on_load / on_enable / on_disable lifecycle, and gives them three ways to talk to each other:

  • direct method calls — await self.execute("OtherPlugin", "method", args=...)
  • 1:N events — await self.publish_event("event_id", payload=...)
  • 1:1 ask-by-topic — result = await self.request_event("event_id", payload=...)

The runtime can stretch across multiple nodes over an mTLS-pinned wire protocol (NetworkManager), so the same execute / publish_event / request_event calls transparently fan out to peer machines whose plugins are flagged remote: true.

A typical deployment in conversational AI, data-pipeline, or event-driven domains wires together base plugins (a Discord bot, a Postgres adapter, an LLM adapter, a TTS pipeline) with orchestrator plugins that hold the business logic — each one a class plus a YAML manifest.


Highlights

  • One-class-per-plugin layout. A plugin.py plus a declarative plugin_config.yml describing endpoints, events, and subscriptions.
  • Three call styles. Direct endpoint calls (execute), 1:N fire-and-forget events (publish_event), and 1:1 topic-routed requests with optional streaming (request_event, request_event_stream).
  • Hot-swap reloading. Any plugin can be disabled, re-instantiated from config, and re-enabled at runtime — its sockets, background tasks, and event subscriptions tear down and rebuild on a fresh class instance with no downtime for the rest of the cluster.
  • Three-tier discipline (project policy). Base plugins wrap one protocol or service; Extension plugins glue bases together; Orchestrator plugins hold business logic.
  • Multi-node clustering. Pinned-fingerprint mTLS between peers; remote endpoints and remote subscribers are reachable through the same Plugin base methods that drive local calls.
  • Sync and async surfaces. Every cross-plugin call has both an async form and a sync form that bridges to the loop, so plugins written against blocking libraries do not have to twist themselves into coroutines.
  • Strict but small. Around fifteen public methods on the Plugin base class; everything else is YAML.

Install

Requires Python 3.11+.

From PyPI (recommended for using Plexus as a library):

pip install plexus-core

Optional: add [fastloop] for a faster event loop (uvloop on Linux/macOS, winloop on Windows). Purely a performance opt-in; everything works without it.

pip install plexus-core[fastloop]

From source (for developing on the framework itself):

git clone https://github.com/Haflix/Plexus.git
cd Plexus
python -m venv .venv
.venv/Scripts/activate          # Windows
# source .venv/bin/activate     # Linux / macOS
pip install -e .
cp config.example.yml config.yml

config.yml is gitignored. Edit it to enable the plugins you want.


Quickstart

A plugin lives in its own folder containing two required files: plugin.py (the class) and plugin_config.yml (declarative metadata). It may ship further modules or sub-packages alongside them; the loader puts the plugin directory on sys.path so plugin.py can import them.

Here is a minimal plugin showing the moving parts: lifecycle hooks, a regular endpoint, a streaming endpoint, a cross-plugin call, and a topic-subscribed endpoint. The shipped copypasta/ folder has a fuller, runnable version of these patterns (a two-plugin demo that also covers rate limiting and capabilities); run it with python copypasta/run_demo.py and see copypasta/README.md.

A minimal plugin.py

from plexus.utils import Plugin
from plexus.decorators import (
    log_errors, async_log_errors,
    async_handle_errors, async_gen_log_errors,
)
import asyncio


class AveragePlugin(Plugin):
    @log_errors
    def on_load(self, *args, **kwargs):
        self._logger.debug("AveragePlugin loaded")
        self.state = {}

    @async_log_errors
    async def on_enable(self):
        self._logger.debug("AveragePlugin enabled")

    @async_log_errors
    async def on_disable(self):
        self._logger.debug("AveragePlugin disabled")

    @async_log_errors
    async def example_method(self, value):
        return value * 2

    @async_handle_errors(default_return=None)
    async def call_other_plugin(self, plugin_name, method_name, args):
        return await self.execute(plugin_name, method_name, args, hosts="any")

    @async_log_errors
    async def handle_event(self, event):
        # event.topic, event.payload, event.author, event.author_host
        return {"received": event.payload, "handled_by": self.plugin_name}

    @async_gen_log_errors
    async def example_stream(self, count):
        for i in range(count):
            await asyncio.sleep(0.1)
            yield f"Item {i + 1} of {count}"

The matching plugin_config.yml

description: Example plugin demonstrating the Plexus plugin structure
version: 1.0.0
remote: True
arguments:

subscriptions:
  handle_event_sub:
    topic: "example/event"
    target_access_name: handle_event
    hosts: "local"

endpoints:
  example_method:
    internal_name: example_method
    remote: True
    accessible_by_other_plugins: True
    description: Doubles the input value.
    arguments:
      - name: value
        type: any
        description: Input value to process

  call_other_plugin:
    internal_name: call_other_plugin
    remote: True
    accessible_by_other_plugins: True
    description: Call another plugin's method by name and return its result.
    arguments:
      - name: plugin_name
        type: str
        description: Name of the plugin to call
      - name: method_name
        type: str
        description: Method to execute on the target plugin
      - name: args
        type: any
        description: Arguments to pass to the method

  example_stream:
    internal_name: example_stream
    remote: True
    accessible_by_other_plugins: True
    description: Streaming method that yields sequential results.
    arguments:
      - name: count
        type: int
        description: Number of items to yield

  handle_event:
    internal_name: handle_event
    remote: True
    accessible_by_other_plugins: True
    description: Endpoint subscribed to "example/event" via the subscriptions block.
    arguments:
      - name: event
        type: any
        description: Event object received from the topic

Run it

The copypasta/ folder ships a runnable version of these patterns. From the repo root:

python copypasta/run_demo.py

It registers two plugins (SensorPlugin + AveragePlugin), wires them together, and drives a scripted scenario: readings published as events flow into a running average, an on-demand cross-plugin execute(), a capability assertion, and the rate limiter rejecting once a bucket is dry.

To register a plugin in your own app, add it to config.yml and launch main_application.py:

plugins:
  - name: YourPlugin
    enabled: true
    path: ./path/to/YourPlugin

From any other plugin you can then call an endpoint, or fire an event the plugin declares:

result = await self.execute("YourPlugin", "some_method", args=...)

count = await self.publish_event("some_event", payload={"hello": "world"})
# count == number of subscribers the dispatch was scheduled for

That is the entire surface to write something useful. The rest is just more endpoints, more events, more subscriptions.


Documentation

File Audience Focus
docs/architecture.md new readers mental model, lifecycle, three-tier discipline, hot-swap, shutdown order
docs/plugin_authoring.md plugin authors full plugin_config.yml schema and a tutorial-style walkthrough
docs/api_reference.md reference users every Plugin-base method (signature, args, raises) plus decorators
docs/notifier.md event-system users publish vs request, topic templating, filter chain, runtime subs
docs/networking.md multi-node operators mTLS, peers, cert pinning, remote semantics, wire protocol
docs/configuration.md operators full config.yml reference and per-plugin overrides
docs/rate_limiting.md operators the token-bucket rate limiter: dimensions, rate_limits: config, observability
docs/capabilities.md operators caller identity + the capabilities: impersonation / system-caller grant model

Status

Active development. Public API of the Plugin base class is stable; internal Plexus helpers (_* prefix) are not. See the wire-protocol table in docs/networking.md for cross-version compatibility.

Acknowledgements

The early networking and streaming work (0.6.0 through 0.6.5, mid-2025) was a collaboration with 1ckyDev, who wrote the first structure of the networking layer. Development has been solo since.

License

See LICENSE.

Release files for plexus-core 0.81.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for plexus-core 0.81.1
File Size Uploaded
plexus_core-0.81.1.tar.gz 301.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for plexus-core 0.81.1
File Interpreter ABI Platform
plexus_core-0.81.1-py3-none-any.whl Python 3 none any Details

Total release size: 617.6 kB

Release files / plexus_core-0.81.1.tar.gz

Download URL plexus_core-0.81.1.tar.gz
Size 301.7 kB
Tags Source
SHA-256 checksum
How to use checksums
bc714905445952172043a1602f9dc4cf6ae34f266b92b27989401f183560e5d3
BLAKE2b-256 checksum
How to use checksums
f57e3fba1445c95f865d929bbbafe5c1f4f39ca32e36642a698534126835017f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release files / plexus_core-0.81.1-py3-none-any.whl

Download URL plexus_core-0.81.1-py3-none-any.whl
Size 315.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
99b452f92e767c58382fb42454c2e92b151ab00038caa4a577d0c920227ad414
BLAKE2b-256 checksum
How to use checksums
f992283dc60f78874e7ee9cdeb21efd8f3aa32198bd40753508b86de599f3a95
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 17, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.81.1 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page