Files
shopdb-flask/docs/PROJECT-MAP.md
cproudlock afce24379c
All checks were successful
CI / naming (push) Successful in 3s
CI / frontend (push) Successful in 13s
CI / migrations-mysql (push) Successful in 39s
CI / backend (push) Successful in 3m42s
Reading anything needs a permission
A sweep of the URL map found 165 GET routes that answered anybody, with no
token at all: the whole asset inventory, every machine, PC, printer and network
device, the USB checkout history and who is holding which drive, the staff
directory with its photographs, every core report. Guest access had been argued
about one feature-flag at a time while the front door stood open.

Each of those now carries the read permission it belongs to. That also makes
the guest role mean something: a site can open exactly the reads it chooses,
and nothing follows by accident.

Thirty-three endpoints stay public, and each is named with its reason rather
than left to be inferred: the app shell and its bootstrap, the settings
allowlist (the handler already filters an anonymous caller down to branding and
a few keys), images fetched by <img src> which carries no Authorization header,
what a display kiosk renders before anybody signs in, the parts and USB kiosks,
the client scripts that run as SYSTEM before a user session exists, and a
secure-send link whose unguessable token IS the authorisation.

tests/test_core/test_public_read_surface.py is the standing gate, the companion
to the mutating-route sweep that has guarded writes for months. A new public
read fails CI until somebody adds it to that list in front of a reviewer. Four
further reads need a login but no permission, because the handler decides from
who you are - your own user record, your own API tokens, the role catalogue the
grid renders - and the same test proves each still refuses an anonymous caller.
Verified not vacuous by ungating one route and watching the sweep name it.

The employees plugin gained employees.view; the directory is names, SSOs,
photographs and reporting lines, and it had no permission of any kind.

Around ninety tests asserted payload shapes against endpoints that answered
anybody. They are about what the answer says, not about who may ask, so they
are signed in now rather than the gate being loosened. The ones that assert an
anonymous caller is refused were left exactly as they were.
2026-08-27 14:26:06 -04:00

4.3 KiB

Project map

Derived from the code on every regeneration. If something here is wrong, the code changed and the map was not regenerated - fix it by running the generator, never by editing this file.

Versions

series value governed by
product __version__ 0.12.0 ADR-007
plugin contract __contract_version__ 0.21.0 ADR-002

They move independently. A contract bump is not a release.

Alembic chains (ADR-008)

Core runs with flask db upgrade; every plugin chain runs with flask plugin upgrade-all. Both are needed on a deploy.

chain head
core 7d34_singular_relationship_types
backups backups0003clearlastseen
computers computers0001anchor
employees employees0002photo
geenforce geenforce0004minlib
guides guides0001baseline
knowledgebase knowledgebase0001anchor
machines machines0002rename
measuringtools measuringtools0001baseline
network network0003prefix
notifications notifications0005boardorder
printedparts printedparts0004txnrev
printers printers0005observedqueues
slides slides0001anchor
tools tools0001securesend
usb usb0002dropmachineid
warranty warranty0002proof

Bundled plugins (16)

plugin version core_version owns migrations
backups 1.0.0 >=0.16.0,<1.0.0 yes
computers 1.0.0 >=0.20.0,<1.0.0 yes
employees 1.0.0 >=0.1.0,<1.0.0 yes
geenforce 0.1.0 >=0.18.0,<1.0.0 yes
guides 1.0.0 >=0.1.0,<1.0.0 yes
knowledgebase 1.0.0 >=0.1.0,<1.0.0 yes
machines 1.0.0 >=0.20.0,<1.0.0 yes
measuringtools 1.0.0 >=0.20.0,<1.0.0 yes
network 1.0.0 >=0.20.0,<1.0.0 yes
notifications 1.0.0 >=0.1.0,<1.0.0 yes
printedparts 0.1.0 >=0.16.0,<1.0.0 yes
printers 1.0.0 >=0.20.0,<1.0.0 yes
slides 2.0.0 >=0.2.0,<1.0.0 yes
tools 1.0.0 >=0.16.0,<1.0.0 yes
usb 1.0.0 >=0.1.0,<1.0.0 yes
warranty 1.0.0 >=0.2.0,<1.0.0 yes

Manifest-less directories under plugins/ are core frontend surface and always ship: applications.

Architecture decisions

ADR title status
ADR-001-asset-as-platform-contract.md ADR-001: Asset model is the platform contract ACCEPTED
ADR-002-plugin-versioning.md ADR-002: Plugin contract versioning ACCEPTED
ADR-003-plugin-distribution.md ADR-003: Plugin distribution model ACCEPTED
ADR-004-deployment-topology.md ADR-004: Deployment topology (per-site instances) ACCEPTED
ADR-005-equipment-vs-measuringtools.md ADR-005: Equipment plugin scope vs measuringtools plugin ACCEPTED
ADR-006-collector-contract.md ADR-006: Plugin collector contract pattern ACCEPTED
ADR-007-product-versioning-and-releases.md ADR-007: Product versioning and releases ACCEPTED
ADR-008-plugin-migration-ownership.md ADR-008: Plugin migration ownership (per-plugin chains from the cutover) ACCEPTED
ADR-009-frontend-plugin-gating.md ADR-009: Frontend plugin gating ACCEPTED
ADR-010-frontend-plugin-hooks.md ADR-010: Frontend plugin hook contract ACCEPTED
ADR-011-machines-rename.md ADR-011: Rename the equipment domain to machines; retype the models catalog with modeltypes ACCEPTED
ADR-012-geenforce-manifest-ownership.md ADR-012: GE-Enforce manifest ownership in shopdb ACCEPTED
ADR-013-plugin-catalog-and-lean-builds.md ADR-013: Plugin Catalog, Curated Shelf, and Lean Per-Site Builds ACCEPTED
ADR-014-schema-lean-per-site.md ADR-014: Schema-lean per-site builds (retire cross-plugin FKs, lift plugin tables) ACCEPTED
ADR-015-site-specific-configuration.md ADR-015: Where a site's own data is allowed to live ACCEPTED
ADR-016-credential-delivery.md ADR-016: Credential delivery to the fleet ACCEPTED (decided; NOT yet implemented -
ADR-017-buildings-and-levels.md ADR-017: Buildings and levels as the map model ACCEPTED

Size

  • test functions defined: 1417 (parametrised cases collect higher)
  • documented API paths: 305 (docs/openapi.json, regenerate with scripts/gen_openapi.py)