Steve's desktop CAD, read: an OpenCascade kernel, a Python feature API, and an optional Blender
mdjsonmcp2026-11-01 · 13 min · cad · blender · agents
Why read this
Solidtop 85%Reads a freshly open-sourced CAD app: what is OpenCascade, what is Blender, what the licences say, and measures how mesh volume differs from exact.
- Original analysis
- Runs on a laptop CPU
- Widely used
Developer tools & infraApache-2.0Practitioner teardown
How this was scored
- Is it new?
- 1 of 3: An incremental tweak
- Can I trust it?
- 2 of 3: Measures key facts from files, code or configs
- Can I run it?
- 2 of 3: Open code or weights with real limits
- Will I understand it?
- 2 of 3: Mechanism from first principles with figures
- Can I act on it?
- 1 of 3: General advice
- Will it last?
- 1 of 3: Relevant for months
- Does it affect many?
- 2 of 3: A widely used model, tool or lab release
- Only here?
- 2 of 3: A teardown or measurement few others did
Score 57 of 100, ranked 461 of 666 rated articles. Each question is answered 0–3 by hand, and a 3 is rare. How articles are scored
On 31 October, @10_X_eng posted: "We are going to just let this rip. Steve's Desktop CAD is now FOSS. No more excuses. Go make something." Nine minutes later came a second post: "I need everyone to remember I am just one dude working on this in my off time. So stay sane please." Both are reported: I read them through the fxtwitter mirror, which also showed 238 likes and 13 replies. It would not return the replies, so I could not read the licensing exchange about Blender that prompted this article. What follows is what the repository itself says.
Two questions. What is Steve's CAD, in the sense of what computes the geometry? And what does it do with Blender, which is GPL software, from inside an Apache-2.0 repository?
I cloned stevecad/steve shallow at commit b9709ca
(dated 1 November 2026, 57 MB, 1,541 tracked files) and read it. Labels used below:
measured is something I counted or computed here, reported is what a file or page
says, reasoned is my inference from the two.
- license
- Apache-2.0
- branch
- main
- tests
- 45 files
- source
- 8.2 MB
- commit date
- 2026-11-01
by size of tracked source at this commit, file counts in brackets; docs, data and vendored trees excluded
local clone, 2026-11-01 at 8e48265 — branch, commit, commitDate, fileCount, hasTests, languages, license, licenseFile, shallow, testFileCount
shallow clone: counts describe the pinned tree, not the history
What I did not run
I did not run Steve. The README's development route needs a pinned CadQuery fork from GitHub and
cadquery-ocp, a 436 MB (unpacked) wheel. I downloaded that wheel to read its metadata and
tried to install it with the fork into a throwaway virtual environment. The sandbox's permission
layer refused, and then refused to run the repository's own unittest modules. I did not try
to get around either refusal. So every statement about what Steve computes is from reading
code; where I measured something, it is my own re-implementation, named as such.
What it is
The README describes it in one sentence: "Local desktop CAD with the Studio-compatible modeling runtime, a PySide6 desktop host, and an assistant powered by Pydantic AI. CAD builds run on this machine." It adds: "This is pre-release software. Known coverage limits remain documented; not every CAD operation is certified."
The size of each part, measured by counting lines in tracked files:
| Part | Files | Lines |
|---|---|---|
src/steve_desktop (Python host, API, assistant) | 64 | 10,000 |
vendor/ Python, excluding Blender and the solver (feature API, drawings, designs) | 62 | 32,330 |
vendor/blender | 2 | 1,293 |
cad-web/src (TypeScript, TSX) | 587 | 110,930 |
vendor/solver/source (C++) | 25 | 42,848 |
tests | 45 | 4,653 |
Most of it is a web front end and a large vendored modeling runtime. The desktop application
itself, the part this repository is for, is about a tenth of the Python. docs/UPSTREAM.txt says
why: "The CAD frontend, modeling runtime, parser and shipped sketch-solver WASM are synchronized
from STEVE Studio", a hosted product. The desktop adds "Python loopback API and document store;
desktop host; Steve assistant bridge; local process lifecycle; WASM solver transport." In
other words, this is a local adapter around a modeling runtime that existed first as a service.
The kernel is OpenCascade, not Blender
A Steve document is Python source. cad_document.validate accepts three kinds: part,
design and blender. A part looks like this, from cad_document.bracket():
from steve import Model
model = Model()
with model.sketch("base_profile", on="XY", name="Base profile") as s:
s.rect("outline", (0, 0), (width, depth))
model.extrude("base", "base_profile", distance=thickness, name="Base plate")
model.box("upright", width, thickness, height, at=(0, depth-thickness, 0),
centered=False, op="join", name="Upright")
model.hole("base_holes", on="base.end", diameter=hole_diameter,
points=[((width-hole_spacing)/2, 20), ((width+hole_spacing)/2, 20)])
result = model.result()That is a parametric feature history written as readable calls with names: "base.end" is a
face reference that survives edits, which is the hard part of any parametric modeler. Top-level
numeric assignments become editable parameters; change_parameters rewrites them in the source
by character span. vendor/steve/model.py is 3,756 lines (measured) and defines the
operations: sketch, extrude, revolve, fillet, chamfer, shell, loft, sweep, patterns, mirror,
draft, hole, boolean combine and split, STEP import, sheet metal through a mixin.
Underneath, model.py imports cadquery as cq and, directly, OCP classes such as
BRepAlgoAPI_Fuse, BRepFilletAPI_MakeFillet, BRepOffsetAPI_MakeThickSolid and
ShapeUpgrade_UnifySameDomain. OCP is the Python wrapper of OpenCascade Technology (OCCT), the
open B-rep kernel. requirements.txt pins cadquery-ocp==8.0.1.1.0, and cad_kernel.py records
occt_version='8.0.1' in every build report (reported). A CadQuery fork at commit
6a8cf4fb supplies the rest.
So the kernel is OCCT. It stores a solid as boundary faces on exact surfaces, which is why the
bracket's hole is a cylinder with a radius and not a ring of triangles. cad_kernel.build calls
shape.isValid(), exports model.brep, and takes per-face type, area and centre from the B-rep
(face.geomType(), surface_area(face)). The mesh in preview.glb is produced later, for
display, by OCCT's native mesher run across threads.
The 2D constraint solver is a third engine. Sketch constraints go to FreeCAD's planegcs,
compiled to WebAssembly. vendor/steve_sketch.py starts a Node process running bridge.mjs
and talks to it over a pipe. vendor/solver/NOTICE lists the licence as LGPL-2.1 or later and
documents each STEVE change to the source. The source and build scripts are in the repository,
which is what the LGPL asks of someone who ships the binary.
What one build costs the machine
docs/LOCAL-COMPUTE-LIMITS.txt is a policy note and worth reading before you point an agent
at this. It says local builds have no default wall-clock timeout, only explicit cancellation.
CPU use follows a percentage of logical cores, 80 by default and settable to 100. Worker
admission "reserves half of physical RAM and estimates 1 GiB per worker", and the note itself
calls that "an estimate, not measured peak-memory scheduling". Booleans stay serial "as upstream
requires for reproducible geometry", while meshing runs on up to the job's share of cores.
On Windows the fork-based topology checks stay serial. All of this is reported text; I
checked it against cad_kernel.local_runtime, which sets the worker counts and replaces the
hosted deadline check with float('inf').
Where Blender comes in
Only a document of kind blender. cad_server.py sends it to local_blender.scene_jobs, a
single-worker executor, while every other kind goes to the CAD executor. The command line is in
local_blender.py:
<blender> --background --factory-startup --disable-autoexec --offline-mode -noaudio
--threads N --python-exit-code 1 --python vendor/blender/blender_run.py -- <mode> 0blender_run.py (1,186 lines, measured) starts with import bpy. It runs the document's
Python on an empty scene whose unit is one millimetre, evaluates modifiers, measures the result,
writes preview.glb and, on a full build, result.blend, four views rendered by Cycles on the CPU
at 16 samples, and an MP4 if the scene has a camera and an animation. The bundled assistant skill
for these documents says the same: "CAD links are imported meshes, not editable CAD solids."
A Blender scene can link a CAD part. LocalBlender.links builds the part with the CAD worker
first and hands Blender its preview.glb under a content hash. Geometry crosses the boundary as
a glTF mesh, "never as code", in the words of the runner's own docstring. It is one way: the CAD
side never receives Blender geometry back.
What follows from this is reasoned: in Steve, Blender is the presentation and organic-shape side. The bundled skill agrees, in effect, when it says to model things to be manufactured as parts or designs, with "STEP, drawings, simulation, CAM, checks", and to use Blender for the rest. The parametric kernel is OpenCascade; Blender never touches a bracket's fillet.
Mesh volume and exact volume
That boundary has a measurable consequence. The Blender path reports numbers computed from
triangles, in vendor/blender/blender_measure.py (107 lines of numpy, no bpy). Its volume is a
sum of signed tetrahedra from the origin, , and a mesh
counts as closed when every edge is shared by exactly two triangles. A B-rep kernel gets a
sphere's volume from an equation.
I re-implemented the three formulas (area, signed volume, closed test) rather than import the repository's file, and ran them in numpy 2.5.3 on a UV sphere of radius 10 mm. Exact values are 4,188.790 mm3 and 1,256.637 mm2. Measured:
| Longitudes | Triangles | Volume error | Area error |
|---|---|---|---|
| 8 | 48 | −23.15% | −12.36% |
| 16 | 224 | −6.26% | −3.18% |
| 32 | 960 | −1.60% | −0.80% |
| 64 | 3,968 | −0.40% | −0.20% |
| 128 | 16,128 | −0.10% | −0.05% |
An inscribed mesh always under-measures, and the error falls about fourfold per doubling.
That is a property of tessellation, not a defect in the code, but it means a Blender-kind
document's volume_mm3 is a render-resolution quantity while a part's is exact. The widget
recomputes this table in your browser and shows two more behaviours I read in summarize:
Delete one triangle and the closed test fails, so the object's area still counts but its volume
does not, and the scene gets an "Open (not watertight) meshes have no volume" warning (read
in the file, reproduced by my copy of the test). Duplicate the sphere and the volume doubles:
summarize warns when the extents of closed objects overlap but does not subtract the overlap.
One trap the repository avoids deserves a line, because it is easy to hit. Blender hands over
vertices as float32, and blender_run.py line 377 casts them to float64 before measuring.
I tested what happens otherwise. A 32-longitude sphere moved along (1, 1, 1) and summed in
float32 is off by 1.85% at a 1,000 mm offset and gives a negative volume at 10,000 mm; cast to
float64 after the float32 store, the error stays under 0.005% at every offset I tried, up to 100,000 mm.
The assistant
A language-model agent drives all of this. agent_features.py imports Pydantic AI and its
harness for file system and shell capabilities. cad_service.cad_tools defines the tool
surface: cad_read, cad_build, cad_inspect, cad_set_parameters, cad_export,
cad_restore, cad_revisions, cad_find_parts, cad_import_package,
cad_import_catalog_part, cad_api_reference, cad_source_package and blender_render
(read). local_mcp.py can expose the same tools over MCP. Edits use revision pinning, so a
stale edit is rejected, and cad_inspect returns named references so the model does not edit by
position.
The security statement is blunt, and I quote it because it is the most useful sentence in the
repository for anyone running a model against it: "All CAD execution is local. CAD source is
Python with the current user's permissions, not a security sandbox. Enabled agent tools execute
directly." cad_process.py filters the worker's environment to a short list (PATH, HOME,
temp directories) so API keys in the session do not leak to document code. That is process
isolation and cancellation, not containment.
The licence question, as the documents word it
I am not a lawyer and this is not legal advice. What is checkable:
What the repository says.
LICENSEis the Apache License 2.0 text.NOTICEreads "Steve / Copyright 2026 10-X-eng and Steve contributors / This product includes third-party software; see THIRD-PARTY-LICENSES/."vendor/blender/UPSTREAM.txt: "Source: synchronized from STEVE Studio's Blender runtime. Runtime reused with project owner permission."blender_run.pyhas no licence header of its own, andimport bpyis on line 29.
What the repository does with Blender's binary. blender_runtime.install downloads a pinned
archive of Blender 4.5.9 from https://download.blender.org/release/Blender4.5/, compares its
SHA-256 against a constant in the file, and unpacks it under the user's own runtime folder; it
refuses anything past 2 GB downloaded or 8 GB unpacked. discover also accepts a Blender the
user installed, found through a setting, the STEVE_BLENDER variable, PATH, or the usual
install folders. probe runs blender --version and rejects anything that is not 4.5. No Blender
binary is among the 1,541 tracked files (measured: the only paths containing "blender" outside
the web front end are Python, a workflow, a skill and a text file), and nothing under packaging/
or tools/ mentions it. The CI workflows download it to test with. I did not check what the
published installer contains, since I could not unpack it here; the Linux manifest I fetched from
dl.stevecad.studio listed version 0.4.1 while the repository carries notes up to 0.4.3.
What Blender says. On blender.org/about/license: Blender "is released under the GNU General Public License", its source "by default being licensed as GNU GPL Version 2 or later", and "That is also the license to use for any distribution of Blender binaries" refers to the newer GPL 3 or later. On scripts: "Blender's Python API is an integral part of the software ... The GNU GPL license therefore requires that such scripts (if published) are being shared under a GPL compliant license." The same page also says: "Under certain conditions, it is possible to make Blender, or scripts that make use of the Blender Python API (bpy), work with proprietary or commercial software", and offers the Foundation's feedback on compliance. It adds that what you create with Blender is yours.
Put those side by side and the shape of the question is visible, without an answer:
- Steve does not, by the evidence I read, redistribute Blender itself. The user's machine fetches it from the Blender Foundation. That is a statement about the binary.
blender_run.pyis a published script that usesbpy, in an Apache-2.0 repository. Blender's page says published scripts of that kind should be under a GPL-compliant licence. Whether Apache-2.0 qualifies, and whether a script that runs only inside a separately installed process is covered the way an add-on loaded into the UI is, is the part I cannot settle. The usual position I remember is that Apache-2.0 is compatible with GPL version 3 and not version 2 only, and Blender's binaries are described as version 3 or later; I could not reach the FSF's list from here to confirm the wording, so treat that as unverified.- The Foundation's page says it opens a conversation first in a reported violation.
If you plan to build on the Blender half, the actionable route is the one the Blender page offers, which is to ask. If you build only on the part/design half, Blender never enters.
A smaller observation from the same folder, measured and not a conclusion: the 461-file
THIRD-PARTY-LICENSES/index.json has no entry naming OCP or OpenCascade, while the
wrapper wheel's own metadata states Apache-2.0. OCCT itself is a separate project with its own
licence; I did not verify what ships in the installer, so I make no claim about it.
What I would check next
- Install the pinned fork and wheel on a machine that allows it, then run
python -m unittest tests.test_cad_localandpython -m steve_desktop --smoke-test. The README names that smoke test as the offline check. - Build the bracket, change
thickness, and confirmbase.endstill resolves. That is the claim the whole feature API rests on. - Compare a Blender-kind document's
volume_mm3with the same shape from a part; the table above predicts the gap. - Unpack the 0.4.x installer and list its contents against
THIRD-PARTY-LICENSES.
What this does and does not tell you
Steve is a real CAD stack, not a Blender wrapper: OpenCascade for solids, planegcs for sketches, Python for the authoring surface, a browser viewer on three.js. It is also young. The README calls it pre-release; the author calls himself one person working in spare time; and its modeling runtime arrives from a hosted product, so some decisions here predate the desktop. I read it; I did not run it.
Blender is a plug-in for scenes and renders. It is fetched, pinned by hash, and kept at arm's
length by a process boundary, a glTF handoff and the rule that no code crosses. Whether that
arm's length satisfies the GPL for a script that does import bpy is the open item, and it is
Blender's and the author's to answer, not mine.
Related: Blender 5.3's Gaussian splats reads another part of Blender's source, with its licence note.