~/satyajit

Steve's desktop CAD, read: an OpenCascade kernel, a Python feature API, and an optional Blender

mdjsonmcp

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

stevecad/steve@8e48265 · snapshot 2026-11-01
tracked files
1,541
license
Apache-2.0
branch
main
tests
45 files
source
8.2 MB
commit date
2026-11-01
source by language
TypeScript6.7 MB(589)Python877.5 kB(137)CSS555.3 kB(82)JavaScript43.5 kB(10)PowerShell2.0 kB(2)HTML1.8 kB(2)

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:

PartFilesLines
src/steve_desktop (Python host, API, assistant)6410,000
vendor/ Python, excluding Blender and the solver (feature API, drawings, designs)6232,330
vendor/blender21,293
cad-web/src (TypeScript, TSX)587110,930
vendor/solver/source (C++)2542,848
tests454,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> 0

blender_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, V=16∑iai⋅(bi×ci)V = \tfrac16 \sum_i \mathbf a_i \cdot (\mathbf b_i \times \mathbf c_i), 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:

LongitudesTrianglesVolume errorArea error
848−23.15%−12.36%
16224−6.26%−3.18%
32960−1.60%−0.80%
643,968−0.40%−0.20%
12816,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.

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:

  1. 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.
  2. blender_run.py is a published script that uses bpy, 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.
  3. 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

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.

Cite this article

For attribution, please use the following reference or BibTeX:

Satyajit Ghana, "Steve's desktop CAD, read: an OpenCascade kernel, a Python feature API, and an optional Blender", ai.thesatyajit.com, November 2026.

bibtex
@misc{ghana2026stevedesktopcad,
  author = {Satyajit Ghana},
  title  = {Steve's desktop CAD, read: an OpenCascade kernel, a Python feature API, and an optional Blender},
  url    = {https://ai.thesatyajit.com/articles/steve-desktop-cad},
  year   = {2026}
}
share