Files
shopdb-flask/docs/API-REFERENCE.md
cproudlock 534f179ee6 Make main the lean product a site receives
main was 16 days and 259 commits behind the line the product is actually built
on, at contract 0.30.0 against 0.35.0, with no per-PC credential code at all. It
was neither current nor curated - just stale. This brings it forward and, at the
same time, makes it mean something: main is what a receiving site installs, and
nothing else.

WHAT IT CARRIES. Twelve plugins - backups, computers, endpoints, guides,
knowledgebase, machines, network, notifications, printers, slides, tools,
warranty - plus applications, which is manifest-less core and always ships. The
only manifest dependency inside that set is endpoints on computers, and both are
in it, so the closure is self-contained.

WHAT IT DOES NOT. employees, geenforce, measuringtools, printedparts and usb
stay on the site line. So do the Windows installer tree, the VM rehearsal
helpers, the agent tooling config, one site's data loader, the internal audit
and findings records, and the planner spreadsheet. 1,135 tracked files against
1,438.

A REMOVAL THAT BREAKS THE APP IS NOT A REMOVAL, and two nearly shipped:

deploy/site-profile-universal.json still named all seventeen plugins. It is the
file docs/DEPLOY.md tells a site to provision from, so a fresh install would
have failed at `flask plugin apply-profile` with PluginError - the first thing a
receiving site does, broken by the branch meant to serve them. It now names the
twelve that exist, and its comment says the two must agree.

agent/ was never refreshed. Whoever populated this branch copied shopdb/,
plugins/, frontend/ and tests/ from the site line and left the agent tree at its
September state: 22 C# and PowerShell sources missing, including AgentCredentials,
EnrollmentService, AgentHttpFactory and the fleet-alert client, plus one file the
site line had deleted still present. The old tree is internally consistent, so it
compiles, and nothing in CI builds it - every gate was green over a server that
speaks enrollment and per-PC credentials to a bundled agent source that cannot.
Resynced whole.

DECISIONS THIS ENCODES, rather than defers:

- The plugin-authoring lab builds printedparts, whose manifest depends on
  employees, so it could not stay. The lab remains on the site line and main
  ships no build-along tutorial. That is a real loss for an adopting developer
  and is recorded here rather than discovered later.
- Recognition, Training and Recertification notifications are no longer seeded.
  Each splits one notification into a card per employee and renders a photo,
  which needs a plugin this branch does not bundle. Seeding them would offer an
  author a type that cannot resolve anybody. A site that installs employees gets
  them from that plugin's own seed.
- Windows/IIS is documented as the legacy path throughout, and the README leads
  with Docker, because that is what a receiving site gets.

The API description is regenerated for THIS branch - 347 paths and 483
operations, down from 415 and 573 - so the published contract describes what is
here rather than what the site line serves. docs/CONFIG.md lost the two
measuring-tool label keys the code no longer declares, which two lanes had
decided opposite ways.

Suite: 3124 passed, 6 skipped. Naming and style gates pass. The docs gates run
rather than skip, and every index line count is derived from the file it names.
2026-09-19 11:24:40 -04:00

90 lines
5.0 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. |
| `GET /api/docs/try` | Swagger UI over the same spec, where Execute sends a real request as whoever is signed in on that browser. Off by default (`API_DOCS_TRYIT_ENABLED`) and not something a production site turns on without deciding to. |
[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.