You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This note frames PR #2704 as one concrete step toward a broader module deployment model. The goal is not only Python dependency isolation. The larger goal is to let DimOS keep a stable module identity while different implementations run locally, remotely, or as native processes.
Problem
DimOS has two parallel module families today:
Python modules run through Python workers and get the full DimOS surface: streams, RPCs, skills, module refs, lifecycle, and blueprint wiring.
Native modules run as external binaries wrapped by a Python NativeModule. The Python wrapper owns blueprint wiring, lifecycle, topic assignment, config serialization, logging, and process supervision. The native process owns computation and pub/sub.
Both families are moving toward the same deployment pressure:
Python modules need isolated dependency environments.
Native modules need repeatable build and runtime environments.
Remote deployment needs code or binary sync.
Weak robot computers may need cross-compilation or prepared artifacts.
Native runtimes may need dynamic libraries, Nix closures, Pixi environments, or transport-specific setup.
The common problem is module deployment.
Current proof point
PR #2704 introduces a narrow proof point for Python modules:
A blueprint can register a Python runtime project.
A module contract can be placed into that runtime.
Deployment reconciles the locked runtime project before launch.
WorkerManagerPython dispatches placed modules into runtime-specific Python worker pools.
The worker launches the prepared .venv/bin/python and imports the runtime implementation.
The coordinator still sees the dependency-light module contract.
That gives us one concrete backend for a broader model: the module contract stays stable while execution moves into a backend-specific runtime.
Native branches point in the same direction
Recent native-module branches show the same shape from another angle:
Andrew's cpp-native-module-stdin work makes native config and topics serializable through stdin instead of only CLI flags.
Andrew's Rust transport work adds a native transport abstraction, including Zenoh.
Lesh's MLS/RPP work uses Rust native modules with cargo build --release and stdin_config=True.
Those branches are not the same feature as PR #2704, but they point at the same seam: DimOS needs a shared way to describe what a module is, what it needs, where it runs, and how deployment prepares it.
A Self-Contained Module Package is the module-owned side of deployment. It carries the facts that should move with the module:
the dependency-light module contract
the Python implementation or native source/binary target
runtime and build declarations such as pyproject.toml, uv.lock, pixi.toml, flake.nix, Cargo.toml, or CMakeLists.txt
preparation recipes
launch recipe
optional smoke test
This should start as a layout convention, not a required manifest. Existing Python and native modules already have most of this structure.
Blueprint
A Blueprint should remain the ungrounded scaffold:
which modules exist
how streams and refs connect
what config surfaces exist
which module contracts participate in the stack
A Blueprint may contain portable deployment intent, but it should not hardcode one lab's SSH hosts, paths, credentials, or robot names.
Grounded Blueprint vs DeploymentSpec
The runnable binding needs a name. The current leaning is Grounded Blueprint rather than DeploymentSpec.
Reason:
Blueprint reads as an architectural scaffold.
A runnable stack needs that scaffold grounded into real targets, assignments, and preparation choices.
DeploymentSpec sounds like a separate ops artifact, while the current project already uses Python-configurable blueprints.
Open question: is Grounded Blueprint the right term, or should we keep DeploymentSpec for the lower-level resolved object?
Execution Target Profile
An Execution Target Profile is the machine-owned side of deployment. It should stay small.
It should include facts we cannot infer from the module package:
target identity
access kind: local, SSH, container, Kubernetes, etc.
architecture and OS family
deployment root and cache root
network identity and transport reachability
hard constraints such as read-only filesystem or no container runtime
It should not become an inventory of installable tools. If uv, pixi, nix, or cargo can be bootstrapped by the deployment workflow, the module package or preparation strategy should say how.
Preparation Strategy
Preparation Strategy is deployment-owned. It decides where and how to realize the module package's runtime requirement for a target.
Examples:
prepare directly on the execution target
sync source, then build remotely
build on the coordinator host, then sync artifacts
cross-compile on a builder, then sync the binary
build a Nix closure, then copy the closure
use a prebuilt artifact
Sync belongs inside the preparation strategy. Sync only makes sense relative to where preparation happens.
Stress-test cases
flowchart LR
subgraph Case1[Python module]
Pkg1[Python package\npyproject + uv/pixi/nix] --> Prep1[prepare on target]
Prep1 --> Run1[run Python implementation]
end
subgraph Case2[Normal native module]
Pkg2[Native package\nCargo / flake / pixi] --> Prep2[build on target]
Prep2 --> Run2[run native binary]
end
subgraph Case3[Weak target native module]
Pkg3[Native package] --> Prep3[cross-compile elsewhere]
Prep3 --> Sync3[sync binary]
Sync3 --> Run3[run on weak target]
end
subgraph Case4[Native binary plus runtime env]
Pkg4[Native package\nsource + runtime env] --> Prep4[cross-compile binary]
Pkg4 --> Env4[prepare/sync runtime closure]
Prep4 --> Run4[run binary]
Env4 --> Run4
end
Loading
These cases argue for three separate concerns:
the module-owned package and requirements
the target-owned machine profile
the deployment-owned assignment and preparation strategy
The broader model should let native modules and remote modules reuse the same shape without forcing them into Python runtime terminology.
Proposed direction
Start with Python APIs and conventions rather than a new declarative manifest.
Near term:
Blueprint
remains the Python-configurable module scaffold
Self-Contained Module Package convention
describes module-owned requirements and recipes
Grounded Blueprint? / DeploymentSpec?
binds blueprint modules to targets and preparation strategies
Deployment Reconciler
prepares, syncs, launches, and connects modules
Later:
dimos prepare <grounded-blueprint>
dimos run <grounded-blueprint>
For backward compatibility, a plain Blueprint can be implicitly grounded to the local target.
Open questions
Should the runnable binding be called Grounded Blueprint or DeploymentSpec?
How much portable deployment intent should remain on Blueprint itself?
What is the smallest useful Execution Target Profile?
Should each Self-Contained Module Package eventually get a manifest, or should layout conventions be enough?
How should a Deployment Reconciler report the resolved plan before mutating remote machines?
What native capabilities need the same contract boundary: config, transport descriptors, RPC/control plane, TF, logging, health?
Suggested narrative
Use this PR as a proof point, not as the whole story:
PR #2704 adds local Python runtime projects. More importantly, it introduces a seam between what DimOS sees and where a module implementation runs. That seam can grow into a shared deployment model for Python modules, native modules, and remote execution.
Status: draft for review.
This is a proposal/RFC issue.
This note frames PR #2704 as one concrete step toward a broader module deployment model. The goal is not only Python dependency isolation. The larger goal is to let DimOS keep a stable module identity while different implementations run locally, remotely, or as native processes.
Problem
DimOS has two parallel module families today:
NativeModule. The Python wrapper owns blueprint wiring, lifecycle, topic assignment, config serialization, logging, and process supervision. The native process owns computation and pub/sub.Both families are moving toward the same deployment pressure:
The common problem is module deployment.
Current proof point
PR #2704 introduces a narrow proof point for Python modules:
WorkerManagerPythondispatches placed modules into runtime-specific Python worker pools..venv/bin/pythonand imports the runtime implementation.That gives us one concrete backend for a broader model: the module contract stays stable while execution moves into a backend-specific runtime.
Native branches point in the same direction
Recent native-module branches show the same shape from another angle:
cpp-native-module-stdinwork makes native config and topics serializable through stdin instead of only CLI flags.cargo build --releaseandstdin_config=True.Those branches are not the same feature as PR #2704, but they point at the same seam: DimOS needs a shared way to describe what a module is, what it needs, where it runs, and how deployment prepares it.
Core concepts
flowchart TD Package[Self-Contained Module Package] Contract[Module Contract] Impl[Implementation] Requirement[Runtime Requirement] Recipe[Preparation / Launch Recipes] Blueprint[Blueprint\nungrounded scaffold] Grounded[Grounded Blueprint?\nrunnable binding] Target[Execution Target Profile] Assignment[Deployment Assignment] Strategy[Preparation Strategy] Reconciler[Deployment Reconciler] Package --> Contract Package --> Impl Package --> Requirement Package --> Recipe Blueprint --> Grounded Package --> Grounded Target --> Grounded Assignment --> Grounded Strategy --> Grounded Grounded --> Reconciler Reconciler --> Prepare[prepare / build] Reconciler --> Sync[sync code, binary, or closure] Reconciler --> Launch[launch process] Reconciler --> Connect[connect streams, RPC, transports]Self-Contained Module Package
A Self-Contained Module Package is the module-owned side of deployment. It carries the facts that should move with the module:
pyproject.toml,uv.lock,pixi.toml,flake.nix,Cargo.toml, orCMakeLists.txtThis should start as a layout convention, not a required manifest. Existing Python and native modules already have most of this structure.
Blueprint
A Blueprint should remain the ungrounded scaffold:
A Blueprint may contain portable deployment intent, but it should not hardcode one lab's SSH hosts, paths, credentials, or robot names.
Grounded Blueprint vs DeploymentSpec
The runnable binding needs a name. The current leaning is Grounded Blueprint rather than
DeploymentSpec.Reason:
Blueprintreads as an architectural scaffold.DeploymentSpecsounds like a separate ops artifact, while the current project already uses Python-configurable blueprints.Open question: is Grounded Blueprint the right term, or should we keep
DeploymentSpecfor the lower-level resolved object?Execution Target Profile
An Execution Target Profile is the machine-owned side of deployment. It should stay small.
It should include facts we cannot infer from the module package:
It should not become an inventory of installable tools. If
uv,pixi,nix, orcargocan be bootstrapped by the deployment workflow, the module package or preparation strategy should say how.Preparation Strategy
Preparation Strategy is deployment-owned. It decides where and how to realize the module package's runtime requirement for a target.
Examples:
Sync belongs inside the preparation strategy. Sync only makes sense relative to where preparation happens.
Stress-test cases
flowchart LR subgraph Case1[Python module] Pkg1[Python package\npyproject + uv/pixi/nix] --> Prep1[prepare on target] Prep1 --> Run1[run Python implementation] end subgraph Case2[Normal native module] Pkg2[Native package\nCargo / flake / pixi] --> Prep2[build on target] Prep2 --> Run2[run native binary] end subgraph Case3[Weak target native module] Pkg3[Native package] --> Prep3[cross-compile elsewhere] Prep3 --> Sync3[sync binary] Sync3 --> Run3[run on weak target] end subgraph Case4[Native binary plus runtime env] Pkg4[Native package\nsource + runtime env] --> Prep4[cross-compile binary] Pkg4 --> Env4[prepare/sync runtime closure] Prep4 --> Run4[run binary] Env4 --> Run4 endThese cases argue for three separate concerns:
How this maps to current PR #2704
flowchart TD Contract[Module Contract\ncoordinator-visible identity] RuntimeImpl[Runtime Implementation\nsubclasses contract] RuntimeProject[Python Runtime Project\npyproject.toml + uv.lock] Placement[RuntimePlacement\ncontract -> runtime + implementation] Blueprint[Blueprint] Manager[WorkerManagerPython] Pool[Runtime Python Worker Pool] Launcher[CommandWorkerLauncher] Venv[prepared .venv/bin/python] Worker[python_worker._worker_entrypoint] Contract --> Blueprint RuntimeImpl --> RuntimeProject RuntimeProject --> Placement Placement --> Blueprint Blueprint --> Manager Manager --> Pool Pool --> Launcher RuntimeProject --> Venv Venv --> Launcher Launcher --> Worker Worker --> RuntimeImpl Contract -. RPC / streams / refs .-> ManagerThe PR implements one local preparation strategy:
The broader model should let native modules and remote modules reuse the same shape without forcing them into Python runtime terminology.
Proposed direction
Start with Python APIs and conventions rather than a new declarative manifest.
Near term:
Later:
For backward compatibility, a plain Blueprint can be implicitly grounded to the local target.
Open questions
DeploymentSpec?Blueprintitself?Suggested narrative
Use this PR as a proof point, not as the whole story:
Links
uv run python examples/dimos-demo-worker-module/demo_run_blueprint.pySynced from DIM-1123