nodus-extensions
Typed, versioned, sandboxed extension loading for Nodus AI systems.
Provides ABI versioning constants, a hook lifecycle runner, subprocess and OCI sandbox runners, disk-discovery extension registry, and a provenance inventory. No required external dependencies — pure stdlib.
Note on naming: This package (
nodus-extensions, plural) is distinct fromnodus-extension(singular).nodus-extensionis the nodus-lang companion that provides.ndscript bindings (import "nodus-extension"). This package is a standalone Tier 4 library for building extension-hosting platforms in Python.
Status: v0.1.0 — prepared, not yet published.
Install
pip install nodus-extensions
What it provides
| Component | Purpose |
|---|---|
ExtensionManifest |
Typed extension declaration (name, version, ABI, surfaces) |
ExtensionRegistry |
Disk-discovery registry: discover(), load(), get(), list_all() |
HookRunner |
Phase hook lifecycle: register, unregister, run |
SubprocessSandboxRunner |
Insecure-dev tier: runs extensions as child processes |
OciSandboxRunner |
Container-grade tier: runs extensions via Docker |
make_runner(tier) |
Factory for the appropriate sandbox runner |
ExtensionProvenance / ProvenanceInventory |
Source, trust class, load time tracking |
ExtensionManifest
from nodus_extensions import (
ExtensionManifest, SandboxTier,
MANIFEST_ABI_V1, AGENT_TOOL_REGISTRATION_ABI_V1ALPHA1,
SURFACE_AGENT_TOOL, OWNER_EXTERNAL_THIRD_PARTY,
)
manifest = ExtensionManifest(
name="myapp.greet-extension",
version="1.0.0",
description="Greets users",
abi_version=MANIFEST_ABI_V1,
sandbox_tier=SandboxTier.INSECURE_DEV,
surfaces=[SURFACE_AGENT_TOOL],
capabilities=["tool.invoke"],
)
ABI version constants
| Constant | Value | Surface |
|---|---|---|
MANIFEST_ABI_V1 |
"v1" |
Manifest schema |
NODE_REGISTRATION_ABI_V1ALPHA1 |
"v1alpha1" |
Dynamic nodes |
WEBHOOK_REGISTRATION_ABI_V1ALPHA1 |
"v1alpha1" |
Webhooks |
FLOW_REGISTRATION_ABI_V1ALPHA1 |
"v1alpha1" |
Flows |
AGENT_TOOL_REGISTRATION_ABI_V1ALPHA1 |
"v1alpha1" |
Agent tools |
PLANNER_BACKEND_REGISTRATION_ABI_V1ALPHA1 |
"v1alpha1" |
Planner backends |
ExtensionRegistry
from nodus_extensions import ExtensionRegistry
registry = ExtensionRegistry()
registry.discover("/path/to/extensions/") # scans for nodus-extension.json files
registry.load("/path/to/specific-ext/") # load one extension dir
ext = registry.get("myapp.greet-extension") # ExtensionManifest | None
all_exts = registry.list_all() # list[ExtensionManifest]
registry.unload("myapp.greet-extension")
HookRunner
from nodus_extensions import (
HookRunner,
PHASE_INIT, PHASE_BEFORE_AGENT_START,
PHASE_BEFORE_MODEL_RESOLVE, PHASE_AFTER_AGENT_END, PHASE_SHUTDOWN,
)
runner = HookRunner()
def my_init_hook(params: dict) -> None:
print("Initialising:", params)
runner.register(PHASE_INIT, "my-hook", my_init_hook)
await runner.run(PHASE_INIT, {"context": "startup"})
runner.unregister(PHASE_INIT, "my-hook")
Hooks are async-aware — sync hooks are called directly; async hooks are
awaited. All hooks for a phase run in registration order.
Sandbox runners
from nodus_extensions import SubprocessSandboxRunner, OciSandboxRunner, make_runner, SandboxTier
# Subprocess (insecure-dev — same OS user as host)
runner = SubprocessSandboxRunner()
result = runner.run("python3 extension.py", input_data={"tool": "greet", "args": {}})
# result.ok, result.stdout, result.stderr, result.returncode, result.output
# OCI container (Docker required)
runner = OciSandboxRunner(image="myapp/greet-extension:1.0")
result = runner.run("python3 extension.py", input_data={...})
# Factory
runner = make_runner(SandboxTier.INSECURE_DEV)
runner = make_runner(SandboxTier.CONTAINER)
Provenance
from nodus_extensions import ExtensionProvenance, ProvenanceInventory, derive_trust_class
inventory = ProvenanceInventory()
prov = ExtensionProvenance(
name="myapp.greet-extension",
source="local",
owner_class="personal",
trust_class=derive_trust_class("personal"),
)
inventory.record(prov)
all_prov = inventory.list_all()
entry = inventory.get("myapp.greet-extension")
Design
- No required dependencies. Pure stdlib (
asyncio,threading,subprocess,importlib,pathlib,json,logging). - Thread-safe.
ExtensionRegistryandProvenanceInventoryusethreading.Lock. - Sandbox tiers are explicit.
SandboxTier.INSECURE_DEV(subprocess) andSandboxTier.CONTAINER(OCI) are named constants, not flags.
Development
pip install -e ".[dev]"
pytest tests/ -q
License
MIT — see LICENSE.
Metadata
Release files for nodus-extensions 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nodus_extensions-0.1.0.tar.gz | 15.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nodus_extensions-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 28.9 kB
Release files / nodus_extensions-0.1.0.tar.gz
| Download URL | nodus_extensions-0.1.0.tar.gz |
|---|---|
| Size | 15.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
86bfe6ce1ff97a2dffa168ca86ec5138fe654a05ccfb00eda74914c3557615ff
|
|
BLAKE2b-256 checksum How to use checksums |
bbf34f65d4034ca34aa6f6990a85e2b43327e80eeaa015a080a99bec17bf63ba
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.9
|
Release files / nodus_extensions-0.1.0-py3-none-any.whl
| Download URL | nodus_extensions-0.1.0-py3-none-any.whl |
|---|---|
| Size | 13.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ce0f0b3d3551cc6c8d1d487c8cffa843f22ece341dfd423c74566f75e6e3dd24
|
|
BLAKE2b-256 checksum How to use checksums |
c307050044f4285a873649999db5bc5e53b3c813c78d66bf829b1e5f2f0fe1d3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.9
|