soldermaskdocs

board.json

One flat structure with everything the three views need, so the viewer never recomputes placement maths that was already done. Written by export.board_json() into every version; served at /api/projects/<slug>/versions/<vid>/board.

{
  "name": "ne555-led-blinker", "width": 28, "height": 22,
  "components": [ { "ref": "U1", "kind": "ic", "value": "NE555", "purpose": "timer",
                    "package": "SOIC-8", "lcsc": "C7593", "mfr": "NE555DR",
                    "price": 0.1263, "basic": true, "stock": 209000,
                    "symbol": "NE555D", "sym": "Timer:NE555D",
                    "x": 15.35, "y": 7.17, "rot": 90, "w": 6.4, "h": 5.2,
                    "body_w": 4.9, "body_h": 3.9, "body_z": 1.75, "lead": "gullwing", "standoff": 0.15,
                    "pads": [ { "name": "1", "x": 12.6, "y": 5.3, "w": 0.6, "h": 1.5, "shape": "rect", "th": false, "drill": 0 } ],
                    "silk": [ { "pts": [[12.15, 4.1], [12.15, 10.2]], "w": 0.15 } ],
                    "body": [[12.5, 4.7], [18.2, 4.7], [18.2, 9.6], [12.5, 9.6]],
                    "court": [12.15, 18.55, 4.1, 10.2] } ],
  "symbols": { "Timer:NE555D": { "rects": [], "polys": [], "arcs": [], "texts": [], "pins": [], "fields": {} } },
  "nets": [ { "name": "GND", "kind": "ground", "voltage_v": 0, "current_a": null, "width_mm": 0.25,
              "pins": [ { "ref": "U1", "pad": "1", "fn": "GND" } ] } ],
  "wires": [ { "net": "OUT", "layer": "F.Cu", "width": 0.25, "pts": [[15.3, 7.2], [18.0, 7.2]] } ],
  "vias": [ { "net": "GND", "x": 10.1, "y": 4.4, "d": 0.6, "drill": 0.3 } ],
  "insignia": { "fills": [ [[10.4, 1.0], [11.9, 1.0], [11.9, 2.5], [10.4, 2.5]] ] },
  "erc": [ { "severity": "warning", "rule": "floating-pin", "message": "U1 pin(s) ['5'] unconnected" } ],
  "gaps": [], "notes": [],
  "bom": { "parts": 0.36, "setup": 9.0 },
  "inventory": { "quantities": [1, 10, 25, 100], "reports": { "1": { "qty": 1, "buildable": true, "parts_cost": 0.36, "setup_cost": 9.0, "total": 9.36, "per_board": 9.36, "checked_live": true, "lines": [ { "lcsc": "C7593", "refs": ["U1"], "needed": 2, "stock": 209000, "headroom": 104500.0, "risk": "ok", "note": "" } ] } } },
  "generation": { "prompt": "A 555 timer LED blinker running from 5V", "backend": "gemini", "model": "gemini-3.8-flash",
                  "summary": "…", "assumptions": [], "unsupported": [], "decisions": [],
                  "input_tokens": 4210, "output_tokens": 1180, "cost_usd": null }
}
KeyContents
name, width, heightboard name and size in mm
components[]resolved parts with absolute, already-rotated pad positions (x, y in mm, board origin bottom-left, y up), the 3D profile (body_z, lead, standoff), price, stock and basic/extended, the symbol name and sym key into the symbol table
components[].pads[].shapewhat the fab flashes: rect, or circle — a round pad longer than it is wide is the oval the Gerber and the .kicad_pcb draw. Since 15 Sep 2026; the 2D view drew every surface pad as a rounded rectangle and every through-hole pad as a circle before it, and still does on a board without the key
components[].silkthe part's silkscreen in board mm, stroke by stroke — pts and the pen width w, or fill: true for a solid shape — the same strokes gerber._silk inks and the .kicad_pcb carries (silk.placed()): the vendor's own drawing where the footprint is the vendor's, and one drawn clear of the pads from the body and pins otherwise. Absent on a board built before 15 Sep 2026, which draws its courtyard box as it always did
components[].body, components[].courtthe body's four corners (F.Fab), turned and mirrored with the part, and the courtyard box [x0, x1, y0, y1] the placer kept clear (F.CrtYd) — where the placer put it, which for a module is not centred on the origin the part turns about
vias[].d, vias[].drillthe via's pad and hole diameter, so the view draws the size the copper has rather than a size it remembers
symbolsthe schematic drawings, one per distinct symbol, in KiCad mils with y up; a part with no library drawing gets a box under generic:<ref>
sheetthe schematic laid out (sheet.py): symbols (ref → x, y, rot, mir, units, role, pinned, box), wires (polylines, stub for a pin's label stub, trunk for a lane), junctions, labels (net, x, y, rot), ports (net, kind, x, y, dir), nc, texts (ref/value placements), frame (sheet size and where the drawing sits on it), undrawn (pads with no pin in their drawing) and stats — all in mils, y down. Rebuilt on read for boards saved before it existed
nets[]name, kind (ground, power, signal), pins with the function name each pin was resolved from, and the electrical facts below
nets[].voltage_vnominal volts against ground, or null — what makes Ohm's law checkable (ERC)
nets[].current_asteady amps, or null — what the copper is sized from
nets[].width_mmthe width the net was actually routed at: IPC-2221 for its current, or the 0.25 mm median when it declared none
wires[], vias[]routed copper by layer (F.Cu, B.Cu) and width
routingthe router's account: predicted (the model's rating of the placement it saw first, and which model), candidates[] (every placement annealed: seed, wirelength cost, p_clean), attempts[] (each placement routed: the router's unrouted and violations counts, how many nets were incomplete, seconds, clean), chosen (index into attempts), router (the kept attempt's counts), clearance_mm (the rule the board was routed at: 0.2 unless its parts pulled it lower, see dsn.rules), and incomplete — net name to the pins its copper never reaches. null on a board built before this was recorded; the workbench then falls back to asking which nets have any wire at all
insigniathe maker's mark as the filled polygons the silkscreen Gerber fills as regions, in board mm — null on a board with no room for it and on versions built before it existed. The views fill these rather than redrawing the logo, so what is on screen is the ink on the board
erc[]every finding: severity, rule, message
gaps[], notes[]what could not be resolved, and what was resolved but worth saying
bomparts cost at qty 1 and the extended-part setup fee
inventorythe stock sweep — see Stock and pricing
generationprovenance: prompt, backend, model, tokens, cost, and the model's summary, assumptions, decisions and unsupported

Boards saved before the symbol table existed get it rebuilt on read (export.attach_symbols), so a project's whole history draws with real symbols.