Download the latest binary for your platform from GitHub Releases:
# macOS (Apple Silicon)
curl -Lo molt https://github.com/olivierdevelops/molt-python/releases/latest/download/molt-darwin-arm64
chmod +x molt && sudo mv molt /usr/local/bin/
# macOS (Intel)
curl -Lo molt https://github.com/olivierdevelops/molt-python/releases/latest/download/molt-darwin-amd64
chmod +x molt && sudo mv molt /usr/local/bin/
# Linux (x86-64)
curl -Lo molt https://github.com/olivierdevelops/molt-python/releases/latest/download/molt-linux-amd64
chmod +x molt && sudo mv molt /usr/local/bin/
# Linux (ARM64)
curl -Lo molt https://github.com/olivierdevelops/molt-python/releases/latest/download/molt-linux-arm64
chmod +x molt && sudo mv molt /usr/local/bin/
# Verify checksum (optional)
curl -Lo molt.sha256 https://github.com/olivierdevelops/molt-python/releases/latest/download/molt-darwin-arm64.sha256
sha256sum -c molt.sha256From source (requires Go 1.22+):
git clone https://github.com/olivierdevelops/molt-python.git
cd molt-python && go build -o molt . && sudo mv molt /usr/local/bin/A complete, opinionated tour of every feature molt ships, with examples
and rationale. This is the document you read once to know what molt
does; for command-by-command precision use docs/MANUAL.md;
for runnable example projects see demos/.
- What molt is and why it exists
- The core model: hermetic projects, shared store
- Quick start
- Project lifecycle
- Dependency management
- The global package store
- Python version management
- Tasks
- Templates
- Multi-project tracking
- Tool registry: global CLI shims
- Native modules — overview
- Cython modules
- Rust + PyO3 modules
- Kernel modules: any C-ABI language
- Kernel builders: per-language compile recipes
- Pre-built
.sobinding [[tool.molt.native]]— external module recipes- Runtime environment:
extra_paths - Environment variables
- Build & distribution: single-binary release
- Integrity & verification
- Editor integration
- Diagnostics
- Why this matters: developers and enterprise
- Comparison to other tools
- Roadmap
molt is a hermetic Python toolchain. It manages Python versions, dependencies, native code compilation, runtime environments, and binary distribution under a single CLI, with three guiding principles:
-
Share what's shareable. Packages live once on disk in
~/.molt/pkg/, indexed by(name, version, abi-tag)and reused across every project. Two projects depending onnumpy 1.26.4forcp312-darwin-arm64get the same files; the second project pays zero install cost. -
Isolate what's project-specific. A project's environment is a single small JSON file (
.molt/syspath.json) that lists the store paths to put onPYTHONPATH. There's no per-project.venv, no activation, nosource—molt runjust sets the env and execs. -
Native code is a first-class citizen — in both directions. molt has six distinct pipelines for native integration:
- Header-driven C extensions (
[[tool.molt.c.modules]]): point at a.hfile and molt auto-parses the function signatures, generates the entire CPythonPyInit_/PyMethodDefglue, and compiles a real.soviazig cc— no Cython, no cffi, no manual binding. - Cython (
.pyx): hot loops and C-API integration with Python syntax. - Rust + PyO3 (
.rs): type-safe complex APIs with full async support. - Kernel modules (
.molt.toml+ any C-ABI source): Zig, C, C++, Odin, Nim, Assembly — drop a file, get a Python module. - Pre-built kernels: bind vendor
.solibraries with a manifest only. - External native recipes (
[[tool.molt.native]]): wrap an existing Cargo / CMake / autotools project.
The relationship is bidirectional: Python calling C extensions, but also C and Mojo code calling back into Python packages via the project's
PYTHONPATH—PyImport_ImportModule("numpy")in C, orPython.import_module("numpy")in Mojo, and the packages are there. - Header-driven C extensions (
The implication: the same molt project source supports the entire
lifecycle from molt init (start a new project) through
molt run (develop and execute) through molt build (produce
a single self-installing binary that runs on any matching machine
without Python installed).
~/.molt/ ← one global location
├── python/3.12.3/ ← Python interpreters managed by uv
├── python/3.13.1/
├── pkg/ ← shared package store
│ ├── numpy/1.26.4/cp312-darwin-arm64/ ← unpacked once, used everywhere
│ ├── numpy/1.26.4/cp313-darwin-arm64/
│ ├── requests/2.32.3/py3-none-any/ ← pure-Python: same files for all ABIs
│ └── ...
├── native/<hash>/<module>.so ← compiled native extensions, content-keyed
├── toolchains/zig/0.14.1/zig ← auto-installed compiler toolchains
├── projects/myapp-abc123/ ← per-project state mirrored centrally
├── kernel-builders.yaml ← per-extension kernel build recipes
├── native-presets.yaml ← named [[tool.molt.native]] presets
└── bin/ ← global tool shims (~/.molt/bin on PATH)
myproject/ ← one per project
├── pyproject.toml ← stays unchanged; standard PEP 621 metadata
├── uv.lock ← deterministic dependency resolution
└── .molt/
├── syspath.json ← {"interpreter": "...", "syspath": [".../numpy/...", ...]}
├── sitecustomize.py ← processes .pth files in each store dir
└── bin/<console-scripts> ← ruff, pytest, etc. — absolute paths baked in
Two facts follow:
- A
molt syncis a graph operation, not a download fest. It readsuv.lock, looks up every package in~/.molt/pkg/, downloads missing ones once, and writes a 30-line JSON file. Adding a fifth FastAPI project to your machine takes seconds; the wheels are already there. - Switching Python versions is fast.
molt python use 3.13re-resolves the syspath against the new ABI and writes a new syspath.json. No venv to recreate.
$ molt init myapp
$ cd myapp
$ molt add fastapi uvicorn[standard]
$ molt run python -c 'import fastapi; print(fastapi.__version__)'
0.115.0
$ molt task add dev "uvicorn myapp.main:app --reload"
$ molt run devThat's everything. No venv created, no shell to activate, no PATH
manipulation. molt run dev execs uvicorn from ~/.molt/bin with
the project's syspath set as env, and exits when uvicorn does.
| Command | What it does |
|---|---|
molt init [name] |
Scaffolds a new project via uv init. Optional --template to apply a saved template. |
molt init --python 3.13 myapp |
Pin Python version at creation. |
molt adopt |
Generate molt.yaml for an existing project that already has pyproject.toml. |
molt sync |
Materialise uv.lock into ~/.molt/pkg/, write .molt/syspath.json. |
molt sync --frozen |
Use existing lock as-is, no resolution. |
molt sync --refresh |
Force-reinstall every package, even those in cache. |
molt info |
Project + environment summary. |
molt sync is the spine. Almost every other command implicitly runs
it when needed. Manual invocation matters mostly when you want to
re-resolve after editing pyproject.toml outside molt, or in CI.
molt delegates resolution to uv —
which is fast, correct, and the same code path that materialises the
store. Adding deps is a thin wrapper:
$ molt add pydantic 'sqlalchemy>=2.0' pytest --dev
$ molt remove pydantic
$ molt lock # regenerate uv.lock without installing
$ molt tree # print the resolved dep graph
$ molt uv pip list # pass-through to uv for advanced casesmolt add does three things:
- Edits
pyproject.toml(delegates touv add). - Re-runs
uv lock. - Re-syncs into
~/.molt/pkg/and updates.molt/syspath.json.
You can do those steps manually with molt uv if you need fine-grained
control.
Wheels go in once per (name, version, abi). The hash key is the
ABI tag computed from your chosen Python — so cp311-darwin-arm64
and cp313-darwin-arm64 get separate copies of numpy, but requests
(py3-none-any) is one set of files shared across every project on
the machine, regardless of Python version.
~/.molt/pkg/
└── numpy/
└── 1.26.4/
└── cp312-cp312-macosx_11_0_arm64/
├── numpy/ ← the actual package
├── numpy-1.26.4.dist-info/
└── .molt/installed.json ← version, files, dist-info
Per-project .molt/syspath.json:
{
"interpreter": "/Users/me/.molt/python/3.12.3/bin/python",
"syspath": [
"/Users/me/.molt/pkg/numpy/1.26.4/cp312-cp312-macosx_11_0_arm64",
"/Users/me/.molt/pkg/requests/2.32.3/py3-none-any",
"..."
]
}When molt run executes Python, it:
- Sets
PYTHONPATHto those dirs joined. - Strips
VIRTUAL_ENV,PYTHONHOME, any inheritedPYTHONPATH. - Prepends
.molt/bin(console scripts) toPATH. exec()s the interpreter — no fork, no venv lookup.
$ molt gc --dry-run # preview
$ molt gc # remove store dirs no project referencesmolt tracks every project that's been synced (in ~/.molt/projects/).
GC walks ~/.molt/pkg/ and removes any directory whose
(name, version, abi) is referenced by zero registered projects. Safe
even if the project's source dir was deleted — GC will then offer to
purge the project's metadata too.
$ molt python list # installed + system Pythons
$ molt python install 3.13 # download via uv
$ molt python use 3.13 # pin in current project
$ molt python use 3.13 --global # set global default
$ molt python which # active interpreter path
$ molt python audit # find every Python on the machine
$ molt python isolation-check # verify project syspath has no leaksmolt python use rewrites .python-version AND triggers a re-sync —
the new ABI tag means different store paths. Switching Python versions
is one command.
molt python audit is uniquely useful for debugging "why is python
on PATH the wrong one." It prints every Python it can find with
version, interpreter type, ABI tag, and source.
molt python isolation-check validates that nothing on sys.path in
molt run python is pointed at a system site-packages or stale venv.
# pyproject.toml
[tool.molt.tasks]
dev = "uvicorn myapp.main:app --reload"
test = "pytest -v --tb=short"
lint = "ruff check ."
fmt = "ruff format ."
migrate = "python manage.py migrate"$ molt run dev # executes the task command
$ molt run test -- -k auth # passes -k auth to pytest
$ molt task list # show all defined tasks
$ molt task add ci "pytest -x && ruff check ."
$ molt task remove ciTasks run under the same hermetic environment as molt run python —
PATH includes .molt/bin so console scripts work, PYTHONPATH is
the store, no venv activation.
C, C++, Zig, and other compiled-language projects that don't need a
Python runtime can use moltproject.toml instead of pyproject.toml.
It supports the same [tool.molt.tasks] format but without the
Python-specific [project] fields (requires-python, dependencies):
# moltproject.toml — no requires-python, no dependencies needed
[project]
name = "stats-engine"
version = "0.1.0"
[tool.molt.tasks]
build = "zig c++ -O2 -std=c++17 -o bin/stats src/main.cpp"
run = "bin/stats"
clean = "rm -f bin/stats"$ molt run build # compiles the C++ binary
$ molt run run # executes bin/stats
$ molt task list # shows all tasks, same as pyproject.tomlmolt searches for moltproject.toml first, then falls back to
pyproject.toml — mixed projects (Python + native orchestration)
can keep both files. When no syspath.json exists (project has never
been synced as a Python project), task env falls back gracefully to
the current shell environment, so zig, clang, and other tools on
PATH are available without any Python setup step.
If the first arg to molt run isn't a task name, it's treated as a
binary:
$ molt run black --check src/
$ molt run python -c 'print(1)'$ molt template list # built-in + user templates
$ molt template show fastapi # render the file tree
$ molt template add my-template ./scaffold # register a user template
$ molt init --template my-template myapp # apply on initTemplates are directory snapshots applied at molt init time. Built-in
ones cover common scaffolds (CLI, FastAPI service, library). User
templates are arbitrary directories you've registered.
Use cases:
- A team can ship one template that bakes in their lint config, CI workflow, README scaffolding, default tasks.
- Quick experiments:
molt init --template scratchbookfor a pre-configured Jupyter setup.
$ molt project list # every project molt has touched
$ molt project info myapp # detailed view
$ molt project where myapp syspath # like `molt where`, for a remote project
$ molt project cd myapp # print path (for shell `cd $(molt project cd ...)`)
$ molt project purge --older-than 30d # bulk cleanup
$ molt project purge --unused # purge projects whose source dir is gone
$ molt project reinit ./previously-purged # re-registerThe --project / -p <name> global flag makes most commands
accept-from-anywhere:
$ molt -p myapp run test # from any directory
$ molt -p myapp where bin # without cd-ing into itUseful for monorepos and for IDE / editor integrations that need to operate on a project from another working directory.
$ molt tool install ./myapp # register myapp's CLI globally
$ molt tool list
$ molt tool show myapp
$ molt tool uninstall myapp
$ molt tool path # print ~/.molt/bin (add to PATH)molt tool install writes a thin shim into ~/.molt/bin/ that, when
invoked, runs the target project's default task (or a named one via
--task). The shim uses absolute paths — no PATH manipulation needed
on the user side beyond having ~/.molt/bin on PATH.
This is how you turn a Python project into a globally-callable command
without pipx install, pip install --user, or shell-specific setup.
This is where molt diverges sharply from other Python toolchains. Python is famously good as glue and bad as a number-cruncher. molt makes adding native code in any language a one- or two-file change. There are six distinct paths, each suited to a different use case:
| Path | What you write | Best for |
|---|---|---|
[[tool.molt.c.modules]] (header-driven C) |
A .h header + .c source |
Zero-boilerplate C extensions; molt auto-generates all CPython glue from the header |
.pyx (Cython) |
Python-flavoured DSL | Numerical kernels, custom C-API integration, hot loops with type hints |
.rs (Rust + PyO3) |
Rust + #[pyfunction] macros |
Type-safe complex APIs, async I/O, anything where Rust's safety pays off |
<name>.molt.toml + <name>.zig/.c/.cpp/... (kernel modules) |
Plain native source + manifest | Drop-a-file native functions in any C-ABI language; primitives only |
<name>.molt.toml + <name>.so (pre-built kernel) |
Manifest only | Binding to vendor-supplied or pre-compiled native libraries |
[[tool.molt.native]] (external module) |
A directory + a build command | Wrapping an existing Cargo / autotools / Make project |
Pick by use case, not by aesthetics:
- "I have a
.hheader I want to call from Python" →[[tool.molt.c.modules]]. - "I have one tight inner loop in Zig or C" → kernel module.
- "I want to take a Python list and return a dict" →
.pyxor.rs. - "I want to bind libsodium from Homebrew" → pre-built kernel.
- "I have a multi-file Rust crate" →
[[tool.molt.native]]with the rust preset.
All six compile to ABI-tagged .so files cached in ~/.molt/native/
and staged into the project's view at sync time. They appear in
Python as ordinary modules.
The simplest path to a CPython extension module: no Cython, no cffi,
no manual PyArg_ParseTuple wrangling. Declare [[tool.molt.c.modules]]
in pyproject.toml, point at a .h header, and molt sync does the rest.
/* fastmath.h — molt parses this automatically */
double add(double a, double b);
double lerp(double a, double b, double t);
double clamp(double value, double lo, double hi);
int gcd(int a, int b);
double mean3(double a, double b, double c);/* fastmath.c — plain C, zero Python-awareness */
double add(double a, double b) { return a + b; }
double lerp(double a, double b, double t) { return a + t * (b - a); }
double clamp(double v, double lo, double hi) { return v < lo ? lo : v > hi ? hi : v; }
int gcd(int a, int b) { while (b) { int t = b; b = a % b; a = t; } return a; }
double mean3(double a, double b, double c) { return (a + b + c) / 3.0; }# pyproject.toml — that's the entire configuration
[[tool.molt.c.modules]]
name = "fastmath"
src = ["fastmath.c"]
# headers defaults to ["fastmath.h"] — auto-discovered from src directory$ molt sync # parses fastmath.h → generates glue.c → compiles fastmath.so
→ c: 1 module(s)
✓ fastmath (cpython-312-darwin-arm64)import fastmath
print(fastmath.lerp(0.0, 100.0, 0.25)) # → 25.0
print(fastmath.gcd(48, 18)) # → 6
print(fastmath.clamp(-5.0, 0.0, 1.0)) # → 0.0What molt generates automatically from the header:
PyInit_fastmath— the CPython module entry pointPyMethodDeftable — one entry per parsed functionPyArg_ParseTuplecall for each function's arguments__doc__strings from function signaturesfastmath.pyistub for IDE / mypy / pyright integration
Because molt compiles your C extension with the project's Python include
directory and sets PYTHONPATH to the full package store, C code can
call back into Python packages at runtime:
/* ml_glue.c — C calling numpy and returning results to Python */
#define PY_SSIZE_T_CLEAN
#include <Python.h>
static PyObject* numpy_mean(PyObject* self, PyObject* args) {
PyObject* list;
if (!PyArg_ParseTuple(args, "O", &list)) return NULL;
/* Import numpy from the project's molt-managed store */
PyObject* np = PyImport_ImportModule("numpy");
PyObject* arr = PyObject_CallMethod(np, "array", "O", list);
PyObject* mean = PyObject_CallMethod(arr, "mean", NULL);
Py_DECREF(arr); Py_DECREF(np);
return mean; /* a Python float back to the caller */
}This works because molt sync writes the full PYTHONPATH into the
process environment before your extension is loaded. numpy, pandas,
torch — any managed dependency is importable from C.
# Compile for Linux from macOS — no Linux toolchain install needed
$ molt c build --target linux_amd64
$ molt c build --target linux_arm64
# Explicit rebuild (skips cache)
$ molt c build
# List discovered modules
$ molt c listThe zig cc cross-compiler handles the platform ABI differences
automatically. The same .c source builds correctly for every target.
[[tool.molt.c.modules]]
name = "fastmath"
src = ["fastmath.c", "mathutil.c"]
flags = ["-O3", "-march=native", "-DNDEBUG"]Drop a .pyx in your project root or under src/:
# fastmath.pyx
cdef extern from "math.h":
double sin(double)
double cos(double)
def vec_dot(double[:] a, double[:] b):
cdef double s = 0.0
cdef Py_ssize_t i, n = a.shape[0]
for i in range(n):
s += a[i] * b[i]
return s
def rotate(double x, double y, double angle):
return (x * cos(angle) - y * sin(angle),
x * sin(angle) + y * cos(angle))$ molt run python -c 'import fastmath; print(fastmath.rotate(1, 0, 1.5708))'
(-3.673e-06, 0.999999...)Configuration in [tool.molt.cython]:
[tool.molt.cython]
paths = [".", "src"]
extra_compile_args = ["-O3", "-march=native"]
include_c = true # auto-include .c/.h alongside
include_dirs = ["vendor/**/include"] # glob-aware
sources = ["src/native/**/*.c"] # bundled .c files
libraries = ["sodium", "z"] # linker -l flags
library_dirs = ["/opt/homebrew/lib"]
defines = { NDEBUG = "1", VERSION = "1" } # → -DNDEBUG=1 -DVERSION=1
std = "c++17" # only used if .cpp source detected
language = "c++" # explicit override (auto-detected otherwise)
pkg_config = ["libsodium", "openssl"] # runs pkg-config at build time
compiler = "zig" # use zig cc/c++ as the C compilerpkg_config is the killer feature here: pkg_config = ["opencv4"]
expands at build time into the dozens of -I, -L, -l flags
needed for OpenCV. No hand-editing.
include_c = true auto-discovers and compiles every .c in your
source tree alongside the generated Cython output. Mixed C+Cython
projects need only one config line.
C++ mode is auto-detected from .cpp sources or a
# distutils: language = c++ directive in your .pyx; molt switches
the toolchain to clang++/g++ automatically.
Drop a .rs file with PyO3 bindings:
// crypto.rs
#[pyfunction]
fn xor_bytes(data: Vec<u8>, key: u8) -> Vec<u8> {
data.iter().map(|b| b ^ key).collect()
}
#[pyfunction]
fn mul(a: f32, b: f32) -> f32 { a * b }
#[pymodule]
fn crypto(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(xor_bytes, m)?)?;
m.add_function(wrap_pyfunction!(mul, m)?)?;
Ok(())
}$ molt run python -c 'import crypto; print(crypto.mul(2.5, 4))'
10.0molt:
- Auto-installs zig (used as the linker for cross-host portability).
- Generates a
Cargo.toml+build.rsin~/.molt/native/rust-build/. - Pins
pyo3to your venv's Python (avoids the_PyErr_GetRaisedExceptionanddlopen dynamic_lookuptraps). - Builds, caches, stages.
Configuration in [tool.molt.rust]:
[tool.molt.rust]
pyo3_version = "0.24" # default
pyo3_features = ["extension-module"] # default
auto_prelude = true # inject `use pyo3::prelude::*;` if missing
auto_attrs = true # auto-add #[pyfunction] / #[pymodule]auto_attrs lets you write the most concise valid form of a
single-file PyO3 module:
fn xor_bytes(data: Vec<u8>, key: u8) -> Vec<u8> {
data.iter().map(|b| b ^ key).collect()
}
fn crypto(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(xor_bytes, m)?)?;
Ok(())
}molt's pre-build pass detects which fn is the module (matches the
file basename) and prepends #[pyfunction] / #[pymodule]
automatically. Functions that already carry any #[…] attribute are
left alone.
The newest pipeline. Manifest-driven, language-agnostic at the
discovery layer, with full IDE support via auto-generated .pyi
stubs.
project/
├── pyproject.toml
├── mathx.molt.toml
├── mathx.zig # or .c, .cpp, .odin, .nim, ...
└── main.py
# mathx.molt.toml
[[fn]]
name = "add"
description = "Sum two i32s."
args = [
{ name = "a", type = "i32", description = "First addend." },
{ name = "b", type = "i32", description = "Second addend." },
]
returns = { type = "i32", description = "The sum a + b." }
[[fn]]
name = "scale"
args = ["f64", "f64"]
returns = "f64"
[[fn]]
name = "fib"
description = "Iterative Fibonacci."
args = [{ name = "n", type = "i32" }]
returns = "i64"// mathx.zig — pure Zig, no Python bindings
export fn add(a: i32, b: i32) i32 { return a + b; }
export fn scale(x: f64, k: f64) f64 { return x * k; }
export fn fib(n: i32) i64 {
if (n < 2) return @intCast(n);
var a: i64 = 0; var b: i64 = 1;
var i: i32 = 2;
while (i <= n) : (i += 1) { const t = a + b; a = b; b = t; }
return b;
}import mathx
mathx.add(2, 3) # 5 — types matched, runtime-checked
mathx.scale(2.5, 4) # 10.0In your editor (VS Code, PyCharm, anything pyright-aware):
- Typed signatures via the auto-generated
mathx.pyi. - Per-arg descriptions surface as hover docs.
mathx.add("oops", 2)flagged at type-check time.
The same manifest also feeds:
- The C glue layer —
PyInit_mathx,PyMethodDef[], per-fnPyArg_ParseTuplewrappers — no hand-written boilerplate. __doc__strings inside the.sosohelp(mathx.add)works at runtime.- The
.pyistub — IDE / mypy / pyright integration.
Configuration in [tool.molt.native_kernel]:
[tool.molt.native_kernel]
paths = [".", "src"] # where to look for manifests
manifest_dir = "manifests" # optional centralised manifest dir
source_extensions = [".zig", ".c", ".cpp"] # auto-extended by configured builders
[tool.molt.native_kernel.build.c] # per-project builder override
command = "{zig} cc -c -O3 -DDEBUG=1 -fPIC -o {output} {source}"| Manifest | Python type |
|---|---|
i8 i16 i32 i64 u8 u16 u32 u64 usize isize |
int |
f32 f64 |
float |
bool |
bool |
void (return) |
None |
Unsupported types (pointers, strings, structs, callbacks) cause the function to be dropped from the binding with a warning; the rest of the module still builds. Pointer / string / struct support is on the roadmap.
A "kernel builder" is the recipe for compiling one source-file extension to a position-independent object file. molt looks them up in priority order:
- Per-project:
[tool.molt.native_kernel.build.<ext>]in pyproject.toml. - Global:
~/.molt/kernel-builders.yaml(auto-seeded with defaults on first use). - Built-in: hardcoded fallbacks for
.zig,.c,.cpp.
$ molt kernel-builder list
ext source command
zig global {zig} build-obj {source} -O ReleaseFast -fPIC -femit-bin={output}
c global {cc} -c -O2 -fPIC -o {output} {source}
cpp global {cxx} -c -O2 -fPIC -o {output} {source}
$ molt kernel-builder add odin --from-template
✓ added builder for .odin → ~/.molt/kernel-builders.yaml
command: odin build {source} -file -build-mode:obj -reloc-mode:pic -o:speed -out:{output}
$ molt kernel-builder add nim 'nim c --app:staticlib --noMain --out:{output} {source}'
$ molt kernel-builder add c '{zig} cc -c -O3 -DDEBUG=1 -fPIC -o {output} {source}' --local
$ molt kernel-builder show odin
$ molt kernel-builder remove odin
$ molt kernel-builder edit # opens ~/.molt/kernel-builders.yaml in $EDITOR
$ molt kernel-builder reset # restore seeded defaults
$ molt kernel-builder path # print global YAML pathToken vocabulary in commands:
| Token | Expands to |
|---|---|
{source} |
absolute path to the user's source file |
{output} |
absolute path molt expects the .o at |
{zig} |
absolute path to the auto-installed zig binary |
{cc} |
resolved system C compiler |
{cxx} |
resolved system C++ compiler |
{include_dir} |
Python include directory |
Adding a new C-ABI language is a one-line config change. No molt code edit required.
--from-template uses molt's curated commands for zig, c, cpp,
odin, nim, rs (rustc without PyO3), f90, f95. Any not in
that table requires the user to provide a command explicitly.
When you add a builder for .odin, molt automatically extends the
list of watched source extensions to include .odin. No
source_extensions = [".zig", ".c", ".cpp", ".odin"] edit needed —
the builder registry IS the source of truth.
Drop a <name>.so (or lib<name>.so) next to a <name>.molt.toml
and molt generates a dlopen-style wrapper:
project/
├── crypto.molt.toml
└── libcrypto.so # vendor-supplied or downloaded release
# crypto.molt.toml — same schema as source-built kernels
[[fn]]
name = "encrypt"
args = [{ name = "key", type = "u32" }, { name = "data", type = "u32" }]
returns = "u32"import crypto
crypto.encrypt(0xdeadbeef, 42)molt detects there's no source file, generates a glue.c that:
dlopen()s the library atPyInit_cryptotime.dlsym()s each declared function into a static fn-pointer slot.- Forwards Python calls through the pointers.
Three resolution modes for the library file (priority order):
| Manifest | Resolution |
|---|---|
library = "/opt/homebrew/lib/libsodium.dylib" |
Explicit absolute path — used as-is. |
library = "vendor/libsodium.dylib" |
Relative to manifest dir. |
(no library field) |
Sibling <name>.so / lib<name>.so / .dylib / .dll. |
The wrapper has no link-time dependency on the impl .so — it's
loaded at import time. Vendor updates trigger a rebuild via the cache
hash (which mixes the impl's content) but no manual cleanup.
- Bind to a brew/Nix-installed system library:
library = "/opt/homebrew/lib/libsodium.dylib". - Bind to a Rust crate built outside molt:
library = "../rust-crate/target/release/libfoo.so". - Distribute a pre-compiled vendor SDK alongside Python source: ship
vendor/libfoo.so+foo.molt.toml; users getimport foo. - Glue around system libraries (zlib, libcurl, libcrypto): one manifest binds primitive subsets without needing a Cython wrapper.
For projects bigger than "one source file + manifest" — e.g. a Rust Cargo workspace, a CMake-built C++ library, an autotools project — use the external-module recipe pattern:
[[tool.molt.native]]
module = "myrustlib"
src = "rust/myrustlib"
preset = "rust"
[[tool.molt.native]]
module = "fastmath"
src = "c/fastmath"
preset = "c"
build = "cc -O3 -shared -fPIC -I{python_include} -o {output} src/*.c"
output = "fastmath.{ext}"
src_patterns = ["src/**/*.c", "src/**/*.h"]molt:
- Looks up the named preset (built-in:
rust,c,cpp; or your own in~/.molt/native-presets.yaml). - Hashes all matching files in
srcfor cache invalidation. - Runs the build command in the source dir.
- Links the resulting
.sointo the project view.
Manage presets globally with:
$ molt native-preset list
$ molt native-preset show rust
$ molt native-preset add --name myrust --build "cargo build --release" --output "target/release/lib{module}.{ext}"
$ molt native-preset remove myrustThis is the "wrap an existing build system" path. The kernel-module system is the "drop a single file in your project" path. Use whichever fits.
[tool.molt.runtime]
extra_paths = ["vendor/lib", "vendor/bin", "vendor/Frameworks"]Each entry is prepended to all relevant runtime path env vars when
molt spawns Python (or any subprocess via molt run):
| Env var | Used for |
|---|---|
PATH |
exec lookup; on Windows also DLL lookup |
LD_LIBRARY_PATH |
shared-library lookup on Linux |
DYLD_FALLBACK_LIBRARY_PATH |
shared-library lookup on macOS (SIP-safe) |
DYLD_FALLBACK_FRAMEWORK_PATH |
framework lookup on macOS |
Drop a binary, a .so, or a Qt.framework into a directory listed
in extra_paths, and it's findable from Python — for subprocess,
ctypes, dlopen, anything — without any shell setup.
project/
├── vendor/
│ ├── bin/ffmpeg # discovered via PATH
│ ├── lib/libcrypto.so # discovered via dynamic-linker var
│ └── Frameworks/Qt.framework # discovered on macOS
└── main.py
# main.py
import subprocess, ctypes
subprocess.check_output(["ffmpeg", "-version"]) # finds vendor/bin/ffmpeg
ctypes.CDLL("libcrypto.so") # finds vendor/lib/libcrypto.soThis is genuinely useful even outside the kernel-module system — any
project that vendors a .so for ctypes use, or ships helper binaries,
can drop them in and have them discoverable on any developer's
machine without per-shell LD_LIBRARY_PATH exports.
Two complementary layers, both opt-in, both injected into every
process molt spawns (molt run, tasks, builds, the project's Python).
Variables you want in every project on the machine: corporate proxy, internal package mirrors, machine-wide secrets, debug flags.
$ molt env set HTTPS_PROXY http://proxy.corp.com:8080
✓ set HTTPS_PROXY in /Users/me/.molt/env.yaml
$ molt env set PIP_INDEX_URL https://pypi.corp.com/simple
$ molt env set GITHUB_TOKEN ghp_xxx
$ molt env list
GITHUB_TOKEN ghp_xxx
HTTPS_PROXY http://proxy.corp.com:8080
PIP_INDEX_URL https://pypi.corp.com/simple
$ molt env get HTTPS_PROXY
http://proxy.corp.com:8080
$ molt env unset GITHUB_TOKEN
$ molt env edit # opens ~/.molt/env.yaml in $EDITOR
$ molt env path # /Users/me/.molt/env.yamlThe file is 0600 since it can hold credentials. Saves are atomic
(write-temp + rename). Sorted YAML output for clean diffs.
Variables bound to a specific project: DATABASE_URL, LOG_LEVEL,
PYTHONUNBUFFERED, anything project-specific.
# pyproject.toml
[tool.molt.runtime.env]
DATABASE_URL = "postgresql://localhost/dev"
LOG_LEVEL = "DEBUG"
PYTHONUNBUFFERED = "1"…or via CLI with --local:
$ molt env set DATABASE_URL postgresql://localhost/dev --local
✓ set DATABASE_URL in pyproject.toml [tool.molt.runtime.env]
$ molt env unset DATABASE_URL --local1. project [tool.molt.runtime.env] ← always wins (explicit project intent)
2. parent shell env (export FOO=…) ← shell-level explicit beats global default
3. global ~/.molt/env.yaml ← only sets vars not already in shell
So export FOO=bar in your shell still wins over a global default —
explicit shell intent isn't silently overridden — but a project that
declares FOO = "..." always wins, since project intent is even more
explicit.
Three names are molt-managed and can't be set through env config:
| Name | Why molt manages it |
|---|---|
PYTHONPATH |
Computed from the project's syspath; manual override breaks store isolation |
VIRTUAL_ENV |
Stripped on every spawn so a stale venv doesn't leak in |
PYTHONHOME |
Same — molt always points at the project's pinned interpreter |
molt env set PYTHONPATH ... errors loudly. The same names in
[tool.molt.runtime.env] are silently dropped at parse time.
| Scenario | Where to put it |
|---|---|
| Corporate proxy | global — applies everywhere |
| Internal PyPI mirror | global — PIP_INDEX_URL, UV_INDEX_URL |
| Per-developer credentials | global — never in committed files |
RUSTFLAGS=-C target-cpu=native |
global if you want it on every build |
DATABASE_URL, LOG_LEVEL |
project — bound to the project |
PYTHONUNBUFFERED=1 |
global if you always want it; project for one app |
| Test-only flags | project, scoped to the tests task |
$ molt build # → myapp-v1.0.0
$ ls -la myapp-v1.0.0
-rwxr-xr-x 42M myapp-v1.0.0
$ scp myapp-v1.0.0 prod:/usr/local/bin/
$ ssh prod './myapp-v1.0.0 install && ./myapp-v1.0.0 run'The output binary contains:
┌────────────────────────┐
│ launcher (Go) │ install / run / verify / uninstall
├────────────────────────┤
│ payload (tar.gz) │ source + deps + Python interpreter + native .so files
├────────────────────────┤
│ trailer │ [payload offset][SHA-256 root hash][magic]
└────────────────────────┘
On the target machine:
./myapp-v1.0.0 installextracts payload to~/.molt/installed/myapp-v1.0.0/, verifies the SHA-256 trailer against the embedded integrity manifest, runspost_installhooks../myapp-v1.0.0 runexecs the default command in the hermetic env../myapp-v1.0.0 run workerruns a named command.- No Python installation required on the target.
The deployment artifact is described separately from pyproject.toml:
version: 1
project:
name: myapp
version: 2.3.1
python: "3.12"
deps:
strategy: pyproject # reads pyproject.toml + uv.lock
include:
- "src/**/*.py"
- "templates/"
- "static/"
assets:
- source: data/cities.json
dest: data/
commands:
default: web
web: { exec: ["gunicorn", "myapp:app", "--bind", "0.0.0.0:8000"] }
worker: { exec: ["celery", "-A", "myapp", "worker"] }
migrate: { exec: ["python", "manage.py", "migrate"] }
env:
default:
LOG_LEVEL: INFO
worker:
CELERY_CONCURRENCY: "8"
hooks:
post_install:
- "python manage.py migrate --noinput"
- "python -c 'import myapp; myapp.warmup()'"
integrity:
verify_on_install: true$ molt build --os linux --arch amd64 # build a Linux binary on a Mac
$ molt build --os linux --arch arm64
$ molt build --output dist/myapp.glibc # custom output pathmolt build uses the same uv resolution under the hood; cross-builds
pull wheels for the target ABI, package them, and produce an
artifact runnable on the target without further toolchain.
$ molt package # build both wheel and sdist
$ molt package --wheel # wheel only
$ molt package --output dist/Standard PEP 517 build via uv — for users who want to publish to PyPI in addition to (or instead of) the single-binary path.
$ molt verify-binary myapp-v1.0.0 # validates trailer ↔ manifest
$ molt verify-binary myapp-v1.0.0 --deep # also re-hashes every file in payload
$ molt inspect myapp-v1.0.0 # print embedded manifest
$ molt inspect myapp-v1.0.0 --files # list every file with hash + size
$ molt diff old.bin new.bin # side-by-side manifest diffEach molt build embeds an integrity manifest containing the SHA-256
of every file in the payload plus a single root hash that's also stored
in the binary's trailer. verify-binary checks both layers; --deep
re-hashes every file from the extracted tar to catch corruption.
This isn't optional security theatre — verify_on_install: true in
molt.yaml blocks the install step if any hash mismatches, which
catches transit corruption (cosmic rays, scp interruption, partial
writes) and tampering.
molt diff is for change auditing: comparing two release artifacts
shows exactly what's new, removed, or modified, by hash.
$ molt editor # auto-detect (vscode, pyright, etc.)
$ molt editor vscode # write/refresh .vscode/settings.json
$ molt editor pyright # write/refresh pyrightconfig.jsonGenerates the right config files so VS Code, pyright, mypy, etc. can
find the project's Python interpreter and the syspath entries from
.molt/syspath.json. After running, IntelliSense, go-to-definition,
and type-checking all work the same as if you'd activated a venv.
For the kernel-module system specifically, the auto-generated .pyi
stubs are picked up automatically — no extra editor config needed.
$ molt doctor
✓ uv 0.5.7 /usr/local/bin/uv
✓ python 3.12.3 /Users/me/.molt/python/3.12.3/bin/python
✓ git 2.39.1 /usr/bin/git
✓ go 1.22.0 /usr/local/go/bin/go (for `molt build`)
✓ cargo 1.83.0 (for .rs auto-compile)
✓ zig 0.14.1 /Users/me/.molt/toolchains/zig/0.14.1/zig (auto-installed)
$ molt info
Project: myapp
Source: /Users/me/work/myapp
Python: 3.12.3
Sync hash: abc123...
Store dirs: 24
Native modules: 3 (1 cython, 1 rust, 1 kernel)
Tasks: dev, test, lint$ molt where syspath
/Users/me/work/myapp/.molt
/Users/me/.molt/pkg/numpy/1.26.4/cp312-cp312-macosx_11_0_arm64
/Users/me/.molt/pkg/...
$ molt where python
/Users/me/.molt/python/3.12.3/bin/python
$ molt where store
/Users/me/.molt/pkg
$ molt where bin
/Users/me/work/myapp/.molt/binmolt where is the universal "show me where X actually is" tool. The
keys (syspath, python, bin, store, state, sitecustomize,
uv-env) cover everything a user might want to script around.
| Pain point | molt's answer |
|---|---|
| Activating a venv every time you switch projects | No venv. molt run sets env and execs. |
Disk bloat from duplicate .venv installations |
Wheels are unpacked once, shared. A 50-project laptop saves 10–20 GB. |
| Switching Python versions takes minutes | molt python use 3.13 re-resolves syspath in seconds. No venv rebuild. |
| Distributing your CLI to non-technical users | molt build gives one binary they can scp and run. |
| Adding native code is a build-system odyssey | Drop a .zig / .c / .rs / .pyx file. Done. |
| IDE doesn't see your project's Python | molt editor vscode, then it does. |
pkg-config flags vary by machine |
pkg_config = ["opencv4"] resolves per-machine at build time. |
| Deciding between PyInstaller, Docker, Nuitka | Single-binary build with integrity verification, native import, no Python on target. |
| Concern | molt's answer |
|---|---|
| Reproducible builds across team machines | The store key includes the ABI tag — same (name, version, abi) ⇒ same files on every dev's machine. |
| Auditing what's in a release | molt inspect + molt diff show every file by hash. |
| Tampering / corruption detection | SHA-256 root hash in the trailer; verify-binary --deep re-hashes the payload. |
| Cross-platform deploys | molt build --os linux --arch amd64 from a developer's Mac. |
| Internal tooling distribution | molt tool install per-developer, OR build a binary and distribute via standard package management. |
| Vendored native dependencies (HSM SDKs, proprietary libs) | [tool.molt.runtime] extra_paths, manifest with library = "...", or [[tool.molt.native]] with a custom build command. |
| Hot-path performance on Python services | Cython for numerical, Rust for type-safe complex APIs, kernel modules for primitives. All staged into the project view automatically. |
| CI/CD: provisioning Python toolchains | One install (curl install.sh | sh), then molt sync + molt build. No system Python required if --bundle-python is set. |
| Long-term project archive | The single-binary output is a standalone artifact — no need to recreate the build environment to run it 5 years later. |
- You're already happy with
uv+pip+venvfor a small hobby project. molt's value compounds with project count and team size; for one solo project, the win is smaller. - You're shipping a library that users
pip install. Useuv build/hatch/flitfor that. molt'spackagecommand produces wheels but the build-binary path is for applications, not libraries. - You need fully-managed Python infrastructure (e.g. Lambda layers, Cloud Run images). molt's binary works in those, but if you're committed to the AWS/GCP-native build path, the integration cost may not be worth it for a single project.
| Capability | molt | pip + venv | poetry | pdm | uv | pyinstaller | docker |
|---|---|---|---|---|---|---|---|
| Dep management | ✓ (via uv) | ✓ | ✓ | ✓ | ✓ | – | ✓ (Dockerfile) |
| Shared package store | ✓ | ✗ | ✗ | ✓ (PEP 582 at one point) | ✗ | ✗ | ✗ |
No .venv per project |
✓ | ✗ | ✗ | ✗ | ✗ | N/A | N/A |
| Python version mgmt | ✓ | ✗ (needs pyenv) | partial | ✓ | ✓ | – | – |
| Single-binary distribution | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ (image) |
| No Python required on target | ✓ | ✗ | ✗ | ✗ | ✗ | ✓ | ✗ (Docker) |
| Cython auto-compile | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| Rust+PyO3 single-file | ✓ | ✗ (use maturin) | ✗ | ✗ | ✗ | ✗ | ✗ |
| Manifest-driven kernel modules | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
Pre-built .so auto-binding |
✓ | partial (ctypes manual) | ✗ | ✗ | ✗ | ✗ | ✗ |
| Multi-language native (Zig, C, C++, Odin, …) | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
| Integrity-verified artifact | ✓ | ✗ | ✗ | ✗ | ✗ | ✗ | ✓ (image digest) |
| Hot-reload tool registry | ✓ | – | ✗ | ✗ | ✓ (uvx) | – | – |
molt's unique surface is the combination: nobody else gives you the global package store + Python version management + native auto-compile across multiple languages + single-binary distribution in one tool. Each of those individually has alternatives; the value is having them coherent and shareable.
Shipping: kernel modules (zig/c/cpp), pre-built .so binding,
extra_paths, kernel-builder CLI, Cython rich config (pkg_config,
include_c, libraries, defines, std, language overrides), Rust+PyO3
auto-prelude / auto-attrs, multi-project tracking, integrity
verification, cross-compile builds.
Near-term:
- Pointer types (
*T) in kernel manifests — unlocks buffer/array args and bulk-array returns. cstring↔strmapping for native strings.- Keyword-argument support in kernel wrappers (real
inspect.signaturefor native fns). molt kernel-builder add --from-templatefor more languages (currently zig/c/cpp/odin/nim/rs/f90).
Medium-term:
- Lazy iterator support: kernel-side
init/next/droptriple wrapped as a Python iterator class. - Struct types in manifests (
[[type]]blocks) for richer FFI. .molt.json/.molt.yamlmanifest formats (parser hooks plumbed, parsers not yet written).- Auto-inference of kernel signatures from C/Zig source for manifest-less single-file workflows.
Speculative:
- Synchronous Python callbacks in kernels via opaque-handle trampolines.
- Asyncio integration via
fdreturn type for non-blocking native workers. - IPC patterns for in-process bidirectional Python ↔ native communication.
molt run is not limited to Python. Any file with a registered
extension is dispatched directly to the appropriate runtime — no tasks,
no configuration, no activation:
$ molt run hello.rb # → ruby hello.rb
$ molt run hello.js Alice # → node hello.js Alice (args forwarded)
$ molt run hello.go # → go run hello.go
$ molt run hello.jl # → julia hello.jl
$ molt run hello.exs # → elixir hello.exs28 runtimes ship built-in (including .rs → rustc). Add your own in one command:
$ molt run-handler add deno "deno run {file} {args}"
$ molt run server.ts # → deno run server.ts
# Per-OS overrides
$ molt run-handler add ts "npx ts-node {file} {args}" --windows "npx.cmd ts-node {file} {args}"
# Inspect and manage
$ molt run-handler list # all 27 built-ins + user handlers
$ molt run-handler show rb # Extension: .rb Command: ruby {file} {args}
$ molt run-handler reset # restore factory defaultsDispatch order inside molt run <arg>:
- Flag-shaped arg → project's default entry point
- No arg →
main.pyorpython -m <pkg> - Ends in
.pyand file exists → project Python interpreter - Ends in
.mojo/.🔥and file exists →mojo run - Has any extension, file exists, handler registered →
runWithHandler - Matches a task in
[tool.molt.tasks]→ task runner - Fallthrough → exec binary on PATH
Token reference:
| Token | Expands to |
|---|---|
{file} |
Absolute path to the script |
{dir} |
Directory containing the script |
{basename} |
Filename without extension |
{args} |
Extra arguments, space-joined |
{tmp} |
Stable per-file temp dir ($TMPDIR/molt-run/<hash>/) |
{python} |
Project's pinned Python interpreter |
{zig} |
Auto-installed zig binary |
The built-in .c and .cpp handlers use {tmp} so compiled binaries
never appear in your working directory. Custom compile-then-run handlers
can use it the same way:
$ molt run-handler add odin \
"sh -c \"odin build {file} -file -out:{tmp}/{basename} && {tmp}/{basename} {args}\""
$ molt run main.odin # compiled to $TMPDIR/molt-run/<hash>/main — not CWDHandlers are stored in ~/.molt/run-handlers.yaml. User entries take
precedence over built-ins of the same extension. The project's full
molt environment (PYTHONPATH, .molt/bin/ on PATH) is applied
before exec — so {python} resolves to the pinned interpreter and any
installed packages are available to child processes.
molt add, molt remove, and molt sync are language-agnostic interfaces.
For a Python project they delegate to uv; for any other language they
route to the ecosystem's native tool via a configurable pkg-backend.
# Rust project (lang = "rust" in moltproject.toml)
$ molt add serde --features derive # → cargo add serde --features derive
$ molt add tokio --version "^1" # → cargo add tokio --version ^1
$ molt sync # → cargo fetch
$ molt remove serde # → cargo remove serde
# Go project
$ molt add github.com/spf13/cobra # → go get github.com/spf13/cobra
$ molt sync # → go mod download
# Node project
$ molt add express@4 # → npm install express@4
$ molt sync # → npm install
# Force a specific language in a mixed or ambiguous project
$ molt add openssl --lang rust # explicit --lang flagmolt infers the target language from the package name structure
(e.g. github.com/… → Go, @scope/pkg → Node, lib* → C, *-sys → Rust)
and from the project's lang field. When inference is ambiguous, pass --lang.
Per-ecosystem version formatting is handled automatically:
| Language | molt add pkg 1.6 becomes |
|---|---|
| python | uv add pkg==1.6 |
| rust | cargo add pkg --version 1.6 |
| go | go get pkg@v1.6 |
| node/bun | npm install pkg@1.6 |
| swift | swift package add pkg --exact 1.6 |
Backend token vocabulary (used in custom command templates):
| Token | Expands to |
|---|---|
{package} |
Package name as typed |
{version_flag} |
Formatted version argument for the ecosystem's CLI |
{flags} |
Extra flags forwarded from the CLI |
{manifest} |
Absolute path to the project manifest |
{project} |
Absolute path to the project root |
Inspect and customise backends:
$ molt pkg-backend list # show all langs (built-in + user)
$ molt pkg-backend show rust # print the rust backend commands
$ molt pkg-backend add nim \
--add "nimble install {package}{version_flag}" \
--remove "nimble uninstall {package}" \
--sync "nimble install"
$ molt pkg-backend reset # restore factory defaultsProject-level override — swap to Bun without changing the global backend:
# moltproject.toml
[tool.molt.backend]
add = "bun add {package}{version_flag}"
sync = "bun install"
# remove, list, upgrade fall back to the global node backend9 built-in backends: python rust go node bun zig c swift nim.
Backends are stored in ~/.molt/pkg-backends.yaml.
Python is an excellent orchestration and glue language, but CPython pays a price for dynamism: every attribute access goes through a dict lookup, every arithmetic operation boxes and unboxes objects, and the GIL prevents true CPU-level parallelism across threads. For most code that price is invisible. For a numerical inner loop or a tight data-processing kernel, it is the difference between seconds and milliseconds.
molt makes native code the path of least resistance — not a build-system adventure.
| Scenario | Pure Python | Native equivalent | Typical speedup |
|---|---|---|---|
| Summing 10M floats in a loop | ~800 ms | C for loop |
20–40× |
| Sorting 1M structs | ~400 ms | C++ std::sort |
5–15× |
| 8-wide SIMD dot product | ~1200 ms | Zig / Mojo SIMD | 50–200× |
| Calling a math primitive 10M times | ~900 ms | C extension (no GIL) | 30–100× |
| Threaded parallel sum | GIL-serialised | C with Py_BEGIN_ALLOW_THREADS |
4–8× on 8-core |
| AES-128 block cipher, 1 GB | ~20 s | AES-NI via Zig or C | 200–500× |
These are representative orders of magnitude, not guarantees. The actual gain depends on whether your bottleneck is CPU, memory bandwidth, or latency. Profile first; optimise only what's hot.
molt supports a spectrum of native-code strategies. Pick the one whose complexity matches the speedup you need:
Python pure → Cython (typed) → C extension → Zig / Mojo SIMD
1× 2–10× 5–100× 50–500×
no build step .pyx file .h header .zig or .mojo file
easy to read mostly Python zero boilerplate maximum hardware use
in molt
1. Cython — annotate types in a .pyx file, drop it next to your Python.
molt compiles it automatically during molt sync.
# pyproject.toml
[[tool.molt.native]]
source = "fastsum.pyx" # Cython auto-detected by extension2. Header-driven C extension (demo 13) — write a .h file with your
function signatures, implement in .c. molt parses the header, generates all
CPython boilerplate, and compiles a real .so.
[[tool.molt.c.modules]]
header = "fastmath.h" # molt reads every scalar signature
sources = ["fastmath.c"]import fastmath
print(fastmath.sum_f64(data)) # direct C call, no marshal overheadSpeedup for a simple float summation: 30–50× vs a Python loop because
the C function processes values without boxing them into float objects.
3. C++ templates (demo 16) — for data structures, sorting, or STL
algorithms that need C++17 features. Compile via molt tasks and call the
binary from Python, or expose as a C extension via extern "C".
4. Zig with SIMD (demo 17) — for true vectorised computation. A Zig
@Vector(8, f32) processes 8 floats per CPU instruction. On modern x86-64
this means a vectorised sum is 50–200× faster than a Python loop and
2–4× faster than scalar C, because the compiler maps the vector directly
to AVX2 registers.
// Zig: 8 floats processed in parallel by a single CPU instruction
const v: @Vector(8, f32) = data[i..][0..8].*;
acc += @reduce(.Add, v);5. Mojo SIMD (demos 10–11) — for Python scientists who want near-metal performance with a Python-like syntax and full access to numpy/pandas. Demo 10 shows a 47× speedup over a pure Python loop; demo 11 reaches near-numpy-MKL matmul performance at N=128.
The interop is bidirectional. C and Zig code can call back into Python
packages — numpy, pandas, scipy — because molt sets PYTHONPATH to the
full package store before executing any native code:
// In your C extension: call numpy from C
PyObject *np = PyImport_ImportModule("numpy");
PyObject *arr = PyObject_CallMethod(np, "array", "O", py_list);
PyObject *mean = PyObject_CallMethod(arr, "mean", NULL);
// numpy's BLAS-backed mean, called from C, result returned to PythonThis lets you write a C or Zig outer loop for parallelism or SIMD, while still delegating to numpy for linear algebra — the best of both worlds.
zig cc and zig c++ are drop-in cross-compilers. From a single developer
machine (say, macOS arm64) you can produce native binaries for every target:
# Compile a C extension for Linux amd64 — from macOS, no Docker
GOOS=linux GOARCH=amd64 molt build # full molt binary, cross-compiled
# Cross-compile a C++ binary for Linux
zig c++ -target x86_64-linux-gnu -O2 -std=c++17 -o bin/stats-linux src/main.cpp
# Build and package the entire molt project for Linux from macOS
molt build --os linux --arch amd64 # cross-compiles Python + native codeWhy this matters for CI/CD: your Mac development machine can produce
Linux release artifacts without a Linux VM, remote SSH, or Docker. The Zig
toolchain is downloaded once by molt into ~/.molt/toolchains/zig/ and
shared across all projects.
ABI compatibility: the store key includes the ABI tag, so a wheel built
for cp313-cp313-linux_x86_64 is never confused with one built for
cp313-cp313-macosx_arm64. Cross-compiled artifacts land in the correct
per-platform slot automatically.
# 1. Profile your Python to find the bottleneck
molt exec python -m cProfile -s cumulative main.py | head -20
# 2. Write the hot function in C (drop a .h + .c next to your Python)
echo '[[tool.molt.c.modules]]' >> pyproject.toml
echo 'header = "fastsum.h"' >> pyproject.toml
echo 'sources = ["fastsum.c"]' >> pyproject.toml
# 3. Re-sync: molt compiles the extension automatically
molt sync
# 4. Import and call — no Python restart needed
python -c "import fastsum; print(fastsum.sum_f64([1.0, 2.0, 3.0]))"
# 5. Profile again to confirm
molt exec python -m cProfile -s cumulative main.py | head -20No Makefile, no setup.py, no pip install .. The extension is compiled,
staged, and visible to Python in one molt sync invocation.
demos/— eighteen runnable example projects:01–06core toolchain (init, deps, FastAPI, data, CLI, binary dist)07assembly kernel module08–12Mojo (hello world, numpy interop, SIMD, matmul, Python extension)13header-driven C extension ([[tool.molt.c.modules]])14universal script launcher (Ruby, Go, Julia, Elixir, Node, …)15C as primary language (molt run hello.c+ multi-file project)16C++17 as primary language (molt run hello.cpp+ multi-file project)17Zig 0.16 as primary language (molt run hello.zig+zig build-exe)18Rust as primary language (molt add→ Cargo,molt sync→cargo fetch)
docs/MANUAL.md— exhaustive command referencedocs/global-store.md— store architecture deep divedocs/kernel-modules.md— kernel-module referencedocs/zig-odin-kernels.md— design rationale for the manifest-driven kernel pipelinedocs/native-modules.md—[[tool.molt.native]]recipes and presetsdocs/mcp-server.md— MCP server for Claude Code / Cursor
For each individual command, molt <cmd> --help is built-in.