
ESP32 S3 ROBOT CONTROLLER
laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

KiCad PCB automation toolkit — placement, routing, scoring, and experiment management
USBPD ESP32 HUB75 is an open-source printed circuit board by laforestlabs, from the laforestlabs/KiCraft repository on GitHub. It is a 2-layer board of 114 × 57.5 mm with 40 parts on 39 nets, routed with 1,181 tracks and 169 vias.
It is built around the ESP32-S3-WROOM-1-N16R8, NS4168 and TLV62569DBVR-TP, with the SN74LVCH245ANSR, AO3401A and TYPE-C-31-M-12 among its other parts. Its parts include 15 capacitors, 12 resistors, 5 ICs, 4 connectors and 2 switches. It belongs with the power boards and the ESP32 boards of this library.
KiCad PCB automation toolkit — automated placement, routing, scoring, and experiment management for KiCad projects via the pcbnew Python API.
KiCraft is a multi-layer pipeline. Top-down:
.kicad_sch, .kicad_pro, _autoplacer.json). The LLM-
driven stages (intent / functional_spec / architecture / bom) run as a
Claude Code skill at .claude/skills/kicraft/, so a Claude Code
subscription pays for inference and no API key is required. The
deterministic synthesis step is a Python CLI (kicraft synthesize). See KiCraft below.# Development install (editable). Synthesis imports KiCad 9's bundled
# `pcbnew`; system Python already has it. For an isolated venv, create it
# with `--system-site-packages` so `pcbnew` is visible (see pipx note below).
pip install -e .
# With KiCraft helpers (pydantic + kicad-skip + easyeda2kicad; the LLM
# stages run in the Claude Code skill, not in this package — no API key needed)
pip install -e ".[design]"
# With all optional dependencies
pip install -e ".[scoring,experiment,design,tuning,server,loadtest,dev]"kicraft synthesize imports KiCad's bundled pcbnew, and
add-part --from-lcsc imports easyeda2kicad. An isolated pipx venv sees
neither by default, so install with --system-site-packages (exposes the
system pcbnew) and the [kicraft] extra (pulls in easyeda2kicad):
pipx install --system-site-packages "kicraft[kicraft]"Already installed without them? Symptoms are add-part --from-lcsc printing
easyeda2kicad not installed, or synthesize raising
ModuleNotFoundError: pcbnew. Remediate with:
pipx reinstall --system-site-packages kicraft
pipx inject kicraft easyeda2kicad # only if the [kicraft] extra was omittedA multi-turn chat that takes a project description in plain English and emits the hierarchical KiCad 9 file set that the placement / routing half of KiCraft ingests. No prior schematic required, and no API key — the conversation runs inside Claude Code on the user's subscription.
.claude/skills/kicraft/. Claude reads/writes a .kicraft/state.json
file matching the ConversationState Pydantic schema.kicraft CLI.From inside this repo (or any project that has copied the skill into its
own .claude/skills/kicraft/):
claude # start a Claude Code session
/kicraft # invokes the skill (or just say "I want to design a PCB")Describe your project ("USB-C powered 3.3V regulator with status LED,
JLCPCB target, under $5 BOM"). Claude steps through the stages, writes
.kicraft/state.json as it goes, and validates after every slot update
via kicraft validate. When all four slots are filled, ask
it to synthesize and it will run:
kicraft synthesize .kicraft/state.json ./generatedCopy the skill directory into your project's .claude/:
cp -r /path/to/KiCraft/.claude/skills/kicraft your-project/.claude/skills/
cp /path/to/KiCraft/.claude/commands/kicraft.md your-project/.claude/commands/Then pip install -e /path/to/KiCraft[kicraft] so the
kicraft CLI is on PATH. The skill activates the next time
you run claude inside your project.
KiCraft now runs each stage in a sub-agent that uses only two bundled CLI
commands — stage-prep (read-only collector) and stage-commit (atomic
validate + merge + archive). With those pre-allowed in ~/.claude/settings.json,
the user only sees interview-style questions, not a stream of permission prompts
for every tool call. Recommended allowlist entries:
"permissions": {
"allow": [
"Bash(kicraft stage-prep *)",
"Bash(kicraft stage-commit *)",
"Bash(kicraft synthesize *)",
"Bash(kicraft validate *)",
"Bash(kicraft archive *)",
"Bash(kicraft list-leaves)",
"Bash(kicraft lookup-symbol *)",
"Write(/tmp/kicraft_*.json)",
"Write(/tmp/claude/kicraft_*.json)"
]
}Install once via /update-config or edit the file directly; the patterns are
narrow (kicraft CLI only).
kicraft stage-prep <stage> [STATE]
# Single-shot collector. Returns JSON: {state, extras} where extras carries
# the leaf-library output for architecture and BATCHED symbol pinouts for
# wiring (replacing N per-part lookup-symbol calls with one).
kicraft stage-commit <stage> --slot-file F.json \
[--questions-file Q.json] [--history-message M] [--project-stem S] \
[STATE] [--no-archive] [--archive-root DIR]
# Atomic: validate the proposed slot, merge into state.json, append history,
# archive. Returns {"ok": true, ...} or {"ok": false, "errors": [...]} so the
# sub-agent can self-correct on validation failures.
kicraft validate .kicraft/state.json
# prints {ok, project_stem, slots_filled, open_questions, blocking_questions}
# exit codes: 0 ok, 2 schema error, 3 library validation error
kicraft list-leaves
# prints the "Available leaves" markdown block. The architecture stage
# receives this via stage-prep's extras; this command is for ad-hoc inspection.
kicraft lookup-symbol Library:Name
# prints one symbol's pin inventory. The wiring stage receives all needed
# pinouts via stage-prep's batched extras; this command is for ad-hoc lookup.
For project stem MYPROJ, synthesis writes:
MYPROJ/
MYPROJ.kicad_sch # hierarchical root
<SHEET>.kicad_sch ... # one per leaf sheet
MYPROJ.kicad_pro # design rules + Default/Power netclasses
MYPROJ_autoplacer.json # ic_groups, power_nets, signal_flow_order, ...
MYPROJ.kicad_pcb # empty pcbnew stubEverything past that is the existing layout/routing pipeline:
cd MYPROJ
autoexperiment MYPROJ.kicad_pcb --schematic MYPROJ.kicad_sch --rounds 20Every synthesis run executes SS9.1-SS9.6 from
docs/kicraft_schematic_prompt.md against the written files
(schematic version, footprints non-empty, pin directions valid,
Sheetfile refs resolve, autoplacer JSON valid, every named ref in
the schematic). Synthesis raises SynthesisValidationError and prints
the failing check rather than shipping a broken file set.
<PROJECT>.pretty/) are out of scope for v1 --
if the BOM references a footprint not in the stock KiCad libraries,
the skill surfaces a blocking question instead..kicraft/state.json in the project directory —
gitignore it (or commit it; it's plain JSON and reviewable).A leaf is a single hierarchical sheet — a pre-routed PCB fragment plus its schematic, BOM, and autoplacer settings. Once you've solved one ("USB-C 1S LiPo charger") and pinned a round you trust, you can promote it into the global Leaf Library. The KiCraft pipeline then reuses it verbatim every time the LLM judges it a match for a new project, collapsing leaf-level design surface to a vetted, pinned solution.
$KICRAFT_LEAF_LIB # default: ~/.kicraft/leaves/
usb-c-lipo-charger/
manifest.json
leaf_routed.kicad_pcb # canonical name (matches .experiments/...)
metadata.json
solved_layout.json
schematic.kicad_sch
autoplacer_fragment.json
bom.csv
renders/
front_all.png
back_copper.png
copper_both.png
thumbnail.pngThe pinned-PCB triad (leaf_routed.kicad_pcb + metadata.json +
solved_layout.json) is the same triad the existing pin manager (under
.experiments/subcircuits/<leaf_key>/) consumes, so an imported leaf
drops in as a pre-solved round and the parent composer treats it as
already-solved on the next autoexperiment --parents-only.
Note: the Experiment Manager GUI that hosted the "Leaf Library" promote flow was removed 2026-06-22 (recover from git history if needed). Leaf promotion is pending a re-home in the web app — see
docs/plans/refactor-roadmap.md. The pinned-round import format described above still applies.
The wizard runs the renders, writes a manifest with the
content-addressed hash, and atomically writes the new directory into
$KICRAFT_LEAF_LIB. To replace an existing leaf, bump the version
(e.g. 0.1.0 -> 0.1.1 for a patch, 0.2.0 for additive interface
changes, 1.0.0 for breaking ones).
When the skill enters the architecture stage it runs
kicraft list-leaves and reads the result so the model can
see the curated catalog. Claude picks any matches by setting
Sheet.from_library = "<name>@<version>" and Sheet.library_instance = N.
kicraft validate then verifies the leaf's hierarchical-
label interface matches the sheet's endpoints exactly. Synthesis then:
schematic.kicad_sch to the project (with refdes
renumbered to fit the host project's existing refs).<project>/.experiments/subcircuits/<leaf_key>/round_lib0001_*.pins.json so ensure_applied() keeps it locked
across solver runs.library_leaves audit record to <project>_autoplacer.json.kicraft-leaf list # rows: name@version, hash, tags, description
kicraft-leaf show <name> # full manifest as JSON
kicraft-leaf path # resolved library directoryPromotion and removal are GUI-only — there's no kicraft-leaf promote.
The intent is that curating the library is a deliberate, human-in-loop
step, not a scriptable one.
KiCraft is project-agnostic. Create a <project>_autoplacer.json (or
autoplacer.json) in your KiCad project root to configure IC groups,
component zones, signal flow, and other project-specific settings.
See examples/llups_autoplacer.json for a full example.
cd /path/to/your/kicad-project
solve-subcircuits project.kicad_sch --pcb project.kicad_pcb --rounds 3 --routeautoexperiment project.kicad_pcb project.kicad_sch --rounds 20 --workers 2Explore leaf candidates first, lock the ones you like, then iterate on the parent only:
# 1. Solve only leaves -- snapshots every round to .experiments/subcircuits/<leaf>/round_NNNN_*
autoexperiment project.kicad_pcb --schematic project.kicad_sch --rounds 30 --leaves-only
# 2. Pin chosen rounds via the GUI Analysis page (Hierarchical Progression -> Accepted
# Leaf Gallery -> "Pin from prior experiment-round snapshots"), or write
# .experiments/pins.json by hand.
# 3. Run only the parent compose phase against the pinned leaves
autoexperiment project.kicad_pcb --schematic project.kicad_sch --rounds 10 --parents-onlyThe composer calls pins.ensure_applied() before loading artifacts, so
pinned leaves stay locked even if a stray leaf-solve overwrites the
canonical files. The best parent of any run is also copied to
.experiments/best/parent_routed.kicad_pcb and <projectname>_best.kicad_pcb
at the project root for fab handoff.
score-layout project.kicad_pcbPlacement constraints live in <project>_autoplacer.json at the project
root. The autoplacer auto-discovers it; the file is project-agnostic --
it references components by ref designator (e.g. J1) or subcircuit
sheet name (e.g. BATT), nothing is hardcoded to a particular project.
See examples/llups_autoplacer.json for a full working file.
component_zones constrains where individual refs land on the board.
Three constraint types, all applied at solve time:
{
"component_zones": {
"J1": { "edge": "left" },
"J2": { "edge": "right" },
"H4": { "corner": "top-left" },
"H86": { "corner": "bottom-right" },
"BT1": { "zone": "bottom" },
"BT2": { "zone": "bottom" }
}
}edge: left|right|top|bottom — connector body flush with the named
edge. Pads face inward, housing overhangs outward by
connector_edge_inset_mm.corner: top-left|top-right|bottom-left|bottom-right — typically
mounting holes. Locks at the corner with mounting_holes.keepout.size_mm
reserved (see Mounting holes below).zone: top|bottom|left|right|... — soft confinement to a region.
The component is locked inside the zone; the solver may place it
anywhere within it.When a ref is part of a child subcircuit (e.g. BT1 inside the BATT
subcircuit), the constraint is propagated to the block during parent
composition, so the BATT block ends up zone-bottom-pinned at parent
solve time.
By default, the parent composer prevents two leaves from overlapping when both have any same-layer copper -- a strict same-layer-outline gate that catches CHARGER-style continuous-F.Cu shorts. PTH pads register as copper on both F.Cu and B.Cu, so a leaf whose only front-side copper is the shadow of through-hole pads (battery holders, screw terminals, large barrel jacks) is treated as having real F.Cu occupancy and rejects SMT-on-front stacking inside its bbox -- even when the body centre is empty and stacking would be physically fine.
To re-enable opposite-side stacking on top of such a leaf, open the GUI's Setup → Per-Component Placement Rules tab, expand the leaf, and toggle the sheet-level switch:
☐ → ☑ Backside THT anchor (allow SMT-front leaves to stack on top)
Click Save to project JSON…, review the diff modal, and confirm.
The toggle persists as a sheet-name entry under
parent_placement.backside_through_hole_leaves. For headless or CI
workflows the same key can be set by hand:
{
"parent_placement": {
"backside_through_hole_leaves": ["BATT", "TERMINAL_BLOCK"]
}
}What the override does:
_stack_compatible_blocks) to use
it as a stacking anchor, actively row-packing front-only candidates
inside its body.The flag is keyed by sheet_name so any project can list its own
THT-back leaves; nothing is hardcoded to a single board. CHARGER-style
leaves with continuous F.Cu routing must not be enabled -- the
override there would let the original same-layer shorts return.
parent_placement.candidate_search controls the K-candidate search
loop. The picker stamps each candidate, runs DRC, scores by a
composite of opposite-side packing, courtyard overlap, ratsnest
length, and bbox density, and picks the highest-scoring candidate
whose stamped DRC reports shorts == 0:
{
"parent_placement": {
"candidate_search": {
"k": 4,
"time_budget_s": 240.0
}
}
}shorts == 0 is the only hard gate. Geometry-validation results
(components/pads outside the auto-grown outline) are recorded on each
candidate for diagnosis but no longer reject -- letting routing run
on a violating layout produces a routed PNG showing exactly where the
problem is, which is more actionable than aborting the round.
The mounting_holes config block parameterizes the corner-anchored
mechanical holes (H4, H86, etc.):
{
"mounting_holes": {
"count": 2,
"screw": "M3",
"hole_diameter_mm": 3.2,
"pad": { "shape": "hexagon", "size_mm": 3.0 },
"keepout": { "shape": "hexagon", "size_mm": 4.0 }
}
}| Field | Meaning |
|---|---|
count | Target number of holes (informational; actual count is whatever the source PCB project ships). |
screw | Free-form screw reference ("M2.5", "M3", "#4-40"). Documents intent; not parsed. |
hole_diameter_mm | Drill clearance diameter for the screw. M3 default = 3.2 mm (clearance fit + fab tolerance). |
pad.shape | hexagon (default), circle, or square. Reserved for the future footprint generator. |
pad.size_mm | Outer extent of the exposed copper / annular ring around the hole, measured from hole center to the closest edge of the shape (radius for circle, half-width-across-flats for hexagon / square). Reserved for the future footprint generator. |
keepout.shape | Same vocabulary as pad.shape. Reserved -- placer currently treats keepout as rectangular. |
keepout.size_mm | Component-free zone radius (face-to-flat distance for hexagon / square). The placer reads this directly: a corner-anchored hole sits this far inboard from each board edge. Default 4 mm = M3 head 3 mm + 1 mm fab slack; bump to ~5 mm if you ship M3 + washer. |
Only keepout.size_mm currently affects geometry. The shape fields and
pad.* are plumbing for an upcoming change that lets KiCraft generate
or validate mounting-hole footprints directly from this block.
The GUI Setup tab exposes 39 searchable parameters that the evolutionary
optimizer mutates during autoexperiment runs. Users can narrow or widen
the search range (min/max) for each parameter to focus exploration.
Bounds persist across sessions automatically and flow to the optimizer via
gui_param_ranges.json. A shared normalize_bounds() helper validates all
bound inputs (rejects NaN/Infinity, clamps to spec domain, swaps inverted
ranges). The only enforced cross-parameter constraint is physical:
via_drill_mm < via_size_mm (annular ring requirement).
Parameter groups:
| Group | Params | Controls |
|---|---|---|
| Placement Physics | 8 | Force strengths, cooling, iterations, convergence |
| Board Geometry | 6 | Dimensions, margins, grid snap, clearances |
| Edge & Connectors | 6 | Courtyard, connector gaps, insets, pad margins |
| SA Refinement | 6 | Temperature, cooling, move radius, swap/rotation |
| Routing | 7 | Trace widths, via dimensions, zone margin, KiCad Routing Tools |
| Thermal | 1 | Keep-away radius around hot components |
| Component Behavior | 1 | THT backside area threshold |
| Zone Pour | 4 | Clearance, fill thickness, thermal relief |
Run autoexperiment with --param-ranges <file.json> to override bounds
from the command line.
kicraft validate STATE.json — Validate a .kicraft/state.json
against the ConversationState schema + library-pick rules.kicraft list-leaves — Print the "Available leaves" markdown
block the architecture stage shows to the model.kicraft synthesize STATE.json OUT_DIR [--smoke] — Emit the
KiCad file set from a complete state. The LLM-driven chat itself runs in
the Claude Code skill at .claude/skills/kicraft/. See
KiCraft.solve-subcircuits — Hierarchical subcircuit placement and routingcompose-subcircuits — Assemble solved subcircuits into parent boardssolve-hierarchy — Full hierarchical solve (leaves → parents)autoexperiment — Automated experiment loop with parameter searchclean-experiments — Clean up experiment artifacts (before/after/nuke)watch-status — Live terminal monitor for running experimentsscore-layout — Score PCB layout qualityrender-pcb — Render PCB layers to PNGrender-drc-overlay — DRC violation overlay on PCB renderrender-failure-heatmap — Routing failure heatmapplot-results — Plot experiment or scoring dashboards (auto-detects format)diff-rounds — Diff between experiment roundsgenerate-report — Generate scoring reporttoken-report: Token usage + estimated cost for a KiCraft design session, parsed from its Claude Code transcriptmove-component — Move a component to absolute positionalign-components — Align components along an axisarrange-grid — Arrange components in a gridadd-gnd-zone — Add GND copper zonecleanup-routing — Clean up routing artifactssplit-schematic — Split flat schematic into hierarchical sheetslist-footprints — List all footprints with positionscheck-trace-widths — Check trace widths against minimumrun-drc — Run design rule checknet-report — Network connectivity reportinspect-subcircuits — Inspect subcircuit hierarchyinspect-solved-subcircuits — Inspect solved subcircuit artifactskicraft/
├── kicraft/ # Chat -> KiCad file set
│ ├── models.py # Pydantic state slots (intent / functional_spec / architecture / bom)
│ ├── library.py # Leaf-library helpers (list / validate picks)
│ ├── synthesize.py # Deterministic state -> file set step
│ ├── synthesis/ # .kicad_sch / .kicad_pro / autoplacer.json emitters + §9 checks
│ └── cli_app.py # `kicraft` validate / list-leaves / synthesize
│ # The LLM-driven stages and per-turn orchestrator live in the
│ # Claude Code skill at .claude/skills/kicraft/ (not in this package).
├── autoplacer/ # Placement and routing engine
│ ├── config.py # Default config + project config loader
│ ├── kicad_routing_tools.py # pinned KRT adapter and copper custody
│ ├── routing_board.py # pcbnew/DRC/copper helpers
│ ├── brain/ # Pure algorithms (no pcbnew dependency)
│ └── hardware/ # KiCad pcbnew API adapter
├── scoring/ # Layout quality scoring checks
├── cli/ # CLI entry-point scripts
└── logging_config.py # Structured logging setup0.20.2 at commit
3ceb773722bea67aa3685e7ee430c0c0d17ef38d with native router 0.20.1MIT
| Qty | Value | Footprint | References |
|---|---|---|---|
| 6 | 100nF | C_0603_1608Metric | C2, C9, C10, C11, C12, C14 |
| 6 | 10k | R_0603_1608Metric | R1, R2, R5, R6, R7, R8 |
| 5 | 100k | R_0603_1608Metric | R3, R4, R10, R11, R12 |
| 4 | 10uF | C_0603_1608Metric | C4, C6, C8, C13 |
| 2 | 1uF | C_0603_1608Metric | C1, C15 |
| 1 | 100uF | CP_Elec_6.3x5.4 | C5 |
| 1 | 2.2uH | L_0805_2012Metric | L1 |
| 1 | 22uF | C_0603_1608Metric | C7 |
| 1 | 330 | R_0603_1608Metric | R9 |
| 1 | 4.7uF | C_0603_1608Metric | C3 |
| 1 | AO3401A | SOT-23_L2.9-W1.3-P1.90-LS2.4-BR | Q1 |
| 1 | BOOT | SW-SMD_4P-L6.0-W6.0-P4.50-LS9.0_H5.0 | SW1 |
| 1 | EN | SW-SMD_4P-L6.0-W6.0-P4.50-LS9.0_H5.0 | SW2 |
| 1 | ESP32-S3-WROOM-1-N16R8 | WIRELM-SMD_ESP32-S3-WROOM-1 | U1 |
| 1 | HUB75 2x08 | PinHeader_2x08_P2.54mm_Vertical | J1 |
| 1 | LED 1x03 | PinHeader_1x03_P2.54mm_Vertical | J2 |
| 1 | NS4168 | ESOP-8_L4.9-W3.9-P1.27-LS6.0-BL-EP | U5 |
| 1 | SN74LVCH245ANSR | SO-20_L12.6-W5.3-P1.27-LS7.8-BL | U2 |
| 1 | STUSB4500QTR | QFN-24_L4.0-W4.0-P0.50-BL-EP2.8 | U3 |
| 1 | Speaker 1x02 | CONN-TH_WJ126V-5.0-2P | J4 |
| 1 | TLV62569DBVR | SOT-23-5_L3.0-W1.7-P0.95-LS2.8-BR | U4 |
| 1 | TYPE-C-31-M-12 | USB-C_SMD-TYPE-C-31-M-12_1 | J3 |

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management
laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management

laforestlabs/KiCraft
KiCad PCB automation toolkit — placement, routing, scoring, and experiment management


jlcjak/astra_piNas
Six-layer CM5 Lite dual-NVMe carrier with PCIe switch, Ethernet, microSD, USB-C PD and PoE. Review design; fabrication hold documented.

Serhio4522/SEM_hitachi-
Digitization of a Hitachi scanning electron microscope (SEM) — hardware design, FPGA firmware, and analog frontend schematics.

FujiNetWIFI/fujinet-hardware
Schematics and design files for FujiNet hardware


puji4810/coffe-scale
USB-C J5 ──► U8 ESD ──► R19/R20 22R ──► U4 IO19/IO20 (native USB-Serial/JTAG) │VBUS ├─► U5 TP4057 充电 ──► J6 1S Li-ion └─► D1/Q1 负载切换 ──► VSYS ──► U6 XC6220 3V3 1A ──► VCC3V3 ──► U4/外设 └──► U7 HT7533 ──► VDDADC ──► U1/U3…

vaknin/PCB
Hardware ideas to fab-ready PCBs as code: Rust pcbgen on KiCad 10, Freerouting, ESP-IDF firmware with QEMU/Wokwi simulation, CadQuery enclosures

It measures 114 × 57.5 mm and has 2 copper layers, 1.6 mm thick.
40 parts, built around the ESP32-S3-WROOM-1-N16R8, NS4168, TLV62569DBVR-TP and SN74LVCH245ANSR. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.
From the laforestlabs/KiCraft repository on GitHub, at the commit this page was made from: the board is kicraft/server/sample_projects/esp32-hub75-controller/USBPD_ESP32_HUB75.kicad_pcb. PCBKit opens the board in the browser, with nothing to install.
Its repository states no licence, so its author keeps all rights: ask laforestlabs before reusing the design.