# Burr
Burr is a design-rule linter for CAD-as-code workflows.
It checks generated design data and artifacts for mechanical mistakes before
they become prints, prototypes, or expensive debugging sessions.
```txt
design file -> generated part -> burr-design-data.json -> Burr checks -> receipt
```
Burr is not a constraint solver, FEA engine, or universal CAD brain. It does not
design the part. It checks whether generated CAD violates known mechanical
rules.
## Why
Image review is useful, but not enough. A screenshot can show that something
looks suspicious; it cannot reliably prove exact hole distances, hidden
clearances, source/STEP freshness, or rule-specific pass/fail.
Burr turns design data into measurable receipts:
```txt
M3 loaded mounting hole
measured center-to-edge = 8.0 mm
required center-to-edge = 10.2 mm
result = fail by 2.2 mm
```
## Install
See [INSTALL.md](INSTALL.md) for current GitHub and uv install paths.
For local development:
```bash
cargo test
uv sync --all-packages
npm run check
```
Install the Rust CLI from GitHub:
```bash
cargo install --git https://github.com/fraylabs/burr burr
burr --version
```
Run the CLI from a local checkout:
```bash
cargo run -- --version
cargo run -- check examples/linear-actuator-bad
cargo run -- check examples/linear-actuator-good
```
Run the build123d adapter examples:
```bash
uv sync --all-packages
npm run check:build123d
```
The build123d examples commit only `design.py`. `actuator.step` and
`burr-design-data.json` are generated by the example scripts and ignored by git.
For local scripts that import the helper directly, run with:
```bash
uv run --package burr-build123d python your_design.py
```
## Commands
```bash
burr --version
```
`check` finds `burr-design-data.json`, runs freshness checks and rulepack
checks, then writes `burr-receipt.json` beside each design data file.
`stamp` computes `sha256` and `size_bytes` for declared source and generated
artifact files.
## Build123d Helper
Burr does not replace build123d. The optional helper records design data while
your normal build123d file creates geometry.
```python
from build123d import Box, BuildPart, Locations, export_step
from burr_build123d import BurrDesignData, DESIGN_DATA_FILE, m3_clearance_hole
design = BurrDesignData(
artifact_id="my-actuator",
artifact_type="actuator_mount",
process={"kind": "FDM", "material": "PETG", "nozzle_mm": 0.4},
)
design.source("design.py")
design.artifact("actuator.step")
design.part("housing", bbox_min=(-42, -16, 0), bbox_max=(42, 16, 26))
with BuildPart() as housing:
with Locations((0, 0, 13)):
Box(84, 32, 26)
m3_clearance_hole(
design,
feature_id="m3_lower_left",
part="housing",
center=(39.5, -8, 8),
axis=(1, 0, 0),
role="loaded_mount",
)
export_step(housing.part, "actuator.step")
design.write(DESIGN_DATA_FILE)
```
That one helper call cuts the hole in build123d and records the feature Burr
checks. Burr core still reads only `burr-design-data.json`, so other CAD tools
can use the same contract.
## Design Data
A lintable CAD artifact folder contains `burr-design-data.json`.
This file is the language-agnostic contract. It can be emitted by build123d,
CadQuery, OpenSCAD, JavaScript CAD, Rust CAD, Fusion scripts, or any tool that
can write JSON.
```json
{
"schema_version": "burr.design-data.v1",
"artifact_id": "linear-actuator-bad",
"artifact_version": "0.1.0",
"artifact_type": "actuator_mount",
"units": "mm",
"source": {
"path": "source.py",
"sha256": "..."
},
"artifacts": [
{
"kind": "step",
"path": "actuator.step",
"sha256": "..."
}
],
"parts": [
{
"id": "housing",
"bbox_mm": {
"min": [-42, -16, 0],
"max": [42, 16, 26]
}
}
],
"features": [
{
"id": "m3_lower_left",
"part": "housing",
"kind": "clearance_hole",
"fastener": "M3",
"diameter_mm": 3.4,
"center_mm": [39.5, -8, 8],
"axis": [1, 0, 0],
"role": "loaded_mount"
}
]
}
```
## Rulepacks
The included actuator mount rule checks loaded M3 clearance holes:
```json
{
"schema_version": "burr.rulepack.v1",
"id": "actuator_mount",
"version": "0.1.0",
"rules": [
{
"id": "m3_loaded_hole_edge_distance",
"kind": "hole_edge_distance",
"applies_to": {
"kind": "clearance_hole",
"fastener": "M3",
"role_any": ["loaded_mount", "mount", "housing_mount"]
},
"min_center_to_edge_diameter_multiple": 3.0
}
]
}
```
## Versioning
Burr has three versioned surfaces:
```txt
Burr package version -> CLI/library behavior
Design data schema version -> JSON shape Burr can read
Rulepack schema version -> rule syntax Burr can execute
```
Receipts include all three:
```json
{
"schema_version": "burr.receipt.v1",
"burr_version": "0.5.0",
"artifact_version": "0.1.0",
"rulepack_version": "0.1.0",
"compatibility": {
"design_data_schema_version": "burr.design-data.v1",
"rulepack_schema_version": "burr.rulepack.v1"
}
}
```
Unsupported design data or rulepack schemas fail lint instead of silently producing
untrustworthy receipts.
Legacy `fray-cad.json` files with schema `fray.cad.artifact.v1` are still read
for transition, but new integrations should emit `burr-design-data.json`.
## Example Result
Bad actuator:
```txt
FAIL examples/build123d-actuator/bad/burr-design-data.json -> <not written>
1 problem:
1. M3 loaded hole m3_lower_left is too close to the edge.
Measured center-to-edge: 8 mm
Required center-to-edge: 10.2 mm
Short by: 2.2 mm
Try moving the hole inward or increasing the surrounding part size.
```
Fixed actuator:
```json
{
"status": "pass",
"measured": {
"center_to_edge_mm": 12,
"wall_to_edge_mm": 10.3
},
"required": {
"center_to_edge_mm": 10.2,
"wall_to_edge_mm": 8.5
},
"margin_mm": 1.8
}
```
## Status
Early prototype. Current checks are design-data-based. Future versions may derive
more facts directly from STEP topology.