Roshera is an agent runtime for geometry. The product is the kernel and the bridge it exposes: a native Rust B-Rep engine whose primitives, topology, and operations carry enough semantic structure for an LLM to query, reason about, and drive directly. Humans orchestrate; agents execute. The React/Three.js frontend that ships in this repo is one client of that runtime — it talks to the kernel over REST + WebSocket the same way an external agent would.

The differentiator is the readable surface: geometry is not just triangles, it is a queryable model with named features, intent, and history. And every operation an agent runs returns the kernel's full validity certificate — a nine-conjunct AND over brep-validity, watertightness, manifoldness, orientation, self-intersection, construction consistency, feature consistency, and two mesh-quality dimensions — computed synchronously under the model write lock and failing closed. The geometry carries its own proof instead of a render that merely looks right.

A second, render-based reconcile views each solid from fourteen viewpoints and cross-checks what it sees against what the kernel claims. That layer is advisory: it runs off the write lock and does not gate a mutation. The distinction matters, so it is stated here rather than blurred — see The Certificate for the anatomy, and Status for what's actually usable today.

Why a certificate matters

AI can generate CAD everywhere now. Nobody can trust what it generates — because the standard ways of checking are blind. We built a benchmark that injects four classes of silent geometric lie (flipped normals, self-intersections, torn seams, non-manifold facets) into sound parts and asks each verifier:

Verifier Lies caught
B-Rep validity check alone 0 / 4
"Mesh looks closed" heuristic 2 / 4
A frontier vision model, shown the render misses the flagships
Roshera's certificate 4 / 4

The benchmark is reproducible (geometry-engine/tests/injected_defect_benchmark.rs; every table cell is derived from measured verdicts at run time, never hardcoded). Two more artifacts back the same claim:

  • Adversarial predicate census (geometry-engine/tests/adversarial_predicate_census.rs) — 12,000 near-degenerate point-in-polygon cases (edge-graze, sliver, arc-chord families) checked against a BigRational oracle. The shipped floating-point ray-cast variants flip sign on hundreds of them; the exact predicate core matches the oracle on every decided case — zero mismatches. This is why the certificate can afford to be exact where it counts.
  • Measured certificate cost (geometry-engine/benches/certificate_cost.rs) — the certificate is cheap enough to run synchronously on the hot path: sub-millisecond cold on a box, a couple hundred milliseconds on an all-edges filleted cube, and a sub-microsecond cache read on any unmutated re-read. Numbers in Performance.

The same certificate acts as the referee for autonomous design. A certified exploration harness (geometry-engine/examples/certified_exploration.rs) samples rocket-engine variants deterministically from a seed, lets the certificate kill every unsound one, and ranks the sound survivors by wall-material volume at a fixed internal-volume target — so the winner is provably the most material-efficient sound design in the sweep, not merely one that renders well. An optimizer without the certificate happily picks corpses; ours can't.

Dark Mode Light Mode
Dark Mode Light Mode

Soundness is not fidelity

The certificate above speaks only to topology — closed, manifold, oriented, non-self-intersecting. It is silent on shape, and the case that proved the gap lives in this repo: a loft of two circles densified its vertex correspondence to a floor of 8 and produced an octagonal frustum — genuinely closed, genuinely manifold, certified SOUND, and 9.97% short of the requested cross-section. Nothing in the verdict could see it, because nothing in the verdict compared the RESULT to the REQUEST.

geometry-engine/src/queries/fidelity.rs is that comparison: requested / measured / signed relative deviation per named quantity, reported beside the certificate and never folded into sound — the topology of that frustum genuinely is sound, and flipping the verdict would swap one false statement for another. The sign is the diagnosis: negative means the kernel built less than was asked for (the octagon class — an inscribed polygon where a curve was requested, the class that costs material and function); positive means more (typically a smooth interpolation running outside a coarsely-sampled request). A quantity that cannot be measured is stated absent with a reason, never as 0 — "requested 7, got 0" would be a louder lie than silence — and when nothing was measured at all, the fidelity_ok key is omitted rather than set true, so a client keying off the boolean cannot inherit a silent pass.

The statistic is calibrated against known cases, which is how you know a given number is trustworthy: an analytic cylinder reads ~1e-16, the fixed loft's residual reads 0.19%, the octagon defect reads 9.97%. The default 2% band that separates them is a product decision, not a law of geometry — the raw numbers ride on every quantity so a caller with a tighter contract applies its own band without re-measuring. Today the block is attached by the dedicated endpoints with a measurable request — /api/geometry/{cylinder,box,revolve,nurbs_loft} (attach_fidelity, api-server/src/main.rs); every other op attaches nothing rather than a block of zeros.

