service-kicad
Public Made by Adomby adom
Shared headless KiCad 10: DRC, ERC, SVG/Gerber/STEP export, Altium library conversion, symbol/footprint/3D-model lookup. Every Adom tool shells to this CLI instead of calling the HTTP API directly.
Install?
Shared headless KiCad 10: DRC, ERC, SVG/Gerber/STEP export, Altium library conversion, symbol/footprint/3D-model lookup. Every Adom tool shells to this CLI instead of calling the HTTP API directly.
adom-wiki pkg install adom/service-kicad
Latest: v1.1.1, published
Contents
README
markdownservice-kicad — shared headless KiCad
The shared service. service-kicad is a headless KiCad 10 service Adom
runs as free shared infrastructure — DRC/ERC, Gerber/SVG/STEP export,
Altium library conversion, and symbol/footprint/3D-model lookups with no
local KiCad install. Every Adom tool and user container shells to the
service-kicad CLI rather than hitting the HTTP API raw.
Fair use. It's free today and we intend to keep it that way. We reserve the right to rate-limit or block abusive usage, and if scale ever demands it, heavy usage may become paid. Nothing about your designs is retained beyond the job lifecycle.
Self-hosting. The service is a single container you can run yourself —
full source on this page (Files tab / adom-wiki repo clone adom/service-kicad). If the shared instance doesn't fit, spin up your own
and point the CLI at it. See Self-hosting below.
Install
adom-wiki pkg install adom/service-kicad
That installs the CLI to your PATH, the Claude skill, and bash completions. Verify:
service-kicad health
Usage
# Health / diagnostics
service-kicad health # service + KiCad version + running git_sha
service-kicad config # effective service URL and where it came from
# DRC / ERC
service-kicad pcb drc board.kicad_pcb
service-kicad sch erc schematic.kicad_sch
# PCB export
service-kicad pcb export gerbers board.kicad_pcb --out ./gerbers/
service-kicad pcb export step board.kicad_pcb --out board.step
service-kicad pcb export svg board.kicad_pcb --out board.svg
service-kicad pcb export svg board.kicad_pcb --layers F.Cu,F.Mask,Edge.Cuts --out board.svg
# Schematic export
service-kicad sch export svg schematic.kicad_sch --out sch.svg
service-kicad sch export svg-project ./project-dir --out ./sheets/ # hierarchical: one SVG per sheet
service-kicad sch export pdf schematic.kicad_sch --out sch.pdf
service-kicad sch export bom schematic.kicad_sch --out bom.csv
# Altium → KiCad library conversion
service-kicad altium sym part.SchLib # → part.kicad_sym
service-kicad altium fp part.PcbLib # → part.kicad_mod
# Symbol / footprint / 3D lookups (KiCad official libraries)
service-kicad sym svg Connector_Generic Conn_01x04
service-kicad fp svg Connector_PinHeader_2.54mm PinHeader_1x04_P2.54mm_Vertical
service-kicad model fetch Connector_PinHeader_2.54mm.3dshapes/PinHeader_1x04_P2.54mm_Vertical.step
Reading a DRC / ERC result
kicad-cli's JSON carries no totals, only the finding collections. The CLI derives every count from those findings and states the verdict on the first line, separately from HTTP success (page issues #2, #3):
$ service-kicad pcb drc board.kicad_pcb
DRC FAILED: 2 errors, 1 warnings (schematic_parity 0 · unconnected_items 0 · violations 3; 5 checks ignored)
kicad 10.0.2 · units mm · input board.kicad_pcb (3150 B, sha256 16a6ff56135e…) · service 1.1.0 @08def5a7
[error] clearance: Clearance violation ( clearance 0.2000 mm; actual 0.1500 mm) @ (9.45, 9.5)
Pad 1 [<no net>] of REF** on F.Cu
...
hint [DRC_FAILED]: 2 error-severity finding(s); the report was produced but the DRC did not pass; exit code 5
Count policy. DRC counts span violations, unconnected_items and
schematic_parity; ERC counts span every sheets[].violations. error
and warning count; exclusion (user-excluded markers) is listed but never
counts toward pass/fail; any other severity is listed under other. A
collection the schema promises but the report lacks makes the status
unknown, never passed. ignored_checks are the checks kicad-cli did not
run: a pass says nothing about them.
Exit codes. 0 passed (warnings allowed), 5 the report was produced
and carries error findings, 1 the input could not be loaded, 2/3/4
transport, auth, service failures. Pass --exit-zero to keep the old
always-zero behaviour while still getting the verdict.
Formats. --format text (default) is the summary above. --format json
is the native report with ONE added top-level key, adom_summary (counts,
status, exit_code, input.sha256, service version + git sha, hints,
erc_scale); every native key is untouched. --format native is the
byte-for-byte kicad-cli report. With --out, the file gets the JSON and
the terminal gets the text.
ERC positions are 100x too small on KiCad 10.0.x. Upstream
eeschema/erc/erc_report.cpp builds WriteJsonReport's units provider
with pcbIUScale (1e6 IU/mm) while the text report and the schematic use
schIUScale (1e4 IU/mm), so every JSON pos is the true millimetre value
divided by 100. The CLI cross-checks item positions against the symbols
they name in the uploaded schematic and, when a factor other than 1 puts
them where the symbols are, prints hint [ERC_COORDINATES_SCALED] with the
factor. Nothing is rescaled: multiply pos.x/pos.y yourself and keep
the raw report. A report that checks out prints nothing; one that cannot
be cross-checked says why (ERC_COORDINATES_UNCHECKED).
"Failed to load". kicad-cli gives no parser location. The CLI reads the
file structurally before uploading and, for a truncated file, wrong file
kind, legacy .sch, or missing (version ...), prints the line/column and
paren balance under INPUT_UNLOADABLE (exit 1). The service does the same
on its 400 (diagnostics + hint fields). A file that parses but is still
refused is a semantic problem (unknown lib_id, unsupported format
version); open it in KiCad for the message.
3D models: STEP is the contract
For board 3D models, export STEP here and convert with
adom-step2glb / service-occt — that is the only supported GLB path.
KiCad-native GLB export is gated (page issue #106) because it bypasses the
molecule pipeline: no MP-marker anchoring, no gold machine-pin material,
no meter normalization, no footprint emit. The endpoints remain for
legacy callers, but every GLB that leaves this service is
provenance-stamped in its glTF JSON
(asset.extras.adom.provenance = "kicad-native-glb", plus an
X-Adom-Provenance response header) so importers and humans can catch it
downstream, and the CLI verb requires an explicit
--i-know-this-skips-the-pipeline acknowledgment.
The shared instance carries the official KiCad symbol/footprint/packages3D
libraries only. Adom's machine-pin 3D libraries are not installed
server-side, so molecule STEP export for boards using Adom library parts
still runs on the laptop (see the kicad-export-molecule skill); boards
built purely from the official libraries can use pcb export step here
directly.
Service URLs — channels
Two channels run in parallel. The default URL serves KiCad 10; KiCad 9 is opt-in for callers that need to pin to the older toolchain.
| Channel | URL | KiCad | When to use |
|---|---|---|---|
| default (v10) | https://kicad-rk5ue5pcfemi.adom.cloud |
10.0.x | every consumer, all the time |
| v9 fallback | https://kicad-v9-7f1gq8j5ohbq.adom.cloud |
9.0.x | only if your project explicitly pins to v9 (e.g., file-format compat for an older kicad-symbols revision) |
The default URL is baked into the CLI. To pin a run to v9 (or any other instance):
SERVICE_KICAD_URL=https://kicad-v9-7f1gq8j5ohbq.adom.cloud service-kicad pcb drc board.kicad_pcb
(KICAD_SERVICE_API is honored as a legacy alias.) Health probe shape is
the same on both channels; kicad_version and git_sha differ:
curl -sf https://kicad-rk5ue5pcfemi.adom.cloud/health
# → {"git_sha":"…","git_sha_short":"…","kicad_version":"10.0.2","ok":true,"service":"service-kicad","version":"1.1.0"}
Self-hosting
The whole service is one container: a Rust API binary shelling to
kicad-cli, plus the official KiCad libraries. Everything you need is in
this repo.
- Provision a container. Follow the standalone service guide for creating a dedicated lightweight container with SSH. Any Ubuntu 24.04 host with ~10 GB free (KiCad + libraries) works too.
- Get the source and deploy.
adom-wiki repo clone adom/service-kicad && cd service-kicad bash deploy.shdeploy.shis the canonical one-shot setup: installs KiCad 10 from the official PPA, overlays the upstream symbol/footprint/packages3D libraries (see library staleness below), builds the API, and installs the watchdog cron.Dockerfiledocuments the same steps as a reference image. KiCad 9 fallback:deploy-v9.shon a separate host — the two PPAs ship the samekicadpackage, so it's one channel per host. - The watchdog keeps it running.
watchdog.sh(cron, every 2 min) fetches origin, rebuilds on new commits, and health-checks the running service. It kills the old process by/proc/PID/exereadlink — notpkill -fpath matching, which self-kills SSH sessions and can match unrelated processes. Keep that. - Expose the port. The API listens on
PORT(default 8780). On an Adom container:adom-cli carbon containers port-add <slug> --port 8780 --prefix kicad. Health check:curl <url>/health—git_shatells you exactly what's deployed. - Point the CLI at it.
Same commands, your container.export SERVICE_KICAD_URL=https://your-instance.example service-kicad config # confirms: source = SERVICE_KICAD_URL env
Service configuration
| Env var | Default | Purpose |
|---|---|---|
PORT |
8780 |
API listen port |
KICAD_CLI |
kicad-cli |
kicad-cli binary to shell to |
KICAD_SYMBOLS_DIR |
/usr/share/kicad/symbols |
symbol library root |
KICAD_FOOTPRINTS_DIR |
/usr/share/kicad/footprints |
footprint library root |
KICAD_PACKAGES3D_DIR |
/usr/share/kicad/3dmodels |
3D model library root |
Uploads are capped at 100 MB per request.
Library staleness
The Ubuntu apt packages of kicad-symbols / kicad-footprints /
kicad-packages3d lag the upstream KiCad GitHub repos by years (24.04
ships a ~2020 snapshot). Dockerfile + deploy.sh overlay shallow git
clones of the upstream libraries at /opt/kicad-*-upstream, point the
KICAD_*_DIR env vars at them, and refresh weekly via
/etc/cron.d/kicad-lib-pull. The apt packages stay installed — they bring
kicad-cli itself — but their library data is shadowed by the upstream
clones.
HTTP API
Full contract: docs/service-kicad-api.md. Prefer the CLI over curling the API — it has the production URL baked in and insulates you from container reprovisions. Major endpoints:
| Endpoint | Body | Returns |
|---|---|---|
GET /health |
— | {ok, service, version, kicad_version, git_sha} |
GET /version |
— | {service, version, api_version, git_sha} |
POST /kicad/pcb/drc |
.kicad_pcb bytes |
JSON |
POST /kicad/sch/erc |
.kicad_sch bytes |
JSON |
POST /kicad/pcb/export/svg?side=top|bottom&layers=F.Cu,… |
.kicad_pcb bytes |
image/svg+xml |
POST /kicad/pcb/export/gerbers |
.kicad_pcb bytes |
tarball |
POST /kicad/pcb/export/step |
.kicad_pcb bytes |
model/step |
POST /kicad/pcb/export/glb[-zup] |
.kicad_pcb bytes |
model/gltf-binary — legacy; provenance-stamped, see STEP is the contract |
POST /kicad/pcb/export/glb-package[-zup] |
tar.gz (board + 3dmodels) | model/gltf-binary — legacy; provenance-stamped |
POST /kicad/sch/export/svg |
.kicad_sch bytes |
image/svg+xml |
POST /kicad/sch/export/svg-project |
tar (project dir) | tar of per-sheet SVGs |
POST /kicad/sch/export/pdf |
.kicad_sch bytes |
application/pdf |
POST /kicad/sch/export/bom |
.kicad_sch bytes |
text/csv |
POST /kicad/altium/sym |
.SchLib bytes |
.kicad_sym bytes |
POST /kicad/altium/fp |
.PcbLib bytes |
.kicad_mod bytes |
GET /sym/export/svg/:library/:name |
— | image/svg+xml |
GET /fp/export/svg/:library/:name |
— | image/svg+xml |
GET /models/<path> |
— | static file from kicad-packages3d |
GET /footprints/<path> |
— | static file from kicad-footprints |
GET /symbols/<path> |
— | static file from kicad-symbols |
Design doctrine
- Tool-agnostic by design. Zero
aci-*deps — nothing in this repo importsadom-circuit. Bug fixes land once for every consumer. - Body is the file. Every POST endpoint takes the raw
.kicad_*bytes as the request body, not a multipart upload. Keeps clients trivial (curl --data-binary @file). - Shell out; don't reimplement.
kicad-cliis the source of truth. We're a thin HTTP layer over subprocess calls, not a KiCad reimpl. - All exports pass
KICAD{6,7,8,9,10}_3DMODEL_DIRso component 3D models resolve during STEP export, regardless of which kicad-cli major version is on the host. - Static library mounts are read-only — nobody writes back through the service.
$PORTconfigurable so the same binary can host side-by-side dev/prod instances on one container.
Contributing
Every change on main triggers the prod watchdog: on the next 2-minute
cycle the container pulls, rebuilds, and relaunches — /health's
git_sha confirms what's actually serving. Breaking changes bump
api_version in service.json + docs/service-kicad-api.md. Open PRs
against the wiki page (adom-wiki pr) so review happens where users
look; the GitHub repo (adom-inc/service-kicad) is the working origin.
Candidate: project-aware PCB DRC
The feature/project-aware-drc candidate adds a complete-project path for fab-rule
qualification. This source has protocol/transport tests, but is not deployed on the
shared service and has not yet passed the native KiCad negative-control fixtures.
Check the installed CLI's help and service capabilities before using it.
service-kicad pcb drc-project ./project --board board.kicad_pcb \
--include Parts.pretty --out project-drc.json
# Separate intentionally failing fixture: prove a named custom rule ran.
service-kicad pcb drc-project ./rule-fixture --board rule-probe.kicad_pcb \
--expect-rule depth-lab-proof-custom-rules-loaded --out probe-drc.json
The board, matching .kicad_pro and .kicad_dru are required. Use
--allow-no-custom-rules only when intentionally relying on project settings
without custom rules. Adjacent library tables are included; include their local
libraries explicitly. Unrelated boards, credentials and arbitrary project-directory
files are not scanned/uploaded. The package supports regular KiCad text inputs;
links, duplicate/case-aliased paths, unsafe paths and special archive entries are
rejected. Select a board relative to the project root, including subdirectories.
The response contains the native report, service/native versions, supplied input
hashes, diagnostics and optional expected-rule observation. ok means the operation
and requested probe completed, not that the board passed DRC. A negative-control
probe normally produces a native violation. Removing that probe in a separate copy
is required before evaluating the real design. Input hashes do not establish rule
enforcement by themselves. HTTP 422 reports a requested rule absent from native
violation descriptions; the CLI saves this full response to --out before exiting
nonzero. Native failures never retry with default rules.
The legacy single-file pcb drc command now emits PROJECT_RULES_OMITTED on stderr
when matching project or rule files are present locally. It retains its existing
response format. The new operation uses a separate endpoint and does not alter
shared production settings or require desktop focus.
Validation: cargo test --workspace; tests using a subprocess fixture explicitly
report test-double-not-native. A real KiCad project run must still verify the
named probe, malformed-rule rejection and the intended fabrication constraints.
A self-contained local CLI/HTTP check is also available after cargo build --workspace:
python3 verification/test_project_drc.py. It starts only its own temporary API
process, uses a clearly labeled protocol test double, verifies five transport cases,
and stops its process. It does not install software or call the shared service.