# service-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](#self-hosting) below.

## Install

```bash
adom-wiki pkg install adom/service-kicad
```

That installs the CLI to your PATH, the Claude skill, and bash
completions. Verify:

```bash
service-kicad health
```

## Usage

```bash
# 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
```

### 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):

```bash
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:

```bash
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.

1. **Provision a container.** Follow the
   [standalone service guide](https://wiki.adom.inc/adom/standalone-service)
   for creating a dedicated lightweight container with SSH. Any Ubuntu
   24.04 host with ~10 GB free (KiCad + libraries) works too.
2. **Get the source and deploy.**
   ```bash
   adom-wiki repo clone adom/service-kicad && cd service-kicad
   bash deploy.sh
   ```
   `deploy.sh` is the canonical one-shot setup: installs KiCad 10 from the
   official PPA, overlays the upstream symbol/footprint/packages3D
   libraries (see [library staleness](#library-staleness) below), builds
   the API, and installs the watchdog cron. `Dockerfile` documents the
   same steps as a reference image. KiCad 9 fallback: `deploy-v9.sh` on a
   separate host — the two PPAs ship the same `kicad` package, so it's one
   channel per host.
3. **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/exe` readlink — not
   `pkill -f` path matching, which self-kills SSH sessions and can match
   unrelated processes. Keep that.
4. **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_sha` tells
   you exactly what's deployed.
5. **Point the CLI at it.**
   ```bash
   export SERVICE_KICAD_URL=https://your-instance.example
   service-kicad config   # confirms: source = SERVICE_KICAD_URL env
   ```
   Same commands, your container.

### 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](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](#3d-models-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 imports `adom-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-cli` is 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_DIR`** so 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.
- **`$PORT` configurable** 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.