Architecture

roshera-backend/       Rust workspace (10 crates)
  geometry-engine/     B-Rep kernel: exact predicates, NURBS math, primitives, topology, operations, tessellation, 2D drawings
  ai-integration/      LLM providers (Claude, OpenAI) + vision pipeline
  timeline-engine/     Event-sourced design history with branching
  session-manager/     Multi-user RBAC + Argon2 auth + durable API keys
  assembly-engine/     Instances, mates, SE(3) solve, DOF, certified interference
  export-engine/       STL, OBJ, STEP (AP242 export + import, round-trip tested), encrypted .ros (AES-256-GCM)
  ros-format/          Native .ros serialization
  verdict-harness/     Multi-agent verdict/consensus harness
  api-server/          Axum REST + WebSocket API
  shared-types/        Common type definitions

roshera-app/           React + Three.js + TypeScript browser client
roshera-mcp/           MCP server exposing the kernel to agents (incl. the six-gate constraint layer)
roshera-eval/          Certificate-scored agent evaluation scenarios (18)
roshera-rl/            Certificate-scored episode loop over the MCP surface (JSONL trajectories;
                       task corpus and model policies not yet built — see below)

Direction

The kernel is the product. The viewport you see in the screenshots is one client of it, written to prove the bridge works at human latency. The same REST + WebSocket surface drives agents.

Where this is going: agents that pick faces, fillet edges, run sections, and walk a part the way a designer would — by reasoning over the readable surface (named features, persistent IDs, the timeline of events that produced the current state), not by parsing triangle soup. An MCP server already exposes the kernel to agents directly — the demo above was built through it, one tool call per feature, with each result verified sound and watertight before the next step. The timeline already answers provenance questions: GET /api/evidence-pack bundles a document's recorded history, its per-operation certificates, and the design notebook into one reviewable artifact. Next is embeddings and retrieval over that history so an agent can ask "how did this corner get to be 4mm" without re-deriving the model from scratch.

The constraint layer

Every layer of an agent harness either asks (steering — an instruction a model may decline) or prevents (constraint — the wrong thing is inexpressible). Measured on this project, constraints held identically across models while steering degraded on weaker ones. So six policies are implemented not as prompt text but as typed refusals in roshera-mcp/src/gates.ts, at the single dispatch choke point every call path runs through — direct tool mount, invoke, and cad_program's batch loop all call the same wrapped handler, so nothing is gated twice and nothing escapes:

  1. Refusal cache — a typed refusal is a stable fact, not a retry target. An identical re-issue is answered with the same refusal from cache until some state-changing call succeeds.
  2. Intent gate — a solid-mutating call with no open design-intent checkpoint is refused, naming the call that opens one. Generic sequence names ("step 3") are refused too, or the gate would be satisfied by exactly what the policy exists to prevent.
  3. Unsound-base gate — mutating a solid whose live kernel verdict is unsound is refused unless the caller passes acknowledge_unsound: true (deliberate repair flows). Never cached: the fact is live kernel state someone else can change.
  4. Sheet-export gate — an exported PDF/DXF carries no ambient certificate and can never re-verify itself, so a sheet whose live certificate is stale or dangling never reaches disk; there is no bypass for that case.
  5. Single-point-run gate — measured failure: an agent burned 1.3M tokens laying out a 256-point gear profile one point per call. After 8 consecutive single-point sketch calls, the 9th is refused, naming the one-call polyline path. Any other call resets the counter, so small sketches never meet the gate.
  6. Verification-scope gate — the intent gate forces intent to be declared before building; nothing forced anyone to look at what came out (the 9.97% loft above shipped certified sound). Closing a checkpoint after mutations with no verify_part/verify_claim is refused. One explicit escape — skip_verification: true on the closing call — so the constraint is escapable but never silent.

The header comment in gates.ts states each gate's reasoning, including where each deliberately fails open or closed and why.

roshera-rl — the episode loop

roshera-rl/ runs parallel, isolated, certificate-scored episodes over the real MCP tool surface and writes JSONL trajectory records. It is an episode loop with certificate-based rewards whose task corpus and model policies are not yet built — not an RL environment you could train an agent in today, and its own README carries a "What this does not prove" section holding it to the same standard as this one.

