Skip to content
nanassoundPublic

About

Text-to-CAD tools in Elixir with build123d, OpenSCAD, replicad, and smlibpy.

Topics

Resources

Stars

38 stars

Watchers

0 watching

Forks

Latest commit

 

History

40 Commits

Folders and files

Repository files navigation

mnft

A cheerful factory line: a prompt goes in, crates lettered m, n, f, t ride a conveyor, a robot arm finishes a part, and it drops into an STL and STEP bin

Say it out loud and your mouth already knows the word: manufacture.

The word started life as Latin manu factus, made by hand. It travelled far and refused to change much along the way. The English say manufacture, the Estonians manufaktuur, the Indonesians manufaktur. Germans kept Manufaktur, the French manufacture, Italians manifattura, Spaniards manufactura, and the Portuguese trimmed it to manufatura. You will hear manufactuur in Dutch, manufaktur in Swedish, manufaktuuri in Finnish, manufaktura in Polish, and мануфактура in Russian. Different alphabets, same idea, and the same four letters hiding in every single one of them: m, n, f, t.

That idea was making things by hand. This project keeps the making and hands the manual labour to something else. You describe a part in plain language. A language model writes CAD code. A guarded engine runs it, and you get an STL, a STEP file, and a 3D preview you can turn around in your browser. Made by prompt, checked by machine, downloaded in the formats a machine shop actually wants.

Everything here is open source, Apache-2.0.

core/mnft_cad/       the core. Turn pipeline, pluggable engines (build123d,
                     OpenSCAD), pluggable storage (an ETS table backed by an
                     fsynced journal by default), BYO-key multi-provider LLM.
                     Pure Elixir/OTP: it ships without a web framework, an auth
                     layer, or a database server.
core/cad_worker/     a small Python CLI that runs build123d code behind static
                     checks, rlimits and a hard wall clock. The build123d
                     engine's runtime.
core/cad_worker_web/ the browser twin of cad_worker: a guarded JS dialect
                     runner over replicad, the real OCCT 8 kernel compiled
                     to WebAssembly. The browser engine's runtime.
examples/mnft_cad_web/      a reference Phoenix app. Chat UI, 3D viewer,
                           anonymous cookie owner. Run it and see.
examples/mnft_cad_headless/ a headless reference: the core with the UI
                           stripped away, running many owners, many sessions,
                           and all turns at once.
examples/mnft_browser/      the browser reference: the whole core compiled to
                           WebAssembly (the BEAM in the browser, via Popcorn),
                           CAD in a Web Worker, no server at all.

Try it

Requirements:

  • Elixir 1.17+ (with Erlang/OTP)
  • Python 3.11 or 3.12, with uv
  • An LLM API key
  • Optional: poppler, only if you paste PDF datasheet links into prompts. Neither mix nor uv installs it. brew install poppler (macOS) or apt install poppler-utils (Debian/Ubuntu). Without it everything else works, but PDFs are not read.
cd core/cad_worker && uv venv --python 3.12 && uv pip install -e . pytest && cd ../..
cp .env.example examples/mnft_cad_web/.env      # then add your LLM_API_KEY
cd examples/mnft_cad_web && mix setup
mix phx.server                            # http://localhost:4200
# no UI?
cd examples/mnft_cad_headless && mix deps.get && MNFT_HEADLESS_FAKE=1 mix run -e "MnftCadHeadless.run()"

In the browser (WASM, no server)

examples/mnft_browser runs the same core compiled to WebAssembly (the BEAM in the browser, via Popcorn) with core/cad_worker_web's replicad/OCCT kernel in a Web Worker. Nothing runs server-side: bring your own key in the page's settings panel (it travels browser → provider only); without one, a scripted sample turn runs. Needs Elixir 1.19+ and Node.js/npm.

cd core/cad_worker_web && npm install && cd ../../examples/mnft_browser
mix deps.get && mix dev    # http://localhost:4000

With Docker

No Erlang, Python or poppler needed on your machine. The image is a mix release of the reference web app (mnft_cad_web) plus the build123d worker — no code reloader, no asset watchers, no mix at runtime.

docker build -t mnft .
docker run --rm -p 4000:4000 -e LLM_API_KEY=sk-... mnft    # http://localhost:4000

Pass any other setting from .env.example with -e or --env-file, for example -e LLM_PROVIDER=anthropic -e LLM_BASE_URL=. Generated models and the journal live in /data and vanish with the container; add -v mnft-data:/data to keep them. For a long-lived deployment also set SECRET_KEY_BASE (without it a random key is generated per boot and saved parts become unreachable after a restart) and PHX_HOST when the app sits behind a proxy. The caution at the end of this README still applies: none of this is a sandbox.

