dimensionalOS / dimensionalOS/dimos

Proposal: Module deployment as Grounded Blueprints

Open
#2,758 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

core spec
Dominant language
Python
Stars
4.5k
Forks
808
Avg merge
3d 5h
Merged PRs (30d)
233

Description

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:

  • 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.

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:

  • 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

These cases argue for three separate concerns:

  1. the module-owned package and requirements
  2. the target-owned machine profile
  3. the deployment-owned assignment and preparation strategy

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 .-> Manager

The PR implements one local preparation strategy:

Python runtime project + prepare locally + launch prepared .venv Python

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

  1. Should the runnable binding be called Grounded Blueprint or DeploymentSpec?
  2. How much portable deployment intent should remain on Blueprint itself?
  3. What is the smallest useful Execution Target Profile?
  4. Should each Self-Contained Module Package eventually get a manifest, or should layout conventions be enough?
  5. How should a Deployment Reconciler report the resolved plan before mutating remote machines?
  6. 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.

Links

  • PR #2704: dimensionalOS/dimos#2704
  • Demo command: uv run python examples/dimos-demo-worker-module/demo_run_blueprint.py

Synced from DIM-1123 by che

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading PR #2704 and its Python runtime placement flow, then run the demo command uv run python examples/dimos-demo-worker-module/demo_run_blueprint.py to understand the current proof point. The work is not yet defined as an implementation task; it would need a decided deployment model, terminology, boundaries, and an agreed definition of done for the open questions.

Written by the indexing model from the issue text.

Assessment

Tech stack
cmake, kubernetes, python, rust
Domain
devops, infrastructure
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.