What is live-verified against a running backend: 8 concurrent episodes, each in its own document and its own kernel model, each seeing exactly one solid — its own; all 16 dimensional claims (two per episode) verified against real geometry by verify_claim; the fidelity block present on all 8 at machine epsilon (signed deviation 1.4e-16 — the calibration case from the fidelity section, measured over the wire). Reward is a named vector — soundness, worst signed fidelity deviation, refusal and call-failure counts — never scalarized, because weighting them is a training choice the kernel cannot justify; an unmeasurable component is a stated gap, never a zero. And in the same breath, what is not yet true: the task corpus is one task (a cylinder with volume + surface-area claims — a pair that provably cannot discriminate a conjugate cylinder satisfying both, a blind spot the task file documents and pins in its tests); no language model has ever driven an episode — every run so far used a scripted reference policy, so this proves the loop, not the agent; recipe replay has not worked live yet (every saved live trajectory carries step_count: 0 with its stated absence — the embedded-steps mechanism has never carried a real step out of a live episode); and nothing outside the repo can consume the output — there is no Gym/verifiers adapter. Reproduce with node bin/run-batch.mjs --concurrency 8 --repeats 8 against a running backend (see roshera-rl/README.md for what a live run requires).

Status

What follows is honest about what's tested versus what's implemented but rough. Measured perf numbers are in Performance. The kernel carries a red-test ratchet (geometry-engine/KNOWN_REDS.md) whose tolerated-failure allowlist is currently empty — no known-failing test is carried as accepted.

Layer Component Status
Math Vector3, Matrix4, Quaternion Tested
B-spline, NURBS evaluation Tested
Exact predicates (orient2d, point-in-polygon, shoelace sign) 12,000-case adversarial census vs a BigRational oracle — zero mismatches where the float ray-cast variants flip sign
Primitives Box, Sphere, Cylinder, Cone, Torus B-Rep topology with Euler validation
Topology Manifold detection, adjacency Tested
Tessellation Per-surface dispatch, adaptive subdivision Watertight for analytic surfaces and extruded/polyline curved profiles; residual non-watertight welds remain only on curved∖curved boolean corefinement (e.g. cross-drilled cylinders)
Operations Extrude (draft, taper, twist) Implemented; side-faces sound and watertight, including partial-arc walls as trimmed cylinder faces
Boolean (union, intersect, difference) Works, incl. shared-imprint corefinement for NURBS × analytic cuts; coincident-face weld has known ordering sensitivity
Fillet (constant-radius) Works; unsupported vertex-blend corners return a structured typed refusal (the reason, not a wrong blend)
Chamfer Works; closed-edge rim chamfers are plane–cylinder only — cone/torus/revolve-seam rims return NotImplemented
Offset, Sewing Implemented, lightly tested
Revolve (full/partial) Works; piecewise-analytic bands (arc → exact Torus/Sphere) on the typed strict path, 360° profiles without inner loops
Sweep (single path) Implemented; multi-guide not done
Loft (ruled + NURBS-skinned) Ruled loft implemented. nurbs_loft skins a single G2-continuous (degree-3) NURBS lateral through closed section rings on the proven watertight cylinder topology — the kernel's first freeform NURBS face
Certificates Soundness (per-operation) Nine-conjunct AND, computed synchronously under the write lock on every mutating-op response, memoized per solid, failing closed
Fidelity (requested vs measured) Reported beside the certificate — never folded into sound — for the ops with a measurable request: cylinder, box, revolve, loft. Signed deviation per named quantity; unmeasurable quantities stated absent with a reason, never as 0
Sketch — DOF, conflict witnesses Minimal conflicting constraint sets via QuickXplain; honest minimal: false rather than a fabricated core
Rebuild (timeline mould) Per-feature verdicts; global soundness re-measured from the resulting B-Rep
Sketch 2D Constraint solver (Newton-Raphson) + DR-plan Works; per-entity constrainment exposed as queryable kernel facts. Parametric sketch (psketch_*) drives the same solver over the agent surface
Drawings Certified 2D sheets Multi-view layout, SECTION A-A, hole tables, GD&T feature-control frames resolved against disclosed datum reference frames, and drawing_export_sheet to PDF/DXF/SVG — all derived from the live B-Rep
Perception Spatial-relationship queries Ordered ray crossings with exact hit points, SDF X-ray occupancy slices (occupancy_view, non-deceivable), view-coverage honesty (part_coverage — which faces the standard views leave unseen), section cutaways — an agent asks instead of parsing meshes
Identity Persistent IDs + labels FacePid carries durable face identity across re-extrude and timeline replay (fillet/chamfer/pattern lineage not yet minted); labels pin names to faces/edges/planes and resolve-or-refuse (never a wrong entity)
Durability Event-log persistence, replay, quarantine Works; boot is a full replay (boot-time snapshots not used). Verified live by kill + resurrect. GET /api/evidence-pack bundles recorded history + certificates (absent → null with reason, never fabricated) + notebook + provenance-labeled mass properties
Agent surface MCP minimal surface + meta-tool funnel Works; 31 default tools (18 core verbs + the 10-tool psketch_* sketching family + find_tool/describe_tool/invoke), the ~90-tool long tail reachable via invoke at fixed cost with identical schema validation. Six typed dispatch gates at the one choke point every call path crosses (see The constraint layer). blackboard_add_entry (agent→human notebook) is in the default surface
Assembly Instances, mates, SE(3) solve, interference Gauss-Newton SE(3) solver with an analytic Jacobian, DOF/mobility analysis, interactive drag, and a dozen-plus mate/joint kinds (Cam/Path/Symmetric are typed but not numerically enforced). Certified interference: static overlap (EPA-backed, enclosure-aware) plus continuous nonlinear time-of-impact swept clearance via Parry — min_clearance = raw_min_clearance − ε, a conservative lower bound, with a typed refusal when a pair's distance is unsupported
Export STL, OBJ, encrypted .ros Works
STEP (AP242) Export and import implemented (tiered writer + parser); primitive and blended-flange round-trips assert topology, validity, and soundness are preserved
AI Claude + OpenAI providers Works
Vision pipeline Implemented
Natural language command parsing Works for common commands
Infrastructure Timeline (event-sourced history) Works. EventCertificate projection is defined and honestly reported (per-op-class-honest), but no producer records it on the timeline yet — the evidence-pack reports it as absent rather than inventing one
Session manager (multi-user, RBAC) Works
Blackboard (agent↔human notebook) REST + MCP + live frontend panel
Auth posture Secure by default (auth enforced on an empty environment; bypass requires the explicit, loudly-logged ROSHERA_DEV_INSECURE=1). Argon2 password auth; API keys persist across restart with a full provision/list/revoke lifecycle (revoked stays revoked). Residual: timeline authorship is taken from the request body, so an authenticated caller can mislabel who authored an event
Frontend React + R3F viewport, toolbar, chat Works; rough around the edges

