The ERP as the KiCad library¶
Make the ERP the only source of placeable parts: KiCad's symbol chooser lists ERP parts, and placing one brings the IPN, description, footprint, value and datasheet along. A part that is not in the ERP cannot be placed. Owner idea, 2026-10-06. Related: KiCad, embeddedera-app, SOP-001, SOP-002, SOP-008.
How it works¶
- KiCad HTTP libraries (KiCad 8 and newer; this server runs 10.0.5) read parts from a web address instead of a symbol file. Format: https://dev-docs.kicad.org/en/apis-and-binding/http-libraries/.
- The InvenTree KiCad plugin (afkiwers/inventree_kicad, MIT, release 2.1.0 on 2026-09-20, needs InvenTree 1.2+; ours is newer) serves the ERP parts in that format at
/plugin/kicad-library-plugin/. - The ERP stores pointers, not drawings. Drawings stay in the KiCad libraries we already have:
| In KiCad | Comes from | Example |
|---|---|---|
| Symbol drawing | default symbol per ERP category | Resistors → Device:R |
| Reference letter | default per category | Resistors → R |
| Footprint | a part parameter (or a mapping table) | Resistor_SMD:R_0603_1608Metric |
| Value | a chosen part parameter | 1K |
| Description, IPN | the part itself | RES 1K 1% 0603, EE-C-0163 |
| Extra fields | part parameters, visibility configurable | MPN, manufacturer, design note |
| Datasheet | not yet from the part link (open plugin issue #170); a parameter works | URL |
- Categories become the chooser's folders, so the ERP category tree matters (Resistors, Capacitors, ICs/MCU, Connectors…).
- One generic drawing serves many parts; only chips and special parts need their own drawing (
EmbeddedEra:STM32G0B1_LQFP48). - Board features with nothing to buy (mounting holes, test points, solder jumpers, programming pads) stay ordinary KiCad symbols with no IPN and excluded from the BOM.
- Placed symbols are copied into the schematic, so old designs open offline or after a part is archived (expected, to be tested).
Login through Cloudflare¶
KiCad sends only Authorization: Token <ERP token>; it cannot send Cloudflare Access headers. Options, in order:
1. A Cloudflare Access bypass for /plugin/kicad-library-plugin/* only, gated by a read-only ERP token for KiCad. Check first that those endpoints expose only part metadata.
2. A LAN-only hostname for the plugin (works only at the shop).
3. A small local proxy on the laptop that adds the Cloudflare service-token headers.
Steps¶
Status 2026-10-06 (owner approved the plan with all recommended options; phases 1 and 2 done on the sandbox ERP, production untouched).
- Sandbox first: DONE. Plugin 2.1.0 installed on the sandbox (pinned in the sandbox
plugins.txt, restart), migrations clean (8 of 8), plugin active. Source read; facts below. Demo passes:erp-library/httplib_check.pyin brancherp-httplibof~/kicad-liblists 7 categories and 175 parts, resolves a resistor (EE-C-0163) and an STM32 (EE-C-0097), places them into a schematic andkicad-cliexports a BOM and netlist carrying IPN, Footprint, Value, Manufacturer and MPN. Finding: production is already ahead of this plan. It already runs plugin 2.0.4 (active), the Symbol/Footprint/Reference/Value templates, default symbol and reference on 7 categories, akicad-libraryreader user, a LAN httplib at~/torrentvpn/inventree/kicad/EmbeddedEra-ERP.kicad_httplib, and 141 parts with all four parameters (inventree card). What is new is 2.1.0, the narrower token user, the repo template and the Cloudflare path. - ERP data: DRAFTED, not applied. Proposal Sheet in the Shared Drive Documents folder (https://docs.google.com/spreadsheets/d/1zQM6SUKdlRZvnzNmMWaYzbCdJu_AKqzXe0CEH4hv3Hw/edit). 189 active production parts: 154 from the library or existing parameters, 11 by rule, 19 not placeable, and 5 left blank (AM90, RGB LED TZ-5050, coin vibration motor, two LiPo cells) because both free models (nemotron-3.5-lightning, ling-3.1-flash) spent their whole token budget thinking and returned no answer, so Engineering fills those by hand. Production already has the categories and templates, so what remains is filling about 30 parts and the category split.
- Access: prepared, not applied. Read-only
assistant-kicaduser and token created on the sandbox only (groupKiCad read-only: part and part_category view, nothing else). Cloudflare script~/assistant-sandbox/scripts/erp-kicad-public-path.py(dry run by default,--apply,--remove,--verify). Required before applying: strip the login header at the origin (see security assessment below). - KiCad: PARTLY DONE.
erp-library/EmbeddedEra-ERP.kicad_httplib(token placeholder, sandbox URL) and the check script are committed on brancherp-httplib(not pushed, not merged). Still to test by hand in the KiCad GUI: the symbol chooser listing and opening an old project offline (a placed symbol is embedded in the schematic, and the BOM export in the demo needs no ERP). - Production: not started; waits for the owner to see the demo. The plugin upgrade is 2.0.4 to 2.1.0 (add the pin to production
plugins.txt, restart). - SOP-008 rewrite: draft section 8 in SOP-008 v2.2 (draft): placing ERP parts, two cases, board features, cache timing.
Plugin facts (read from 2.1.0 source, confirmed against the sandbox)¶
- Symbol: a per-part parameter (plugin setting
KICAD_SYMBOL_PARAMETER, here template Symbol) overrides the default symbol of the deepest selected category the part sits under; if neither, the settingDEFAULT_FOR_MISSING_SYMBOL. Extra colons in a name are replaced by underscores. - Footprint: the category's own footprint template, else the global
KICAD_FOOTPRINT_PARAMETER(Footprint), mapped through the optional per-category mapping table, else the category default footprint. Value:KICAD_VALUE_PARAMETER, else the category default value template, else the part Name (this is why resistors named by MPN show the MPN until Value is filled). Reference: per-part parameter, else category default, elseX. - Fields emitted: value, footprint, datasheet, reference, description, keywords;
InvenTree(part id) andPart URL(hidden);IPNonly whenKICAD_INCLUDE_IPNis set (production: include_visible); every other part parameter as an extra field (units included); withKICAD_ENABLE_MANUFACTURER_DATA(production: on)Manufacturer_n,MPN_n,Supplier_n,SPN_n.exclude_from_bom,exclude_from_board,exclude_from_simcome only from parameters named in the plugin settings (not configured; default False). Datasheet is the first part attachment whose comment isdatasheet(not the Link field). Stock count in the description is optional (production: on). - Caching: none on the server (only per-request lookups). KiCad caches by the httplib file:
timeout_parts_seconds,timeout_categories_seconds. Inactive and (setting) template parts are hidden from category lists; sub-category parts are included in the parent (a part shows under Components and under Components/Passives). - Hazards found:
/v1/parts/(no category) lists every part including assemblies and consumables; the list is not limited to the selected categories. Parameters beyond the KiCad ones are all exposed as fields.
Endpoints (for the Cloudflare review), sandbox, 2026-10-06¶
Base /plugin/kicad-library-plugin/. Without any token every one returns 401. With the read-only token (part and part_category view only):
| Path | Method | Returns | Read-only token |
|---|---|---|---|
v1/ (and any unknown path) |
GET | links to categories and parts | 200 |
v1/categories/ |
GET | selected categories: id, path name, description | 200 |
v1/parts/category/<id>.json |
GET | parts: id, name, description (+stock), symbol, all fields above, exclude flags | 200 |
v1/parts/<id>.json |
GET | one part, same | 200 |
v1/parts/ |
GET | every part in the ERP, same fields (about 166 KB) | 200 |
api/category/ (+ <pk>/) |
GET, POST, PUT, PATCH, DELETE | KiCad category settings; creates, edits, deletes them | 403 (needs part_category change rights; an Owner or Engineering token could write) |
upload.json |
POST | imports a KiCad XML netlist into part parameters | 403 for the read-only user (CSRF page); writes for a user with rights |
progress_bar_status |
GET | import progress of the caller | 200, harmless |
Not exposed: prices, suppliers' prices, customers, orders, stock locations, BOMs. Exposed: part names, descriptions, IPNs, MPNs and manufacturers, supplier names and SKUs, stock counts, all part parameters, internal part URLs. The same token can still read /api/part/, /api/user/ and /api/settings/global/ if it can reach them (any authenticated user can on this InvenTree); those stay behind Access.
Cloudflare security assessment¶
- Scope the bypass to
/plugin/kicad-library-plugin/v1/*, not/*. KiCad only callsv1/; this keepsupload.jsonand the category write API behind Google login. - Blocker: forged login header. Production uses
INVENTREE_REMOTE_LOGIN=Truewith headerHTTP_CF_ACCESS_AUTHENTICATED_USER_EMAIL. On a bypassed path Access does not authenticate, so a client can send that header itself, and InvenTree would trust it unless the origin strips it (reasoned from the config, not tested live). Mitigation: in the ERP Caddyfile:80block drop the header for the path (@kicad path /plugin/kicad-library-plugin/*thenrequest_header @kicad -Cf-Access-Authenticated-User-Email), then run the script's--verify, whose spoof test must return 401. Not checked on the live host because that needs the production change. Production config was not edited. - Residual risk once done: the read-only token is the only guard, on the public internet; part data (not prices, no customer data) is readable by whoever holds it. Mitigations: dedicated narrow user (not the existing
kicad-library, whose Read-only group can also view admin, stock and purchase orders), token rotation (erp-token flow), Cloudflare rate-limit on the path, and--removerolls back in one call. Option 2 (LAN-only) stays the zero-exposure choice.
Open questions for the owner¶
- Programming header (EE-C-0065, -0067): footprint only, or a connector we buy?
- Bypass (option 1) acceptable, or keep the ERP fully behind Google login and use option 2 or 3?
Not checked yet¶
Placing from the real KiCad GUI chooser (the demo emulates the HTTP client and places through a generated schematic); behaviour of KiCad with the ERP unreachable at chooser time; the live Cloudflare header-strip test (needs the production Caddy change).