On this page

Dependencies, isolation and runtimes

Where a module's code runs, and which libraries it can import.

Declaring dependencies#

dependencies block
dependencies:
  python:
    requires-python: ">=3.12"
    packages: ["scikit-learn>=1.5"]   # private to your module, installed into .venv
    inherit: [pm4py, pandas, numpy]   # reuse the platform's installed copies
    isolation: in_process             # in_process (default) | subprocess
    execution: thread                 # thread (default) | worker
  npm: ["d3-sankey@^0.12"]            # bundled with your frontend
FieldMeaning
packagesInstalled into modules/<folder>/.venv; other modules cannot see them.
inheritNames the platform already ships — avoids reinstalling hundreds of megabytes per module. A package may not appear in both lists.
requires-pythonA validation gate for in_process, an interpreter selector for subprocess.
executionIn-process only: thread (fast, cooperative cancel) or worker (killable child process).

Each environment is hashed: unchanged dependencies are skipped on boot, and for in_process modules the platform's major.minor Python version is part of the hash, so an interpreter upgrade rebuilds automatically. A private import namespace puts your .venv/site-packages first, so your versions win and other modules' packages stay invisible.

Choosing an isolation mode#

SituationMode
Pure Python, finishes quicklyin_process + thread (default).
Long native call that ignores cancellationin_process + execution: worker.
Another Python version, or a native-library conflictisolation: subprocess.
A Java libraryruntime: { kind: jvm }.
A method that needs its own serverA sidecar service.

Subprocess modules run in a long-lived worker on their own interpreter, with every ctx call proxied over a Unix-socket JSON-RPC connection and DataFrames handed over as Parquet. Each context touch costs roughly 1–50 ms, so batch: one aggregated duckdb_fetch beats a thousand calls. Cancellation is a three-second soft window followed by SIGKILL of the process group; a crashed worker respawns with exponential backoff, and a crash loop becomes a terminal error rather than a hot loop.

JVM modules ship as one self-contained fat jar with a Main-Class and speak the same protocol, so the SDK surface is equivalent:

A JVM module entry point
public static void main(String[] argv) {
    MateModule.builder("my_jvm_module")
        .onEventJob("precompute", "log.imported",
            JobSpec.of().progress(true).cancellable(true).title("My JVM analysis"),
            MyImpl::precompute)
        .route("get_ranking", RouteSpec.get("/ranking"), MyImpl::getRanking)
        .build()
        .run(argv);   // connects, handshakes, serves - never returns
}

The context offers eventLog() (with duckdbFetch running host-side and materialize() returning a Parquet path), cache(), bus(), registry(), progress(), logger(), config(), workdir(), and checkCancelled(). Build with make sdk-jvm; modules/performance_java is the reference. There is no server-side Maven or Gradle resolution — the jar must bundle everything but the JRE.

Sidecar services (a graph database, a search engine, a solver) follow a fixed contract: declared under an optional compose profile the operator starts, bound to loopback, addressed through a config → environment → default chain, probed with a health check that produces an actionable error, wiped before and after each run, and never treated as a store — results go to ctx.cache. Full rules: modules/SIDECAR_SERVICES.md.