For a capability map maintained against the source tree rather than intent — including the four places a check is deliberately narrower than its name — see Envelope.

Performance

Numbers below are labeled with the date, machine, and build profile they were taken on — the profile matters: the workspace's default bench profile builds with full LTO (lto = true, codegen-units = 1), and disabling LTO measures ~3–4× slower on these benches. The certificate table below is the default full-LTO profile; the older math/primitive tables were taken under the previous LTO=off methodology and are marked for re-measurement. These are internal regression figures, not comparisons against any third-party kernel. Reproduce with the commands at the end. Full detail in BENCHMARKS.md.

Certificate cost (certificate_cost)

The nine-conjunct soundness certificate, computed synchronously under the write lock. Cold is the price paid once per mutating op on an empty cache; memoized is the cache read on any unmutated re-read. Measured 2026-07-21 on a quiet machine (i7-1355U).

Solid Cold (per mutation) Memoized re-read
Box (6 faces) 0.29 ms < 1 µs
Bored boolean (f7 straddling bore: union + off-centre through-bore) 38.5 ms < 1 µs
Filleted cube (all edges, r3) 224.5 ms < 1 µs

The cold cost scales with topological complexity; memoization collapses every unmutated re-read to under a microsecond, which is what makes a synchronous certificate on the hot path affordable.

Math microbenchmarks (geometry_bench)

Measured on an earlier quiet run; re-measurement pending a quiet machine.

Operation Time
Vector3 dot 500 ps
Vector3 cross 884 ps
Vector3 normalize 1.68 ns
Vector3 add 984 ps
Matrix4 multiply 5.14 ns
Matrix4 inverse 29.4 ns
Matrix4 transpose 5.50 ns
Point3 distance 505 ps
Point3 translate 868 ps

Primitive creation (geometry_bench)

Full B-Rep topology construction with a fresh BRepModel per iteration. Measured on an earlier quiet run; re-measurement pending a quiet machine.

Primitive Time
Box 65 µs
Sphere 49 µs
Cylinder 50 µs

Tessellation and Boolean (structure of the current suites)

