Skip to content

Plugin Registry

Masterplanner25 edited this page Jun 6, 2026 · 1 revision

Plugin Registry

aindy-runtime's plugin registry lets you extend the runtime with domain-specific routers, flows, jobs, syscalls, capabilities, tools, and startup hooks — all from an external Python package. This is how the 16 domain apps in aindy-apps-monolith are mounted at boot time.

Plugin Discovery

Plugins are discovered from aindy_plugins.json at the root of the deployment:

{
  "profile": "default-apps",
  "plugins": [
    {"package": "aindy_sales", "enabled": true},
    {"package": "aindy_analytics", "enabled": true}
  ]
}

Set AINDY_BOOT_MODE=runtime-only to skip plugin loading entirely and run the runtime without any app layer.

Plugin Entry Point

A plugin package must expose an aindy_plugin entry point in pyproject.toml:

[project.entry-points."aindy.plugins"]
aindy_sales = "aindy_sales.plugin:register"

The register function receives a PluginRegistry and calls the registration APIs.

Registration APIs

register_router

Mount a FastAPI router at the /apps/ prefix:

from AINDY.platform_layer.registry import register_router

def register(registry):
    from aindy_sales.routes import router
    register_router(router, prefix="/apps/sales", tags=["sales"])

register_flow

Register a named flow definition:

from AINDY.platform_layer.registry import register_flow

def register(registry):
    from aindy_sales.flows import quarterly_report_flow
    register_flow("sales.quarterly_report", quarterly_report_flow)

register_job

Register a named background job (appears in the distributed queue):

from AINDY.platform_layer.registry import register_job

def register(registry):
    from aindy_sales.jobs import sync_crm_job
    register_job("sales.sync_crm", sync_crm_job, schedule="0 */6 * * *")

register_syscall

Register a new syscall handler:

from AINDY.platform_layer.registry import register_syscall

def register(registry):
    from aindy_sales.syscalls import get_pipeline_handler
    register_syscall(
        name="sys.v1.sales.get_pipeline",
        handler=get_pipeline_handler,
        capabilities=["read_sales"],
        idempotency="AT_LEAST_ONCE",
    )

register_startup_hook

Run code after the runtime finishes startup:

from AINDY.platform_layer.registry import register_startup_hook

def register(registry):
    def on_startup(context):
        context["log"].info("Sales plugin ready")
    register_startup_hook(on_startup)

Plugin Enrichment for Agents

Plugins can enrich agent runs in four ways:

Enrichment type What it does
planner_context Injects domain context into the agent's planning prompt
suggestions Provides suggested next actions based on current state
tool_catalog Adds domain-specific tools to the agent's tool list
completion_hook Runs after the agent completes (for post-run orchestration)
from AINDY.platform_layer.registry import register_planner_context_provider

def register(registry):
    from aindy_sales.enrichment import sales_planner_context
    register_planner_context_provider(sales_planner_context)

Execution Sandbox

Third-party plugin nodes run through the extension sandbox. In single-instance mode, the default runner is insecure_dev_subprocess — suitable for first-party trusted code only. For production deployments with third-party plugins, configure:

AINDY_PLUGIN_SANDBOX_RUNNER=containerized_oci
AINDY_PLUGIN_CONTAINER_IMAGE=your-sandbox-image:latest

See Security for the full sandbox policy.

Import Boundary

Plugin code may import from AINDY.* runtime modules (routers, services, syscall dispatch). The reverse is explicitly blocked — AINDY/ modules must not import from apps.*. This boundary is enforced at startup via the router boundary validator.

Clone this wiki locally