It shipped 1.77 MB of vendored assets to every site - swagger-ui-bundle.min.js alone is larger than Redoc - in order to provide an Execute button that only the development config ever enabled. Production config had it off, testing had it off hard, and when off the routes were never bound at all. The staticdocs directory drops from 2.9 MB to 1.1 MB, and there is one fewer vendored dependency to track for licences and advisories. What replaces it: an agent has the MCP server's shopdb_call, and a human has Redoc for reading plus the 324 harvested examples in the spec for a curl that is mostly copy-paste. The read-only page was always the one that mattered. The tests this leaves behind are the interesting part. That file was named for try-it but half of it proved things about the page that stays, above all that nothing it loads comes from the internet, which is what a CDN reference costs on an air-gapped box. Those are kept and the file is renamed for what it covers. Pointing the air-gap test at redoc.standalone.js immediately failed: the bundle still carried a trailing sourceMappingURL for a .map that was never vendored, so every devtools open was a guaranteed 404 with no internet to fall back on. That was visible in a browser console and nobody had chased it, because the test had only ever been parametrised over the two Swagger files. The pragma is gone and the assertion now checks the TRAILING pragma specifically: Redoc's own code contains that string three more times, in a data: URI it assembles and in a worker blob, and a blunt "not in body" assertion fails on correct code. A first attempt to strip it with a line-anchored pattern removed 507 KB, because the bundle is minified onto long lines. Hence the anchored, byte-exact removal and the assertion that can tell a directive from source.
89 lines
4.8 KiB
Markdown
89 lines
4.8 KiB
Markdown
<!-- docmeta
|
|
purpose: reference
|
|
audience: integrator, developer
|
|
status: current
|
|
summary: Where the generated API spec is, what llms.txt is for, and the surface and auth orientation a spec cannot give.
|
|
loadwith: none
|
|
-->
|
|
|
|
# API reference
|
|
|
|
**The reference is generated and served by the app. This page is a pointer to
|
|
it, plus the two things a specification cannot tell you.**
|
|
|
|
There used to be a hand-written endpoint list here. It competed with the
|
|
generated spec, lost, and went stale in between - so it was cut rather than
|
|
maintained. Everything it listed is in the spec, derived from the code: every
|
|
operation with its auth, its query parameters, its response shape and
|
|
harvested examples. Counts are in [PROJECT-MAP](PROJECT-MAP.md), which is
|
|
generated; nothing of that kind belongs on this page.
|
|
|
|
## Where the generated reference is
|
|
|
|
| Reach it at | What it is |
|
|
|---|---|
|
|
| `GET /api/docs` | The reference, rendered with Redoc. The bundle is vendored in the image, so it renders with no network - which is the only reason it is usable on an air-gapped site. |
|
|
| `GET /api/docs/openapi.json` | The raw OpenAPI 3.1 spec. Also tracked as `docs/openapi.json`, so it can be read from a checkout without running anything; regenerate with `scripts/gen_openapi.py` after any API change. |
|
|
| `GET /api/docs/llms.txt` | The short version, for a machine caller. Also [llms.txt](llms.txt) in this corpus. |
|
|
|
|
[llms.txt](llms.txt) is not a second spec. It is the orientation a caller needs
|
|
before the spec is useful: the response envelope, how authentication is
|
|
obtained and sent, the pagination and filtering conventions, and the handful of
|
|
recipes people get wrong. Read it first, then look operations up in the spec.
|
|
|
|
## Which surface owns what
|
|
|
|
The spec lists every operation; it does not say which contract governs one.
|
|
There are three surfaces, and the third is everything else.
|
|
|
|
| Surface | Who calls it | Where its semantics live |
|
|
|---|---|---|
|
|
| **Fleet and client contracts** | Shop-floor PCs, the endpoint agent, kiosks, displays and printer installers - machines, not browsers | [COLLECTOR-INTEGRATION](COLLECTOR-INTEGRATION.md), [COLLECTOR-PAYLOADS](COLLECTOR-PAYLOADS.md), [COLLECTOR-AUTH](COLLECTOR-AUTH.md), [PRINTER-INSTALLER](PRINTER-INSTALLER.md), [EVENTSAVER](EVENTSAVER.md) |
|
|
| **Bulk import** | A script loading a site's data. There is no separate import surface: it drives the same endpoints the UI does, with a header that preserves the source rows' timestamps | [IMPORT-API](IMPORT-API.md) |
|
|
| **Core UI API** | The Vue frontend, and anything else holding a token | [CONTRACT-STABILITY](CONTRACT-STABILITY.md) for what is safe to depend on |
|
|
|
|
## How auth works at a glance
|
|
|
|
Four credentials, and which one an operation takes follows from its surface.
|
|
The spec states it per operation; this is the shape behind it.
|
|
|
|
- **JWT** - a login token or a Personal Access Token. The core UI API.
|
|
- **Scoped service token** - `X-API-Key` or `Authorization: Bearer`, minted and
|
|
revoked per purpose (`collector.ingest`, `collector.ingest.endpoints`). A
|
|
token can be bound to specific resources and is refused anything outside
|
|
them. This is what the fleet uses.
|
|
- **A per-PC credential** - an enrolled endpoint holds its own secret, and
|
|
revoking one endpoint costs nothing to the rest. See [COLLECTOR-AUTH](COLLECTOR-AUTH.md)
|
|
and [ADR-019](adr/ADR-019-per-pc-endpoint-credentials.md).
|
|
- **A kiosk device token, or an address inside the kiosk's allowlist** - a
|
|
credential for a screen, not authorization for a person. It says which
|
|
cabinet is asking; the badge on the request says who is standing at it. Both
|
|
kiosks are off by default and answer 404, not 403, when they are off.
|
|
|
|
Two rules cover almost everything else:
|
|
|
|
- **Every read is behind a permission.** Reading is not free, and a site
|
|
decides what a signed-out visitor may see by granting the seeded `guest` role
|
|
the read permissions it chooses - in the same role grid it uses for staff.
|
|
- **Every mutation needs a JWT and a permission or role.** None are public.
|
|
|
|
## What is reachable with no credential
|
|
|
|
This is the inventory a firewall or a deployment reviewer asks for, and it is
|
|
the one question the spec answers badly - it is a question about the whole
|
|
surface, not about any one operation.
|
|
|
|
Do not read it off a page somebody maintains. It is enumerated, with a reason
|
|
per entry, in `tests/test_core/test_public_read_surface.py`, which fails the
|
|
build when a new public read appears without being added to the list. That file
|
|
is the answer, and it is the answer because it cannot go stale without turning
|
|
CI red.
|
|
|
|
## See also
|
|
|
|
- [CONFIG](CONFIG.md) - the environment variables and settings behind these
|
|
endpoints, including the public settings allowlist.
|
|
- [CONTRACT-STABILITY](CONTRACT-STABILITY.md) - what a sister site may depend
|
|
on, and how tightly to pin.
|
|
- [DEPLOY](DEPLOY.md) - the site-exposure surface a deploy reviewer needs.
|