The older synthetic rows — a flat "1M-triangle" tessellation estimate and "1k-face" Boolean timings — were retired as measurement concepts when their harness was replaced. The current tree measures:

  • Tessellation (tessellation_bench) — per-surface (box, sphere, cylinder, cone, torus, curved NURBS) at coarse / default / fine chord-tolerance tiers, rather than one extrapolated triangle count.
  • Boolean (boolean_classification) — named real parts (box∪box overlap, coincident boss + cap merge, 3-prism chain, box−cylinder poke, straddling f7 bore, box∩cylinder) so timings track the corefinement paths the kernel actually exercises, not a synthetic face count.

Fresh timings for both are omitted here rather than published from a loaded machine; run the commands below on a quiet host to reproduce.

Coverage gaps

  • NURBS surface eval and memory-per-vertex — no timing bench in the current tree; the old estimates were retired with the internal suite.
  • Delete primitives — correctness tests only (delete_solid, delete_face, cascade, orphan cleanup); no Criterion target.
  • 2D sketch creation (sketch2d) — a large subsystem with a broad passing correctness suite (185 integration + 338 unit tests after the constraint-solver campaign); the sketch_solver bench times the solve step, not sketch creation.

Reproduce

cd roshera-backend
# Default profile = the workspace's full-LTO release settings (this is what
# the certificate table was measured with; expect a long first compile).
cargo bench -p geometry-engine \
  --bench geometry_bench --bench certificate_cost \
  --bench tessellation_bench --bench boolean_classification

Getting Started

# Backend (needs a reachable PostgreSQL — see Prerequisites)
cd roshera-backend
cargo run --bin api-server
# API on http://localhost:8081, WebSocket on ws://localhost:8081/ws

# Frontend (separate terminal)
cd roshera-app
npm install
npm run dev
# UI on http://localhost:5173 (proxies /api and /ws to localhost:8081)

Auth is on by default — a fresh clone with an empty environment enforces authentication, so bare API calls return 401. For a purely local dev run, start the backend with ROSHERA_DEV_INSECURE=1 (the bypass is deliberate and loudly logged, never a default); otherwise provision an API key and send Authorization: ApiKey <key> on every request.

Docker

roshera-backend/Dockerfile builds a two-stage release image of the api-server (builds with Rust 1.84, exposes 8081, /health liveness probe); it too needs DATABASE_URL pointing at a reachable PostgreSQL. The docker-compose.yml beside it is currently stale — it publishes ports 3000/9090, which the server does not listen on, and defines no database — so build and run the image directly rather than relying on docker compose up.

Prerequisites

  • Rust, recent stable (no MSRV is pinned; the Docker image builds with 1.84)
  • PostgreSQL reachable at DATABASE_URL (default postgresql://postgres:postgres@localhost/roshera) — the server requires it at boot
  • Node.js 20.19+ (Vite 8 requirement)

API

# Create a box (returns the tessellated mesh + its soundness certificate).
# Assumes a server started with ROSHERA_DEV_INSECURE=1; on a secure run add
#   -H "Authorization: ApiKey <key>"
curl -X POST http://localhost:8081/api/geometry \
  -H "Content-Type: application/json" \
  -d '{"shape_type": "box", "parameters": {"width": 10, "height": 10, "depth": 10}, "position": [0, 0, 0]}'

The dedicated primitive/op endpoints with a measurable request — /api/geometry/cylinder, /api/geometry/box, /api/geometry/revolve, /api/geometry/nurbs_loft — also carry the fidelity disclosure described above in their perception block, beside the certificate. The generic POST /api/geometry shown here does not attach one.

// WebSocket
const ws = new WebSocket("ws://localhost:8081/ws");
ws.send(JSON.stringify({
  type: "GeometryCommand",
  data: {
    command: {
      cmd: "CreatePrimitive",
      primitive_type: "Sphere",
      parameters: { params: { radius: 5.0 } }
    }
  }
}));

Logo

The Roshera mark is a Boolean union of a rectangle (2x × 3.236x) and a circle (radius x).

License

Functional Source License 1.1 (FSL-1.1-Apache-2.0) — Fair Source.

You may use, copy, modify, and redistribute Roshera for any purpose — including commercial use — except offering a product or service that competes with it (e.g. a competing geometry kernel, CAD engine, or hosted service built on this code). Internal use, education, research, and building non-competing products on top of Roshera are all permitted.

Every release automatically converts to Apache 2.0 two years after publication — today's code is tomorrow's open source.

For licensing beyond the grant (OEM/embedding, competing-use exceptions): [email protected].