Engines

Pick with MNFT_ENGINE in examples/mnft_cad_web/.env. Defaults to build123d.

MNFT_ENGINE Kernel Output Setup
build123d (default) OpenCascade via cad_worker STEP + STL + GLB included in the venv bootstrap above
openscad OpenSCAD binary STL + GLB only (3D-printing path) install the binary, nothing else
smlibpy (alpha) SMLib NURBS kernel (AWV Informatik AG, MIT) STEP + STL + GLB one small extra into the venv

OpenSCAD — the engine shells out to the openscad CLI (OPENSCAD_BIN overrides the path; the default is openscad on PATH).

  • macOS — brew install --cask openscad is disabled upstream (Gatekeeper). Install the snapshot DMG instead:
    hdiutil attach OpenSCAD-*.dmg && cp -R /Volumes/OpenSCAD/OpenSCAD.app /Applications/ \
      && hdiutil detach /Volumes/OpenSCAD
    xattr -dr com.apple.quarantine /Applications/OpenSCAD.app
    ln -sf /Applications/OpenSCAD.app/Contents/MacOS/OpenSCAD "$(brew --prefix)/bin/openscad"
  • Windows — install the installer/ZIP from openscad.org, then point the config at the CLI (the GUI binary alone won't work): OPENSCAD_BIN=C:\Program Files\OpenSCAD\openscad.exe.
  • Linux — apt install openscad / dnf install openscad / snap install openscad (snap needs an alias: sudo snap alias openscad.openscad openscad).

Then set MNFT_ENGINE=openscad.

smlibpy — ~7 MB wheel (Python 3.10–3.14; macOS, Linux and Windows wheels available):

cd core/cad_worker && uv pip install -e ".[smlibpy]"
# then set MNFT_ENGINE=smlibpy

smlibpy is alpha here: less production time than build123d, treat it as a second option, not a replacement.

Examples

Three real turns from the reference web app: the ask, then what the code built.

A CNC-machined robotic arm, asked for in plain language.

A pin-jointed robotic arm modelled for CNC machining

A TE 583861-5 card-edge connector, from a datasheet URL and a photo.

A TE 583861-5 card-edge connector modelled from a datasheet and a photo

A Seymour Duncan SSL-7 single-coil pickup, from its spec sheet.

A Seymour Duncan SSL-7 single-coil pickup modelled from its spec sheet

Your results will differ: the CAD that comes back depends on the LLM you bring, and models write build123d and OpenSCAD with different skill. Experiment. Swapping LLM_PROVIDER or LLM_TEXT_MODEL is one line of config, and sometimes that is all a tricky part needs.

Building on the core

mnft_cad/README.md has the embedding guide. The short version: the core never asks who you are. You hand it an opaque owner id, your choice of storage adapter, your choice of engine, and an event handler to carry progress wherever you want. API keys come with a resolver callback, so you can keep them wherever your host keeps secrets. Provider, endpoint and models are config only.

Environment variables, if you just want to run it: LLM_API_KEY, LLM_BASE_URL, LLM_TEXT_MODEL, LLM_VISION_MODEL, LLM_TIMEOUT_S, CAD_WORKER_PYTHON, CAD_WORKER_CWD, OPENSCAD_BIN, MNFT_DATA_DIR. Set LLM_PROVIDER=anthropic and leave LLM_BASE_URL empty for Claude models.

Tests

cd core/cad_worker && .venv/bin/python -m pytest -q
cd core/mnft_cad && mix test
cd examples/mnft_cad_web && mix test
cd core/cad_worker_web && npm test   # browser dialect, not yet in CI

CI runs the first three on Linux and macOS.

Acknowledgement

This project is designed, built, and maintained by Nanas Sound using AI coding agents. The commits tell the truth: most of the code was written in conversation, reviewed by a human, and shipped the same day. Fitting, for a tool about describing things in plain language and getting real objects back.

A word of caution

The engines run code a language model wrote. The worker puts real fences around it: static gates, restricted interpreter, memory and CPU limits, a wall clock, and a process-group kill when time runs out. That protects you from mistakes and prompt slips. It is still not a sandbox. Run it locally or behind your own auth, and if you ever expose it to strangers, put kernel-level isolation between them and the worker first.

About

Text-to-CAD tools in Elixir with build123d, OpenSCAD, replicad, and smlibpy.

Topics

Resources

Stars

38 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages