classcad pdm·in development, not released
A PDM for ClassCAD models that you host yourself. Parts, assemblies and the code that generates them, with versions, branches, releases and bills of material. ClassCAD opens, checks and measures every file that comes in.
reads OFB STEPwrites OFB STEP SCGREST APIon Postgres
ENG-3000Enginev1A
ENG-2001Piston assemblyv1A
ENG-2002Connecting rod assemblyv1A
ENG-1001Crankshaftv1A
ENG-1002Connecting rodv1A
ENG-1003Pistonv1A
ENG-1004Piston pinv2B
ENG-1005Origin markerv1A
BRK-2001Base platev2B
BRK-2002L-bracketv1A
BRK-2003Guide rodv1A
BRK-2004Locating pindraft
BRK-2005Square washerdraft
FST-0022Hex bolt M22v1A
FST-0023Hex nut M22v1A
MSC-0001AWV logo cubev1A01Documents
A part, an assembly or a generator is a document with a number. Its file goes to ClassCAD in a job that opens it, checks it and measures it. A file it cannot read fails, and says why.
Drag a file onto the page, or click one
02Scheduler
Jobs, generator runs and editing sessions reach ClassCAD through one port. Behind it, the scheduler keeps a pool of engines on your server. It keeps each session on the engine that holds it, sends new work to the one with the most room, keeps heavy imports away from interactive work, starts engines when it gets busy, retires them when it is quiet and restarts one that crashes.
- affinity
- placement
- heavy jobs
- scaling
- health
room for 4 more: max = 6
The scheduler's rules with its defaults, except max_sessions = 4 and heavy_workers = 1 to keep the pool small. Times are shortened: it checks the pool every 30 s and retires an engine after 10 idle minutes.
03Versions
Saving writes into the draft and overwrites it. A version is a step you take: a frozen state with a number. Branch from any version, take a branch back over, release what is done. It works the way Onshape does; the difference is where it runs.
The crankshaft, ENG-1001: version 1 is released as revision A. The draft of main builds on it. Save something.
- v1InitialA
- maindraft · as its versionyou are here
04Assemblies
An assembly names its parts by document number. Making a version pins every one of them to the version it was built with, so a released engine is always the same engine. When a part moves on, the assembly says so, and you take the update when you want it.
AssemblyENG-3000 reserved by anna
Engine
- Parts
- 05 different
- Sub-assemblies
- 02 different
- Volume
- 0cm³
| Item | In the model | Document | Version | Qty |
|---|---|---|---|---|
| 1 | Crankshaft | ENG-1001 Crankshaft | v1A | ×1 |
| 2 | PistonAsm | ENG-2001 Piston assembly | v1Ainside | ×3 |
| 3 | Bearing | ENG-1004 Piston pin | v1Arev B | ×1 |
| 4 | Piston | ENG-1003 Piston | v1A | ×1 |
| 5 | PleulAsm | ENG-2002 Connecting rod assembly | v1A | ×3 |
| 6 | Pl | ENG-1002 Connecting rod | v1A | ×1 |
| 7 | origin | ENG-1005 Origin marker | v1A | ×1 |
ENG-1004 is used by ENG-2001 Piston assembly, which is used by ENG-3000 Engine. Where-used is one click on every document.
05Editor
Edit opens the draft in Buerligons, our CAD app, right inside the PDM. It reserves the draft while you work, so nobody overwrites what you are changing. ClassCAD runs in your browser tab or on your server.
1 Change LBracket_Thickness on the right
2 Save into the draft: the red dot, or ⌘ S
PartLBracket
Features
- 01Sketch
- 02Extrusion
- 03WCS_Hole2-Bottom
- 04Cylinder
- 05Subtraction
- 06WCS_Hole2-Top
- 07WCS_Origin
- 08WCS_Hole1-Bottom
- 09Cylinder0
- 10Subtraction0
- 11WCS_Hole3-Bottom
- 12Cylinder1
- 13Subtraction1


Expressions
- LBracket_Thickness
- 10mm
- LBracket_Height
- 60mm
- LBracket_Width
- 50mm
- LBracket_Depth
- 100mm
- Hole_Diameter
- 10mm
- Rod_Hole_Diameter
- 10mm
The draft, as ClassCAD measured it
part-derive done
- Volume
- 96.857cm³
- Centre of gravity
- X14.72Y50Z20.2
06Code
A code document points at a generator in a git repository. The PDM reads its parameters, shows them as a form, runs it on a worker against ClassCAD and files each result as a document of its own. Released code is frozen with its commit, so a released part can always be made again.
- import { defineGenerator } from '@classcad/pdm-generator'
- export default defineGenerator(async ({ params, api, savePart }) => {
- const { outerDiameter, thickness, boreDiameter, boltCircle, holeCount, holeDiameter } = params
- if (boreDiameter >= boltCircle - holeDiameter) throw new Error(`The bore must stay inside the holes`)
- const part = await api.v1.part.create({ name: 'Flange' })
- const geometry = await api.v1.part.entityInjection({ id: part, name: 'Geometry' })
- const disc = await api.v1.solid.cylinder({ id: geometry, diameter: outerDiameter, height: thickness })
- const tools = [await api.v1.solid.cylinder({ id: geometry, diameter: boreDiameter, height: thickness + 2 })]
- for (let i = 0; i < holeCount; i++) {
- const a = (2 * Math.PI * i) / holeCount
- const translation = [(boltCircle / 2) * Math.cos(a), (boltCircle / 2) * Math.sin(a), 0]
- tools.push(await api.v1.solid.cylinder({ id: geometry, diameter: holeDiameter, height: thickness + 2, translation }))
- }
- await api.v1.solid.subtraction({ id: geometry, target: disc, tools })
- await savePart({ key: 'flange', name: `Flange ${outerDiameter}x${thickness}, ${holeCount} holes` })
- })
CodeGEN-0001Flange generator
Runs
- run 2code-execute · doneØ160 × 16 · bore Ø60 · 8 × Ø14 on Ø125DOC-000002 Flange 160x16, 8 holes
- run 1code-execute · doneØ120 × 12 · bore Ø40 · 6 × Ø11 on Ø90DOC-000001 Flange 120x12, 6 holes
07Self-hosted
One API, one Postgres database, a folder for files, and as many workers and engines as the work needs. Your models stay on your network. The only call out is the engine fetching its key from classcad.ch, and machines without internet take an offline key instead.
| Item | Part | What it does | Runs as | Port |
|---|---|---|---|---|
| 1 | Reverse proxy | HTTPS for everything; serves the web app, forwards /api and the editor’s WebSocket | nginx, Caddy, … | 443 |
| 2 | Web app | The PDM in the browser, Buerligons included | static files | · |
| 3 | API | Documents, versions, releases, references, jobs, files; runs its migrations on start | Node 20 | 3333 |
| 4 | Postgres | Metadata, versions, references, the audit log | Postgres 16 | 5432 |
| 5 | File store | Every file once, named by its SHA-256 | a folder | · |
| 6 | Job workers | Open, measure, build and export models, run generators, render thumbnails | Node 20 | · |
| 7 | Scheduler | One port in front of the engines: affinity, placement, scaling, restarts | classcad-scheduler | 9091 |
| 8 | Engines | ClassCAD, one process each, started by the scheduler | classcad-cli worker | 9094… |
08API
The web app, the job workers and your own scripts use the same REST API. It is described as OpenAPI, so you can read it in the browser and generate a client from it.
- /api/v1 REST, with the OpenAPI description at
/doc - ETag on what changes: send
If-Match, and a write based on a stale state gets 412 instead of overwriting a colleague's work - Jobs are claimed by workers over the same API, with heartbeats and retries
- Audit log: every write, who, when, what
- # a part, its file, its job, a version, a release
- POST /api/v1/documents {"name": "Bracket", "kind": "part"}
- → 201 DOC-000042, with the draft d7… of main
- POST /api/v1/versions/d7…/artifacts?kind=part-source
- → 200 the source is in the draft, part-derive is queued
- GET /api/v1/jobs?versionId=d7…
- → 200 part-derive succeeded
- POST /api/v1/workspaces/5a…/versions {"name": "Initial"}
- → 201 version 1
- POST /api/v1/versions/9c…/release
- → 200 revision A, released
- GET /api/v1/versions/9c…/bom
09Status
The PDM runs today, on a developer’s machine. It is not ready for your servers yet: the pieces under “Before the release” come first. If you want to run it when it is, or have a say in what comes first, write to us.
- Product
- ClassCAD PDM
- State
- in development
- Revision
- none yet
- Made by
- AWV Informatik, St. Gallen
Works today
- Documents, folders, search
- Drafts, versions, branches, take over, restore
- Releases with revision labels, obsolete
- Assemblies with pinned references, BOM, where-used, updates
- Editing in Buerligons, with reservations
- Generators from git repositories, runs, variants
- Jobs that measure, render and export with ClassCAD
- A REST API with its OpenAPI description
Before the release
- Sign-in through your own identity provider (OIDC)
- Projects with members and rights
- The scheduler in a ClassCAD release
- An install package for your servers
- S3-compatible file storage
- Generator runs in a sandbox