diff --git a/.claude/QUICK-REFERENCE.md b/.claude/QUICK-REFERENCE.md
index 75232567..bdc49bbd 100644
--- a/.claude/QUICK-REFERENCE.md
+++ b/.claude/QUICK-REFERENCE.md
@@ -11,29 +11,58 @@ Fast lookup for architectural decisions and contracts.
- **ADR-008**: Plugin migration ownership - PLUGIN_TABLE_OWNERS, migration chains
- **ADR-013**: Plugin catalog - Lean builds, plugin manifest
- **ADR-015**: Site-specific configuration - Where site literals belong
-- **ADR-016**: Credential delivery - Device tokens, kiosk auth
+- **ADR-016**: Credential delivery to the fleet - cmdkey credentials for bays,
+ targeting and rotation. ACCEPTED but NOT built. It says nothing about device
+ tokens or kiosk auth, and nothing forbids the registry: it names registry
+ provisioning as a recommended first step
## Import Surface (stable API)
+Every name below is in `shopdb/api/__init__.py` `__all__`. That list is the
+surface; a name that is not in it is not importable from `shopdb.api`, whatever
+else in the tree defines it.
+
```python
from shopdb.api import (
# Database
db,
-
- # Core Models
- Asset, Employee, User, Building, MapLevel,
-
- # Plugin Base
- BasePlugin,
-
- # Auth/Authz
- requires_role, requires_device_token,
-
+
+ # Core models
+ Asset, AssetType, AssetStatus, AssetRelationship,
+ Location, BusinessUnit, Model, OperatingSystem, Vendor,
+ User, Role, Setting,
+
+ # Auth / authz
+ require_permission, require_role, require_any_permission,
+ caller_holds, caller_is_role,
+ client_ip, ip_in_cidrs, request_ip_allowlisted,
+
+ # Responses and paging
+ success_response, error_response, ErrorCodes,
+ get_pagination_params, paginate_query, paginated_response,
+
# Utilities
- badges, datevalues, queryargs
+ audit_log, cache,
+ normalise_badge, badge_kind, BadgeShapeError,
+ parse_date, parse_date_fields, DateValueError,
+ intarg, intargs, QueryArgError,
+ resolve_asset_position,
)
```
+NOT on this surface. This block used to name all six, so the first import a new
+agent copied raised ImportError:
+
+- `BasePlugin`, `PluginMeta` - `from shopdb.plugins.base import ...`, which is
+ what every `plugins/*/plugin.py` in the tree does.
+- `Building`, `MapLevel` - `shopdb.core.models.maplevel`. An asset's position
+ normally comes back through `resolve_asset_position`, which IS exported.
+- `Employee` - no such model. The directory lives in a read-only external
+ database reached through `employee_connection`, and the plugin's own row type
+ is `DirectoryEmployee` in `plugins/employees/models/`.
+- `badges`, `datevalues`, `queryargs` - module names under `shopdb/utils/`, not
+ exported. Import the functions listed above instead.
+
## Plugin Structure
```
@@ -55,9 +84,12 @@ plugins/{name}/
## Database Naming
-- **Tables**: `printedpart`, `mappeddrives` (lowercase concatenated)
-- **Columns**: `serial_number`, `last_seen` (snake_case)
-- **Enforced**: `scripts/check-naming-and-style.sh`
+- **Tables**: `machines`, `mappeddrives` (lowercase concatenated, plural)
+- **Columns**: `machineid`, `lastzabbixsync`, `isactive` (lowercase concatenated,
+ singular). NOT snake_case
+- **Indexes**: `idx_machines_locationid`. The only place an underscore is allowed
+- **Enforced**: `scripts/check-naming-and-style.sh`, on model attributes AND on the
+ column names a migration creates
## Branch Model
@@ -132,19 +164,98 @@ PLUGIN_TABLE_OWNERS = {
}
```
-## Device Tokens (ADR-016)
+## Kiosk Access
-Kiosks require device authentication:
+Not ADR-016 - that ADR is about cmdkey credentials for bays and is not built.
+A kiosk route admits a caller on EITHER ground, both gated on the kiosk's own
+enable flag:
+
+This is `plugins/usb/api/routes.py` as it stands, not a paraphrase:
```python
-from shopdb.api import requires_device_token
+from shopdb.api import Setting, client_ip, ip_in_cidrs
-@bp.route('/kiosk/endpoint')
-@requires_device_token(['usb_kiosk', 'parts_kiosk'])
-def kiosk_view():
- return render_template('kiosk.html')
+KIOSK_NEVER_A_CLIENT = '127.0.0.0/8, ::1/128'
+KIOSK_TRUSTED_PROXIES = 'usb_kiosk_trusted_proxies'
+
+def _kiosk_client_ip():
+ return client_ip(request)
+
+def _kiosk_address_is_infrastructure(address):
+ if ip_in_cidrs(address, KIOSK_NEVER_A_CLIENT):
+ return True
+ return ip_in_cidrs(address, Setting.get(KIOSK_TRUSTED_PROXIES))
+
+def _kiosk_ip_allowlisted():
+ address = _kiosk_client_ip()
+ if _kiosk_address_is_infrastructure(address):
+ return False
+ return ip_in_cidrs(address, Setting.get('usb_kiosk_allowed_cidrs'))
+
+def _kiosk_open():
+ if not _kiosk_enabled(): # the site has to turn it on first
+ return False
+ return _kiosk_token_presented() or _kiosk_ip_allowlisted()
```
+THIS IS NOT `request_ip_allowlisted(request, cidrs)`. That helper is exactly
+`ip_in_cidrs(client_ip(request_obj), rawcidrs)` and nothing else
+(`shopdb/utils/ipallowlist.py`), so it has no infrastructure refusal. The USB
+kiosk refuses an address that belongs to the path rather than to a caller BEFORE
+it consults the allowlist, and that refusal is the whole point: see the topology
+below. Writing the helper here instead of the real shape is how the two got
+confused.
+
+- The allowlists are per kiosk (`usb_kiosk_allowed_cidrs`,
+ `printedparts_kiosk_allowed_cidrs`) and ship EMPTY, so a site that enters no
+ ranges stays token-only.
+- `ip_in_cidrs` fails closed on an empty setting, an unparseable caller address
+ and a malformed entry. Never copy a CIDR parser into a plugin.
+- A refused caller gets 404, not 403: these routes do not advertise themselves.
+- The two kiosks HAVE DIVERGED. `plugins/printedparts/api/routes.py:556` still
+ calls `request_ip_allowlisted(request, Setting.get('printedparts_kiosk_allowed_cidrs'))`
+ with no infrastructure check, so a site that allowlists loopback to work
+ around the topology below opens that kiosk to every caller. Read the plugin
+ you are editing; do not assume the other one matches.
+
+### Why the infrastructure refusal exists (the shipped Windows path)
+
+Trace it, do not guess. IIS hands the request to HttpPlatformHandler, which
+connects to waitress on 127.0.0.1 with
+`--trusted-proxy=127.0.0.1 --trusted-proxy-headers=x-forwarded-for
+--trusted-proxy-count=1` (`deploy/windows/web.config`). waitress, seeing the
+trusted peer, overwrites `environ['REMOTE_ADDR']` from the LAST X-Forwarded-For
+hop. `client_ip()` then returns `strip_port(request.remote_addr)`
+(`shopdb/utils/clientip.py`) - it never reads the header itself.
+
+The catch: IIS does not set X-Forwarded-For on its own, and the rewrite rule
+that would is commented out in `web.config` - it SHIPS DISABLED because it needs
+the URL Rewrite module. So on a default install there is no header, waitress has
+nothing to promote, and every caller arrives as 127.0.0.1. `TRUSTED_PROXY_COUNT`
+defaults to 0 (`shopdb/config.py`), so ProxyFix is not installed either, and it
+would not help: waitress already did that job.
+
+Consequences a kiosk change has to respect:
+
+- Until someone enables the rule (`shopdb-install.ps1 -ClientIpSource direct`
+ installs URL Rewrite, allows `HTTP_X_FORWARDED_FOR` at server level and
+ uncomments it), a CIDR allowlist admits nobody and the kiosk is token-only.
+ That is the fail-closed direction and it is correct.
+- Allowlisting 127.0.0.0/8 to "fix" that would admit everyone, which is why
+ `KIOSK_NEVER_A_CLIENT` refuses it whatever the setting says.
+- `-ClientIpSource proxy` leaves the rule off ON PURPOSE: behind ARR or a load
+ balancer, REMOTE_ADDR is the proxy and the rule would destroy the real client
+ address. There the upstream proxy sets the header.
+
+## Fleet Credentials
+
+- Agent and GE-Enforce reporters run as SYSTEM, so there is nobody to prompt.
+ The collector key, base URL and server pins come from
+ `HKLM\SOFTWARE\GE\ShopDB`. That hive is the design, not a violation.
+- An enrolled PC's own per-PC credential is DPAPI-protected in a SYSTEM-only
+ directory and sent as `X-Endpoint-Credential`; the shared fleet key still
+ works for a PC that has not enrolled and is refused for one that has.
+
## Wiki Location
Full docs: `~/Documents/github/shopdb-flask.wiki/`
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bfa99c3d..adf36346 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -89,8 +89,19 @@ jobs:
lean-build:
# ADR-013 Phase 5: prove a per-site build carries only its chosen plugins.
- # Builds a lean site (machines + printers) and asserts an omitted plugin's
- # code is absent from the bundle - the delete-a-plugin guarantee in CI.
+ # The profile is the WJ plugin set, so this job proves the build this branch
+ # actually ships, not a two-plugin toy. The omission guarantee is what is
+ # under test, so the assertions below name a component from EVERY omitted
+ # plugin: a set that grows without its check growing is how a leak ships.
+ #
+ # Chosen (12): backups computers endpoints guides knowledgebase machines
+ # network notifications printers slides tools warranty
+ # Omitted (5): employees geenforce measuringtools printedparts usb
+ # 'applications' is manifest-less core and ships in every build (ADR-013).
+ # endpoints hard-depends on computers, which is already chosen, so the
+ # closure adds nothing - if that changes, resolve_plugin_closure.py pulls
+ # the dependency in and the absence assertions below start failing, which is
+ # the signal to update this list rather than to loosen the check.
runs-on: arc-runner-set
steps:
- uses: actions/checkout@v5
@@ -100,21 +111,28 @@ jobs:
cache: npm
cache-dependency-path: frontend/package-lock.json
- run: cd frontend && npm ci
- - name: Build lean site (machines + printers)
+ - name: Build lean site (WJ plugin set)
run: |
- printf '{ "site": "ci-lean", "plugins": ["machines", "printers"] }' \
+ printf '%s' '{ "site": "ci-lean", "plugins": ["backups", "computers", "endpoints", "guides", "knowledgebase", "machines", "network", "notifications", "printers", "slides", "tools", "warranty"] }' \
> /tmp/lean-profile.json
bash scripts/build-site.sh /tmp/lean-profile.json /tmp/leansite
- name: Assert omitted plugin code is absent, chosen present
run: |
assets=/tmp/leansite/frontend-dist/assets
- for code in PartsKiosk ManifestEditor USBLabelBatch KnowledgeBaseDetail; do
+ # One component per OMITTED plugin: employees, geenforce,
+ # measuringtools, printedparts, usb.
+ for code in EmployeeDirectory ManifestEditor MeasuringToolsList PartsKiosk USBLabelBatch; do
if grep -rqoh "$code" "$assets"/*.js; then
echo "FAIL: omitted-plugin code '$code' leaked into the lean bundle"
exit 1
fi
done
- for code in MachineDetail PrinterDetail; do
+ # One component per CHOSEN plugin, so a plugin silently dropped from
+ # the bundle fails here instead of on the floor.
+ for code in BackupHistory PCsList EndpointsList GuidesList \
+ KnowledgeBaseDetail MachineDetail NetworkDevicesList \
+ NotificationsList PrinterDetail SlideManager ToolsIndex \
+ WarrantiesList; do
grep -rqoh "$code" "$assets"/*.js || {
echo "FAIL: chosen-plugin code '$code' missing from the lean bundle"
exit 1; }
@@ -124,8 +142,18 @@ jobs:
grep -rqoh "ApplicationsList" "$assets"/*.js || {
echo "FAIL: core page 'ApplicationsList' missing from the lean bundle"
exit 1; }
- test -d /tmp/leansite/plugins/machines
- test ! -d /tmp/leansite/plugins/printedparts
+ for plugin in backups computers endpoints guides knowledgebase \
+ machines network notifications printers slides tools \
+ warranty; do
+ test -d "/tmp/leansite/plugins/$plugin" || {
+ echo "FAIL: chosen plugin '$plugin' missing from the backend tree"
+ exit 1; }
+ done
+ for plugin in employees geenforce measuringtools printedparts usb; do
+ test ! -d "/tmp/leansite/plugins/$plugin" || {
+ echo "FAIL: omitted plugin '$plugin' staged into the backend tree"
+ exit 1; }
+ done
echo "lean build verified: only chosen plugins present"
# NOTE: the MySQL-8 migration/seed job (fresh `flask db upgrade` + every
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b6b5691..b618f80e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -702,6 +702,187 @@ ADR-007 and ADR-002.
`plugins/tools/frontend/labelPages.js`, where the column contract and the
two-sided page order are covered by tests.
+### Added
+
+- **Every PC gets its own endpoint credential, and enrolls to get it.** The
+ fleet authenticated with one shared collector key: every bay held the same
+ secret, one of them leaking meant reissuing all of them, and a PC could post
+ a check-in naming any asset it liked. A PC now enrolls once and is
+ authenticated as itself thereafter. `POST /api/endpoints/enroll` issues a
+ credential to a PC that matches exactly one computer on hostname and BIOS
+ serial and holds none yet; anything else - unknown hostname, a duplicate, a
+ serial that disagrees, a PC already enrolled - is recorded as a request an
+ administrator approves or rejects, and the agent's persisted nonce is what
+ binds an approved request back to the PC that asked. Enroll never refuses, so
+ a PC installed with no key at all still turns up in the queue instead of
+ vanishing. One auth helper serves every agent route: with
+ `X-Endpoint-Credential` the asset comes from the credential, so a request
+ naming a different PC is refused with `ASSET_MISMATCH` rather than believed,
+ and a revoked credential answers `CREDENTIAL_INVALID` so the agent
+ re-enrolls rather than going quiet. The core collector takes the credential
+ for the computers, printers and endpoints collectors through two hooks on
+ `shopdb.api` (contract 0.33.0).
+
+ Settings > Endpoints > Enrollment is where this is run. The Migration tab
+ shows readiness counts, each PC's state with the reasons it is blocked,
+ "Upgrade selected" and "Upgrade next 25", and live wave progress; a wave
+ packages the agent once and queues the existing per-PC upgrade. The
+ Credentials tab lists enrolled PCs with revoke, pending requests with
+ approve and reject, and the two switches. The fleet list gained a Credential
+ column and filter, and a PC's detail page shows its credential state, with
+ Revoke and Allow re-enrollment for an administrator. Migration 0023 creates
+ `endpointcredentials`, `endpointenrollrequests` and
+ `endpointmigrationwaves`.
+
+- **The agent pins the server it talks to, and the registry owns the pins.**
+ An air-gapped site runs its own certificate, so "trusted chain" says very
+ little; the agent now checks the server's SubjectPublicKeyInfo SHA-256
+ against a pinned list before it will send a credential anywhere, and a
+ credential is only ever sent back to the server that issued it. Pins live in
+ `HKLM\SOFTWARE\GE\ShopDB` under `ServerPins`, with `appsettings.json` as the
+ fallback. The registry is AUTHORITATIVE whenever that value exists, even
+ when it is empty: the two used to be merged, so removing a pin from HKLM
+ never took effect anywhere. Two pins are carried at once, current and next,
+ so a certificate can be rotated without a flag day.
+ `Set-ShopDBServerPins.ps1` is the GE-Enforce entry point (`-Clear` deletes
+ the value, `-None` writes an empty one) and `Get-ShopDBServerPin.ps1` reads
+ what a PC has. `GET /settings/server-cert` returns the pin to put in that
+ entry, the Server Certificate page shows it, and the page also compares the
+ certificate the site actually presents at its public address with the
+ certificate file, warning when the two disagree.
+
+- **An agent on a one-off PC from a single command.** A PC outside GE-Enforce -
+ a one-off, a loaner, something rebuilt in a hurry - had no path to an agent
+ short of doing the whole managed install by hand. An administrator now
+ generates a short-lived token in Settings > Endpoints > Endpoints and hands a
+ technician one elevated PowerShell line. The script refuses to run
+ unelevated, refuses a PC that already has an agent (a managed bay gets its
+ agent from GE-Enforce), checks both binaries against hashes the server baked
+ into the package, and installs with NO shared key - the PC then asks to
+ enroll and the script watches locally for two minutes for an administrator to
+ approve it. The token routes answer 404 to anything wrong rather than
+ confirming a token exists, are rate limited per address and are audited with
+ the caller, and a bootstrap package can never carry a collector key.
+ Migration `0024_endpoint_bootstrap_tokens` adds the token table, which stores
+ a hash, an expiry and a use count.
+
+- **A kiosk admits a caller by where it is on the network, not only by its
+ token.** The device token assumes a handful of fixed screens provisioned
+ once, and that is not how these kiosks are used: most people open the page on
+ their own PC, there is no login, and per-browser provisioning would mean
+ touching every PC on the floor and losing it again at the next reimage.
+ Both the USB and printed-parts kiosks now admit a caller on either ground -
+ the per-device token, or a source address inside an allowlist - the same
+ shape the GE-Enforce client endpoints already use. `shopdb.api` gained
+ `ip_in_cidrs` and `request_ip_allowlisted` (contract 0.35.0) so the two
+ plugins share one gate rather than two copies of a CIDR parser; they fail
+ closed on an empty setting, an unparseable caller address and a malformed
+ entry, and they read `remote_addr`, so the IIS rewrite rule is what makes
+ them meaningful. The allowlists are separate per kiosk
+ (`usb_kiosk_allowed_cidrs`, `printedparts_kiosk_allowed_cidrs`) for the same
+ reason the tokens are, and both SHIP EMPTY: nothing opens by itself, and a
+ site that enters no ranges keeps exactly today's token-only behaviour. Both
+ settings pages get a picker listing the network plugin's subnets with their
+ VLANs, storing resolved CIDR strings rather than subnet ids so the setting
+ still works at a site that does not run that plugin. Stated plainly in the
+ admin UI: anyone on an allowed network can check a drive in or out against a
+ badge number they guess, and badges are attribution, not authorization.
+
+- **A kiosk says when it is not set up, and an administrator can copy the setup
+ link.** The settings API masks every key whose name contains `token`, so the
+ real kiosk token is on screen for exactly one moment - after Generate, before
+ Save - and a screen provisioned with the literal mask answers 404, which is
+ also how the route answers a caller it has never heard of. Each kiosk gained
+ a permission-gated provisioning-link route that returns the real setup URL to
+ somebody who already holds `settings.edit`, and both settings pages a "Copy
+ setup link" button that falls back to showing the link when the clipboard is
+ refused. Revealing a token writes an audit row. The USB kiosk now asks the
+ server on load whether this screen may be used and says plainly when it may
+ not, instead of rendering a keypad that only fails once somebody has typed
+ their SSO.
+
+- **The USB audit says whether a returned drive came back wiped and scanned.**
+ Removable-media custody is CMMC evidence, and custody without the state of
+ the media on return is half the record. Both stores already held the values -
+ `sanitized` and `scanned_viruses` in the external `checkinoutlog`,
+ `waswiped` and `scannedviruses` in the self-hosted `usbcheckouts` - and
+ neither mode selected them. Both now report them in the JSON, the CSV and the
+ column list. Only a return carries them, so a check-out row shows blanks
+ rather than asserting something about an event that had not happened yet, and
+ unknown stays blank rather than rendering as "No": both stores keep these
+ nullable deliberately, and "nobody wrote it down" is a different claim from
+ "it came back unwiped".
+
+### Changed
+
+- **Endpoint agent 1.20.1 and tray 1.7.1. Ship them together.** Every HTTP
+ client in the agent, script and package downloads included, is now built by
+ one `AgentHttpFactory`, so there is a single place the pins and the
+ credential are applied rather than one per caller. `EnrollmentService`
+ enrolls once using whatever shared key the PC already has, stores the
+ credential DPAPI-protected in a SYSTEM-only directory and swaps to it without
+ a restart; an old server with no enroll route simply leaves the agent on the
+ shared key. A named pipe lets the interactive user ask the agent to send
+ status, send a report, list alerts and acknowledge one, so `--once` and
+ `--report` run as a user become pipe clients, and `appsettings.json` is
+ restricted to SYSTEM and Administrators once the pipe is up. The tray holds
+ no key and reads no agent configuration at all - everything it does goes
+ through that pipe, an acknowledgement is remembered only once the agent
+ confirms it, and when the agent is not running the menu says so instead of
+ failing quietly.
+
+ The version numbers moved from 1.20.0 and 1.7.0 with no behaviour change of
+ their own: those binaries were built and shipped to the display scope before
+ two fixes landed, so a rebuild carried different bytes under the same version
+ number. Detection is by hash and copes with that, but nobody reading a
+ version number can tell two 1.20.0.0 files apart, and the runbook asks for one
+ build across the share, the agent store and the display profile.
+
+- **A PC that has enrolled is refused the shared fleet key.** Once a PC holds
+ its own credential, presenting the fleet key is either a misconfiguration or
+ somebody else using a leaked secret, and either way the right answer is not
+ to accept it: that call is refused with `SHARED_KEY_REFUSED`. This only ever
+ applies to a PC that has already enrolled. `plugin.endpoints.sharedkeyallowed`
+ still DEFAULTS TO ON, so every un-migrated bay goes on reporting exactly as it
+ does today; a site turns it off deliberately once its fleet has moved, and the
+ page asks for confirmation and says how many PCs are not enrolled yet.
+ `plugin.endpoints.autoenroll` also defaults to on, so a clean hostname and
+ serial match is issued a credential without an administrator waiting on it.
+
+- **The shared-key page now reads as the migration state it is.** Settings >
+ Endpoints presents the key as what PCs use UNTIL they enroll, with enrollment
+ progress, a link to Enrollment, and rotate and revoke confirmations that say
+ how many PCs would lose their only credential. The settings cards describe
+ the server certificate as what PCs actually receive, on IIS or Docker.
+ `AGENT-CONTRACT.md` documents per-PC credentials, and the rollout runbook
+ covers the phases, the gates, rollback, the pin and wave entries, and every
+ place the shared key lives and how to clear it at cutover.
+
+### Removed
+
+- **Client certificate authentication, the 2026-09-14 attempt.** `client_cert.py`,
+ certificate renewal, expiry monitoring, the unauthenticated
+ `/test/client-cert` route, the nginx client-certificate forwarding and the
+ agent certificate script are all gone. The header carrying the client
+ certificate could be forged on IIS, the private key was readable by users,
+ and the shared key remained a fallback underneath it all, so it bought
+ nothing the per-PC credential above does not buy properly. Migration 0023
+ drops the certificate columns from `assets` where a test database has them;
+ 0021 and 0022 keep their ids but no longer touch the core table.
+
+### Security
+
+- **The tray no longer holds a secret.** It ran as the interactive user and
+ carried enough configuration to talk to the server on its own. Everything it
+ needs now comes over the agent's pipe, which means a standard user on a bay
+ can read nothing out of it.
+
+- **`collect_log` refuses to collect the things that hold credentials.** Setup
+ answer files, `appsettings.json`, agent directories and key files are refused
+ at enqueue rather than shipped to the server inside a log bundle. The setup
+ token is no longer printed to the log either, and two VM notes that carried a
+ live collector key and advised plain http were deleted rather than corrected.
+
## [0.12.0] - 2026-08-20
Printers become a property of the bay rather than of whoever last walked up to a
diff --git a/CLAUDE.md b/CLAUDE.md
index abd32fc9..83177715 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,12 +1,12 @@
# ShopDB Flask - Claude Code Context
-ShopDB is a manufacturing floor asset management system built with Flask, Vue 3, and PostgreSQL, deployed across air-gapped GE Aerospace sites.
+ShopDB is a manufacturing floor asset management system built with Flask, Vue 3, and MySQL, deployed across air-gapped GE Aerospace sites.
## Architecture
- **Backend**: Flask 3.x with SQLAlchemy 2.x, plugin-based architecture
- **Frontend**: Vue 3 with Vite, component-based UI with plugin panels
-- **Database**: PostgreSQL 15+ with Alembic migrations per plugin
+- **Database**: MySQL 8 with Alembic migrations per plugin (`docker-compose.yml` runs `mysql:8.4`; WJ prod is IIS in front of MySQL 8)
- **Deployment**: Docker Compose with gunicorn, air-gapped sites via bundle workflow
- **Plugins**: 20+ plugins (computers, endpoints, printers, machines, etc.)
@@ -27,10 +27,13 @@ Each plugin owns its migration chain in `plugins/{name}/migrations/versions/`.
- Migrations run automatically on deploy
### Database Naming Convention
-- **Tables**: lowercase concatenated, no underscores (e.g., `printedpart`, `mappeddrives`)
-- **Columns**: snake_case (e.g., `serial_number`, `last_seen`)
-- **Indexes/constraints**: follow PostgreSQL conventions
-- Enforced by `scripts/check-naming-and-style.sh` pre-commit hook
+- **Tables**: lowercase concatenated, plural, no underscores (e.g., `machines`, `mappeddrives`)
+- **Columns**: lowercase concatenated, singular, no underscores (e.g., `machineid`,
+ `lastzabbixsync`, `isactive`). NOT snake_case - see CONTRIBUTING.md
+- **Indexes**: `idx_
_`. Underscores are allowed HERE ONLY, because an
+ index is infrastructure and not data
+- Enforced by `scripts/check-naming-and-style.sh` pre-commit hook, on model attributes
+ AND on the column names a migration creates
### Branch and Deployment Model
- `wj` -> prod (shop floor at WJ site)
@@ -54,10 +57,20 @@ Plugins extend ShopDB via:
- `frontend/views/` - Vue components auto-discovered by router
- `migrations/versions/` - Alembic migrations
-Import from `shopdb.api` for stable surfaces:
+Import from `shopdb.api` for stable surfaces. Every name has to be in that
+module's `__all__`; this line used to read
+`db, Asset, Employee, BasePlugin` and two of the four do not resolve, so the
+first block a new agent copied raised ImportError:
```python
-from shopdb.api import db, Asset, Employee, BasePlugin
+from shopdb.api import db, Asset, User, Setting, require_permission
+# BasePlugin is NOT on that surface. plugin.py takes it from its own module,
+# which is what every plugins/*/plugin.py in this tree does:
+from shopdb.plugins.base import BasePlugin, PluginMeta
```
+There is no `Employee` model. The directory is an external read-only database
+reached through `employee_connection`; the plugin's own row type is
+`DirectoryEmployee`. See `.claude/QUICK-REFERENCE.md` for the full export list
+and for the other names that are commonly assumed and are not there.
## Documentation
@@ -69,7 +82,7 @@ Full documentation in the GitHub wiki at `../shopdb-flask.wiki/`:
- ADR-006: Collector contract (endpoint agent)
- ADR-007: Product versioning and releases
- ADR-013: Plugin catalog and lean builds
-- ADR-016: Credential delivery
+- ADR-016: Credential delivery to the fleet (cmdkey credentials for bays; ACCEPTED, NOT built - it is not about kiosks or device tokens)
**References**:
- `API-REFERENCE.md` - Core API surface
@@ -116,7 +129,24 @@ Full documentation in the GitHub wiki at `../shopdb-flask.wiki/`:
- **Migration conflicts**: Keep plugin chains linear, coordinate with PLUGIN_TABLE_OWNERS
- **Unicode**: ASCII only - check logs, comments, commit messages
- **Import surface**: Use `shopdb.api`, not internal paths
-- **Device tokens**: Kiosks require device_token authentication (ADR-016)
+- **Kiosk access**: a kiosk route admits a caller on either of two grounds - the
+ per-kiosk device token, or a source address inside that kiosk's CIDR allowlist
+ (`usb_kiosk_allowed_cidrs`, `printedparts_kiosk_allowed_cidrs`) - and both are
+ gated on the kiosk's enable flag. Use `ip_in_cidrs` from `shopdb.api`; do not
+ hand-roll a CIDR parser. `request_ip_allowlisted` is only
+ `ip_in_cidrs(client_ip(request), cidrs)` - the USB kiosk does NOT use it,
+ because it first refuses an address that belongs to the path rather than to a
+ caller (loopback, or a configured trusted proxy). On the shipped Windows stack
+ the X-Forwarded-For rewrite rule is disabled, so every caller arrives as
+ 127.0.0.1 and that refusal is what keeps the door shut. The real shape and the
+ full trace are in `.claude/QUICK-REFERENCE.md`. There is no
+ `requires_device_token` decorator
+- **Fleet credentials are in the registry, deliberately**: the endpoint agent and the
+ GE-Enforce reporters run as SYSTEM with no user to prompt, and read their collector
+ key, base URL and server pins from `HKLM\SOFTWARE\GE\ShopDB`. An enrolled PC's
+ own per-PC credential is DPAPI-protected in a SYSTEM-only directory instead.
+ Nothing forbids the registry hive - ADR-016 names registry provisioning as a
+ recommended first step
## Work PC Role
diff --git a/deploy/windows/installer/shopdb-install.ps1 b/deploy/windows/installer/shopdb-install.ps1
index afe9e052..3701897e 100644
--- a/deploy/windows/installer/shopdb-install.ps1
+++ b/deploy/windows/installer/shopdb-install.ps1
@@ -124,8 +124,11 @@ param(
# the proxy, so applying it would DESTROY the real client IP.
#
# Without one or the other, IIS sends no X-Forwarded-For at all and every
- # client reads as 127.0.0.1: the GE-Enforce IP allowlist, the dashboard
- # visitor-location lookup and per-host login rate limiting all break quietly.
+ # client reads as 127.0.0.1: the GE-Enforce IP allowlist, BOTH logged-out
+ # kiosk allowlists (usb_kiosk_allowed_cidrs, printedparts_kiosk_allowed_cidrs
+ # - these admit writes with no login), the dashboard visitor-location lookup
+ # and per-host login rate limiting all break quietly. Stage 4 verifies this
+ # in both modes; see the client-IP check there.
[ValidateSet('direct','proxy')] [string] $ClientIpSource = 'direct',
# Load the shared vendor/model catalog during stage 3. Offered by the
# wizard rather than assumed: a site that machines nothing does not want
@@ -2500,27 +2503,71 @@ public class ShopdbSmokeTestCertPolicy : ICertificatePolicy {
}
}
- # Is the client-IP rule actually in effect? It is the control that makes
- # remote_addr trustworthy: without it IIS forwards whatever X-Forwarded-For
- # the CALLER sent, waitress trusts it because it arrives from loopback, and
- # the GE-Enforce IP allowlist becomes bypassable from anywhere on the network.
- # It does not fail closed. Verified here rather than assumed, because the
- # failure is silent and looks exactly like a working server.
+ # Is the client-IP control actually in effect? Something has to make the
+ # address the app sees the CALLER's: waitress is launched with
+ # --trusted-proxy=127.0.0.1 --trusted-proxy-headers=x-forwarded-for, and
+ # HttpPlatformHandler always connects from loopback, so waitress ALWAYS
+ # believes the X-Forwarded-For it is handed. Whether that header is worth
+ # believing is decided in front of it, and nothing fails closed if it is not.
+ #
+ # THREE allowlists now ride on this, not one: the GE-Enforce client
+ # allowlist and the two kiosk allowlists (usb_kiosk_allowed_cidrs,
+ # printedparts_kiosk_allowed_cidrs), which admit UNAUTHENTICATED writes by
+ # network position. Naming only GE-Enforce here sent people to the wrong
+ # place - and left the kiosks out of the decision entirely.
+ #
+ # Checked in BOTH modes. It used to run only under -ClientIpSource direct,
+ # so the proxy deployment - the one where the header really does arrive from
+ # outside - had nothing verified at all.
+ $liveCfg = Join-Path $AppRoot 'web.config'
+ $cfgText = if (Test-Path $liveCfg) { Get-Content $liveCfg -Raw } else { '' }
+ $ruleLive = ($cfgText -match '') -and ($cfgText -notmatch 'SHOPDB-CLIENTIP-BEGIN')
+ $moduleThere = Test-Path (Join-Path $env:windir 'system32\inetsrv\rewrite.dll')
if ($ClientIpSource -eq 'direct') {
- $liveCfg = Join-Path $AppRoot 'web.config'
- $cfgText = if (Test-Path $liveCfg) { Get-Content $liveCfg -Raw } else { '' }
- $ruleLive = ($cfgText -match '') -and ($cfgText -notmatch 'SHOPDB-CLIENTIP-BEGIN')
- $moduleThere = Test-Path (Join-Path $env:windir 'system32\inetsrv\rewrite.dll')
if ($ruleLive -and $moduleThere) {
Write-Log 'client-IP rule is live; remote_addr is trustworthy' 'OK'
} else {
Write-Log 'CLIENT-IP RULE IS NOT IN EFFECT' 'WARN'
if (-not $moduleThere) { Write-Log ' URL Rewrite module is not installed' 'WARN' }
if (-not $ruleLive) { Write-Log ' web.config does not have an active block' 'WARN' }
- Write-Log ' every client will be recorded as 127.0.0.1, and the GE-Enforce IP' 'WARN'
- Write-Log ' allowlist is BYPASSABLE - a caller can send its own X-Forwarded-For' 'WARN'
- Write-Log ' re-run with -ClientIpSource direct, or use token authentication' 'WARN'
+ Write-Log ' a caller can send its own X-Forwarded-For and be believed, so EVERY' 'WARN'
+ Write-Log ' address-based control is BYPASSABLE from anywhere on the network:' 'WARN'
+ Write-Log ' - the GE-Enforce client IP allowlist' 'WARN'
+ Write-Log ' - usb_kiosk_allowed_cidrs (logged-out USB kiosk writes)' 'WARN'
+ Write-Log ' - printedparts_kiosk_allowed_cidrs (logged-out parts kiosk writes)' 'WARN'
+ Write-Log ' and with no header at all every client is recorded as 127.0.0.1.' 'WARN'
+ Write-Log ' the USB kiosk refuses a loopback caller outright rather than' 'WARN'
+ Write-Log ' admitting one, so that case reads as "not set up", not as access.' 'WARN'
+ Write-Log ' install URL Rewrite and enable the SHOPDB-CLIENTIP block, or leave the' 'WARN'
+ Write-Log ' two kiosk allowlists EMPTY and provision each screen with its token' 'WARN'
}
+ } else {
+ # Proxy mode. The rule must be OFF here - it would overwrite the real
+ # client with the proxy's address - and the thing that has to be true
+ # instead is about the proxy, which this script cannot reach out and
+ # test. So: check what can be checked, and state the rest plainly.
+ if ($ruleLive) {
+ Write-Log 'web.config OVERWRITES X-Forwarded-For but -ClientIpSource is proxy' 'WARN'
+ Write-Log ' every request will be attributed to the proxy, not the caller:' 'WARN'
+ Write-Log ' the GE-Enforce and kiosk allowlists then match nothing at all' 'WARN'
+ Write-Log ' remove the block from web.config' 'WARN'
+ } else {
+ Write-Log 'client IP comes from the proxy; no rewrite rule in web.config' 'OK'
+ }
+ Write-Log 'PROXY MODE: two things this installer cannot verify for you' 'WARN'
+ Write-Log ' 1. the proxy must APPEND the caller to X-Forwarded-For (ARR does).' 'WARN'
+ Write-Log ' waitress takes the LAST hop and puts it in REMOTE_ADDR, so a proxy' 'WARN'
+ Write-Log ' that passes a caller-sent header through unchanged makes every' 'WARN'
+ Write-Log ' address control bypassable from anywhere that can reach the proxy.' 'WARN'
+ Write-Log ' 2. set usb_kiosk_trusted_proxies (Settings > USB) to where the proxy' 'WARN'
+ Write-Log ' is, BEFORE filling in usb_kiosk_allowed_cidrs. The app does not' 'WARN'
+ Write-Log ' read forwarding headers itself - waitress already promoted the' 'WARN'
+ Write-Log ' caller into REMOTE_ADDR, so the allowlist matches the CALLER, not' 'WARN'
+ Write-Log ' the proxy. What it is for is the opposite case: a request that' 'WARN'
+ Write-Log ' arrives wearing the PROXY address, because the proxy forwarded' 'WARN'
+ Write-Log ' nothing. A proxy normally sits on the same plant network the' 'WARN'
+ Write-Log ' allowlist names, so that request would otherwise be admitted on no' 'WARN'
+ Write-Log ' evidence at all. Naming the proxy here subtracts it and fails closed.' 'WARN'
}
# The self-hosted API documentation. It is the thing an operator - or the
diff --git a/docs/api-inventory.json b/docs/api-inventory.json
new file mode 100644
index 00000000..53b0d85a
--- /dev/null
+++ b/docs/api-inventory.json
@@ -0,0 +1,4787 @@
+[
+ {
+ "surface": "core-apitokens",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/apitokens",
+ "auth": "jwt",
+ "params": "not documented",
+ "purpose": "List the caller's own tokens. Admins may pass ?all=true for everyone's.",
+ "example": "curl http://localhost:5001/api/apitokens -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/apitokens",
+ "auth": "permission:apitokens.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a token for the caller. Returns the full secret ONCE.",
+ "example": "curl -X POST http://localhost:5001/api/apitokens -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/apitokens/",
+ "auth": "permission:apitokens.create",
+ "params": "path: tokenid (int)",
+ "purpose": "Revoke (deactivate) a token. Own token, or any if admin.",
+ "example": "curl -X DELETE http://localhost:5001/api/apitokens/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/apitokens/",
+ "auth": "permission:apitokens.create",
+ "params": "path: tokenid (int); body: JSON (fields not yet documented)",
+ "purpose": "Rename, rescope, or deactivate a token. Own token, or any if admin.",
+ "example": "curl -X PUT http://localhost:5001/api/apitokens/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-applications",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/applications",
+ "auth": "permission:applications.view",
+ "params": "not documented",
+ "purpose": "List all applications.",
+ "example": "curl http://localhost:5001/api/applications -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/applications",
+ "auth": "permission:applications.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new application.",
+ "example": "curl -X POST http://localhost:5001/api/applications -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/applications/",
+ "auth": "permission:applications.delete",
+ "params": "path: app_id (int)",
+ "purpose": "Permanently delete an application, its versions, and its uploads.",
+ "example": "curl -X DELETE http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/applications/",
+ "auth": "permission:applications.view",
+ "params": "path: app_id (int)",
+ "purpose": "Get a single application with details.",
+ "example": "curl http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/applications/",
+ "auth": "permission:applications.edit",
+ "params": "path: app_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update an application.",
+ "example": "curl -X PUT http://localhost:5001/api/applications/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/applications//image",
+ "auth": "permission:applications.edit",
+ "params": "path: app_id (int)",
+ "purpose": "Remove an application's image and clear the field.",
+ "example": "curl -X DELETE http://localhost:5001/api/applications/1/image -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/applications//image",
+ "auth": "permission:applications.edit",
+ "params": "path: app_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Upload (or replace) an application's image.",
+ "example": "curl -X POST http://localhost:5001/api/applications/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/applications//installed",
+ "auth": "permission:applications.view",
+ "params": "path: app_id (int)",
+ "purpose": "List all computers that have this application installed.",
+ "example": "curl http://localhost:5001/api/applications/1/installed -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/applications//package",
+ "auth": "permission:applications.edit",
+ "params": "path: app_id (int)",
+ "purpose": "Remove an uploaded installer and clear the path it set.",
+ "example": "curl -X DELETE http://localhost:5001/api/applications/1/package -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/applications//package",
+ "auth": "permission:applications.edit",
+ "params": "path: app_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Upload (or replace) the installer for an application.",
+ "example": "curl -X POST http://localhost:5001/api/applications/1/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/applications//revert",
+ "auth": "permission:applications.edit",
+ "params": "path: app_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Strip an application back to a catalogue record.",
+ "example": "curl -X POST http://localhost:5001/api/applications/1/revert -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/applications//versions",
+ "auth": "permission:applications.view",
+ "params": "path: app_id (int)",
+ "purpose": "List all versions for an application.",
+ "example": "curl http://localhost:5001/api/applications/1/versions -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/applications//versions",
+ "auth": "permission:applications.create",
+ "params": "path: app_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Create a new version for an application.",
+ "example": "curl -X POST http://localhost:5001/api/applications/1/versions -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/applications/image/",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "path: filename (path)",
+ "purpose": "Serve an uploaded application image.",
+ "example": "curl http://localhost:5001/api/applications/image/example.png"
+ },
+ {
+ "method": "GET",
+ "path": "/api/applications/machines/",
+ "auth": "permission:applications.view",
+ "params": "path: machine_id (int)",
+ "purpose": "List all applications installed on a computer.",
+ "example": "curl http://localhost:5001/api/applications/machines/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/applications/machines/",
+ "auth": "permission:applications.create",
+ "params": "path: machine_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Install an application on a computer.",
+ "example": "curl -X POST http://localhost:5001/api/applications/machines/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/applications/machines//",
+ "auth": "permission:applications.delete",
+ "params": "path: machine_id (int), app_id (int)",
+ "purpose": "Uninstall an application from a computer.",
+ "example": "curl -X DELETE http://localhost:5001/api/applications/machines/1/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/applications/machines//",
+ "auth": "permission:applications.edit",
+ "params": "path: machine_id (int), app_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update installed application (e.g., change version).",
+ "example": "curl -X PUT http://localhost:5001/api/applications/machines/1/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/applications/package/",
+ "auth": "permission:applications.download",
+ "params": "path: filename (path)",
+ "purpose": "Download an uploaded installer.",
+ "example": "curl http://localhost:5001/api/applications/package/example.png -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "core-assets",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/assets",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List all assets with filtering and pagination.",
+ "example": "curl http://localhost:5001/api/assets -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/assets",
+ "auth": "permission:assets.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new asset.",
+ "example": "curl -X POST http://localhost:5001/api/assets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/assets/",
+ "auth": "permission:assets.delete",
+ "params": "path: asset_id (int)",
+ "purpose": "Delete (soft delete) an asset.",
+ "example": "curl -X DELETE http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/assets/",
+ "auth": "permission:assets.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Get a single asset with full details.",
+ "example": "curl http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/assets/",
+ "auth": "permission:assets.edit",
+ "params": "path: asset_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update an asset.",
+ "example": "curl -X PUT http://localhost:5001/api/assets/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/assets//communications",
+ "auth": "permission:assets.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Get all communications for an asset.",
+ "example": "curl http://localhost:5001/api/assets/1/communications -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/assets//relationships",
+ "auth": "permission:assets.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Get all relationships for an asset.",
+ "example": "curl http://localhost:5001/api/assets/1/relationships -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/assets/lookup/",
+ "auth": "permission:assets.view",
+ "params": "path: assetnumber (string)",
+ "purpose": "Look up an asset by its asset number.",
+ "example": "curl http://localhost:5001/api/assets/lookup/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/assets/map",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Get all assets with map positions for unified floor map display.",
+ "example": "curl http://localhost:5001/api/assets/map -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/assets/relationships",
+ "auth": "permission:assets.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a relationship between two assets.",
+ "example": "curl -X POST http://localhost:5001/api/assets/relationships -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/assets/relationships/",
+ "auth": "permission:assets.delete",
+ "params": "path: rel_id (int)",
+ "purpose": "Delete an asset relationship.",
+ "example": "curl -X DELETE http://localhost:5001/api/assets/relationships/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/assets/relationshiptypes",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List all asset relationship types.",
+ "example": "curl http://localhost:5001/api/assets/relationshiptypes -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/assets/relationshiptypes",
+ "auth": "permission:assets.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new asset relationship type.",
+ "example": "curl -X POST http://localhost:5001/api/assets/relationshiptypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/assets/relationshiptypes/",
+ "auth": "permission:assets.delete",
+ "params": "path: type_id (int)",
+ "purpose": "Delete a relationship type. Refused if any relationship still uses it.",
+ "example": "curl -X DELETE http://localhost:5001/api/assets/relationshiptypes/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/assets/relationshiptypes/",
+ "auth": "permission:assets.edit",
+ "params": "path: type_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a relationship type.",
+ "example": "curl -X PUT http://localhost:5001/api/assets/relationshiptypes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/assets/statuses",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List all asset statuses.",
+ "example": "curl http://localhost:5001/api/assets/statuses -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/assets/statuses",
+ "auth": "permission:assets.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new asset status.",
+ "example": "curl -X POST http://localhost:5001/api/assets/statuses -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/assets/statuses/",
+ "auth": "permission:assets.delete",
+ "params": "path: status_id (int)",
+ "purpose": "Delete an asset status. Refused if any asset still uses it.",
+ "example": "curl -X DELETE http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/assets/statuses/",
+ "auth": "permission:assets.view",
+ "params": "path: status_id (int)",
+ "purpose": "Get a single asset status.",
+ "example": "curl http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/assets/statuses/",
+ "auth": "permission:assets.edit",
+ "params": "path: status_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update an asset status.",
+ "example": "curl -X PUT http://localhost:5001/api/assets/statuses/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/assets/types",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List all asset types.",
+ "example": "curl http://localhost:5001/api/assets/types -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/assets/types",
+ "auth": "permission:assets.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new asset type.",
+ "example": "curl -X POST http://localhost:5001/api/assets/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/assets/types/",
+ "auth": "permission:assets.view",
+ "params": "path: type_id (int)",
+ "purpose": "Get a single asset type.",
+ "example": "curl http://localhost:5001/api/assets/types/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/assets/types/",
+ "auth": "permission:assets.edit",
+ "params": "path: type_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update an asset type's display fields (color/icon/description). The name, plugin, and table are structural and not editable here.",
+ "example": "curl -X PUT http://localhost:5001/api/assets/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-auditlogs",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/auditlogs",
+ "auth": "permission:audit.view",
+ "params": "not documented",
+ "purpose": "List audit logs with filtering and pagination.",
+ "example": "curl http://localhost:5001/api/auditlogs -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/auditlogs/entity//",
+ "auth": "permission:audit.view",
+ "params": "path: entitytype (string), entityid (int)",
+ "purpose": "Get audit history for a specific entity.",
+ "example": "curl http://localhost:5001/api/auditlogs/entity/x/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/auditlogs/purge",
+ "auth": "permission:audit.purge",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Delete entries older than the retention window.",
+ "example": "curl -X POST http://localhost:5001/api/auditlogs/purge -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/auditlogs/retention",
+ "auth": "permission:audit.view",
+ "params": "not documented",
+ "purpose": "Size of the log and what the configured retention would remove.",
+ "example": "curl http://localhost:5001/api/auditlogs/retention -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/auditlogs/stats",
+ "auth": "permission:audit.view",
+ "params": "not documented",
+ "purpose": "Get audit log statistics.",
+ "example": "curl http://localhost:5001/api/auditlogs/stats -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "core-auth",
+ "endpoints": [
+ {
+ "method": "POST",
+ "path": "/api/auth/change-password",
+ "auth": "jwt",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Change the authenticated user's own password.",
+ "example": "curl -X POST http://localhost:5001/api/auth/change-password -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/auth/login",
+ "auth": "none (the login surface itself; rate limited, 5-strike lockout)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Authenticate user and return JWT tokens.",
+ "example": "curl -X POST http://localhost:5001/api/auth/login -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/auth/logout",
+ "auth": "jwt",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Logout user (for frontend token cleanup).",
+ "example": "curl -X POST http://localhost:5001/api/auth/logout -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/auth/me",
+ "auth": "jwt",
+ "params": "not documented",
+ "purpose": "Get current authenticated user info.",
+ "example": "curl http://localhost:5001/api/auth/me -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/auth/refresh",
+ "auth": "jwt (refresh token)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Refresh access token using refresh token.",
+ "example": "curl -X POST http://localhost:5001/api/auth/refresh -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-businessunits",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/businessunits",
+ "auth": "jwt-optional (works signed out; the handler decides what shows)",
+ "params": "not documented",
+ "purpose": "List all business units.",
+ "example": "curl http://localhost:5001/api/businessunits -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/businessunits",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new business unit.",
+ "example": "curl -X POST http://localhost:5001/api/businessunits -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/businessunits/",
+ "auth": "jwt + role:admin",
+ "params": "path: bu_id (int)",
+ "purpose": "Delete (deactivate) a business unit.",
+ "example": "curl -X DELETE http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/businessunits/",
+ "auth": "permission:assets.view",
+ "params": "path: bu_id (int)",
+ "purpose": "Get a single business unit.",
+ "example": "curl http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/businessunits/",
+ "auth": "jwt + role:admin",
+ "params": "path: bu_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a business unit.",
+ "example": "curl -X PUT http://localhost:5001/api/businessunits/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-collector",
+ "endpoints": [
+ {
+ "method": "POST",
+ "path": "/api/collector/",
+ "auth": "api-key (per-plugin collector token)",
+ "params": "path: pluginname (string); body: JSON (fields not yet documented)",
+ "purpose": "Generic collector ingest for any plugin (ADR-006).",
+ "example": "curl -X POST http://localhost:5001/api/collector/x -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/collector/_schemas",
+ "auth": "jwt",
+ "params": "not documented",
+ "purpose": "List collector schemas for all enabled plugins (JWT-protected).",
+ "example": "curl http://localhost:5001/api/collector/_schemas -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/collector/apps",
+ "auth": "api-key (collector token, legacy shared key)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Update installed applications for a PC (matched by hostname).",
+ "example": "curl -X POST http://localhost:5001/api/collector/apps -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/collector/bulk",
+ "auth": "api-key (collector token, legacy shared key)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Bulk update multiple PCs at once.",
+ "example": "curl -X POST http://localhost:5001/api/collector/bulk -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/collector/heartbeat",
+ "auth": "api-key (collector token, legacy shared key)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Record PC online status / heartbeat (single or batch).",
+ "example": "curl -X POST http://localhost:5001/api/collector/heartbeat -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/collector/pc",
+ "auth": "api-key (collector token, legacy shared key)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Update one PC from the collection script (matched by hostname).",
+ "example": "curl -X POST http://localhost:5001/api/collector/pc -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/collector/status",
+ "auth": "api-key (per-plugin collector token)",
+ "params": "not documented",
+ "purpose": "Check collector API status.",
+ "example": "curl http://localhost:5001/api/collector/status -H \"X-API-Key: $KEY\""
+ }
+ ]
+ },
+ {
+ "surface": "core-customfields",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/customfields",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List custom-field definitions. Filter by ?assettypeid=. ?active=false includes inactive ones.",
+ "example": "curl http://localhost:5001/api/customfields -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/customfields",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: customfields.create_field has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/customfields -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/customfields/",
+ "auth": "jwt + role:admin",
+ "params": "path: fieldid (int)",
+ "purpose": "UNDOCUMENTED: customfields.delete_field has no docstring.",
+ "example": "curl -X DELETE http://localhost:5001/api/customfields/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/customfields/",
+ "auth": "jwt + role:admin",
+ "params": "path: fieldid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: customfields.update_field has no docstring.",
+ "example": "curl -X PUT http://localhost:5001/api/customfields/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/customfields/asset/",
+ "auth": "permission:assets.view",
+ "params": "path: assetid (int)",
+ "purpose": "Active custom fields for an asset's type, merged with its values.",
+ "example": "curl http://localhost:5001/api/customfields/asset/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/customfields/asset/",
+ "auth": "jwt + role:admin",
+ "params": "path: assetid (int); body: JSON (fields not yet documented)",
+ "purpose": "Upsert values for an asset. Body: {values: {fieldid: value, ...}}.",
+ "example": "curl -X PUT http://localhost:5001/api/customfields/asset/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-dashboard",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/dashboard",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Get dashboard summary data (asset-based).",
+ "example": "curl http://localhost:5001/api/dashboard -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/dashboard/capabilities",
+ "auth": "jwt-optional (works signed out; the handler decides what shows)",
+ "params": "not documented",
+ "purpose": "What THIS caller can reach, for chrome that would otherwise only 401.",
+ "example": "curl http://localhost:5001/api/dashboard/capabilities -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/dashboard/health",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "not documented",
+ "purpose": "Health check endpoint (no auth required).",
+ "example": "curl http://localhost:5001/api/dashboard/health"
+ },
+ {
+ "method": "GET",
+ "path": "/api/dashboard/navigation",
+ "auth": "jwt-optional (works signed out; the handler decides what shows)",
+ "params": "not documented",
+ "purpose": "Get navigation items from all loaded plugins.",
+ "example": "curl http://localhost:5001/api/dashboard/navigation -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/dashboard/stats",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Get detailed statistics by asset type.",
+ "example": "curl http://localhost:5001/api/dashboard/stats -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/dashboard/summary",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Get dashboard summary data (asset-based).",
+ "example": "curl http://localhost:5001/api/dashboard/summary -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/dashboard/widgets",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Aggregate dashboard widget definitions from all enabled plugins.",
+ "example": "curl http://localhost:5001/api/dashboard/widgets -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "core-dashboarddefaults",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/dashboarddefaults",
+ "auth": "permission:settings.view",
+ "params": "not documented",
+ "purpose": "List all visitor-IP -> business-unit mappings.",
+ "example": "curl http://localhost:5001/api/dashboarddefaults -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/dashboarddefaults",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a visitor-IP -> business-unit mapping.",
+ "example": "curl -X POST http://localhost:5001/api/dashboarddefaults -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/dashboarddefaults/",
+ "auth": "jwt + role:admin",
+ "params": "path: default_id (int)",
+ "purpose": "Delete (deactivate) a mapping.",
+ "example": "curl -X DELETE http://localhost:5001/api/dashboarddefaults/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/dashboarddefaults/",
+ "auth": "jwt + role:admin",
+ "params": "path: default_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a mapping.",
+ "example": "curl -X PUT http://localhost:5001/api/dashboarddefaults/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/dashboarddefaults/display-role",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "not documented",
+ "purpose": "Resolve what a single 'display' PC should show, from its own IP.",
+ "example": "curl http://localhost:5001/api/dashboarddefaults/display-role"
+ },
+ {
+ "method": "GET",
+ "path": "/api/dashboarddefaults/visitor-location",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "not documented",
+ "purpose": "Resolve the business unit for the calling display by its IP.",
+ "example": "curl http://localhost:5001/api/dashboarddefaults/visitor-location"
+ }
+ ]
+ },
+ {
+ "surface": "core-docs",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/docs/",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "not documented",
+ "purpose": "Redoc page. url_for keeps the asset URLs correct under any mount.",
+ "example": "curl http://localhost:5001/api/docs/"
+ },
+ {
+ "method": "GET",
+ "path": "/api/docs/llms.txt",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "not documented",
+ "purpose": "Concise LLM-oriented API guide.",
+ "example": "curl http://localhost:5001/api/docs/llms.txt"
+ },
+ {
+ "method": "GET",
+ "path": "/api/docs/openapi.json",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "not documented",
+ "purpose": "The generated OpenAPI 3.1 spec (machine + LLM readable).",
+ "example": "curl http://localhost:5001/api/docs/openapi.json"
+ },
+ {
+ "method": "GET",
+ "path": "/api/docs/redoc.standalone.js",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "not documented",
+ "purpose": "Vendored Redoc bundle (offline).",
+ "example": "curl http://localhost:5001/api/docs/redoc.standalone.js"
+ }
+ ]
+ },
+ {
+ "surface": "core-locations",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/locations",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List all locations.",
+ "example": "curl http://localhost:5001/api/locations -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/locations",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new location.",
+ "example": "curl -X POST http://localhost:5001/api/locations -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/locations/",
+ "auth": "jwt + role:admin",
+ "params": "path: location_id (int)",
+ "purpose": "Delete (deactivate) a location.",
+ "example": "curl -X DELETE http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/locations/",
+ "auth": "permission:assets.view",
+ "params": "path: location_id (int)",
+ "purpose": "Get a single location.",
+ "example": "curl http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/locations/",
+ "auth": "jwt + role:admin",
+ "params": "path: location_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a location.",
+ "example": "curl -X PUT http://localhost:5001/api/locations/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/locations/types",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List location types. ?active=false includes inactive ones.",
+ "example": "curl http://localhost:5001/api/locations/types -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/locations/types",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: locations.create_location_type has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/locations/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/locations/types/",
+ "auth": "jwt + role:admin",
+ "params": "path: type_id (int)",
+ "purpose": "UNDOCUMENTED: locations.delete_location_type has no docstring.",
+ "example": "curl -X DELETE http://localhost:5001/api/locations/types/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/locations/types/",
+ "auth": "jwt + role:admin",
+ "params": "path: type_id (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: locations.update_location_type has no docstring.",
+ "example": "curl -X PUT http://localhost:5001/api/locations/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-maplevels",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/maplevels",
+ "auth": "jwt-optional (works signed out; the handler decides what shows)",
+ "params": "not documented",
+ "purpose": "Every active level, grouped by building, in display order.",
+ "example": "curl http://localhost:5001/api/maplevels -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/maplevels",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: maplevels.create_level has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/maplevels -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/maplevels/",
+ "auth": "jwt + role:admin",
+ "params": "path: levelid (int)",
+ "purpose": "Deactivate a level, refusing while assets are still placed on it.",
+ "example": "curl -X DELETE http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/maplevels/",
+ "auth": "jwt-optional (works signed out; the handler decides what shows)",
+ "params": "path: levelid (int)",
+ "purpose": "UNDOCUMENTED: maplevels.get_level has no docstring.",
+ "example": "curl http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PATCH|PUT",
+ "path": "/api/maplevels/",
+ "auth": "jwt + role:admin",
+ "params": "path: levelid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: maplevels.update_level has no docstring.",
+ "example": "curl -X PATCH http://localhost:5001/api/maplevels/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/maplevels//blueprint",
+ "auth": "jwt + role:admin",
+ "params": "path: levelid (int); body: JSON (fields not yet documented)",
+ "purpose": "Upload this level's blueprint for one theme.",
+ "example": "curl -X POST http://localhost:5001/api/maplevels/1/blueprint -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/maplevels//blueprint/",
+ "auth": "jwt-optional (works signed out; the handler decides what shows)",
+ "params": "path: levelid (int), filename (path)",
+ "purpose": "Serve a level's blueprint image. Public, for the same reason as above.",
+ "example": "curl http://localhost:5001/api/maplevels/1/blueprint/example.png -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/maplevels/buildings",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: maplevels.create_building has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/maplevels/buildings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "PATCH|PUT",
+ "path": "/api/maplevels/buildings/",
+ "auth": "jwt + role:admin",
+ "params": "path: buildingid (int); body: JSON (fields not yet documented)",
+ "purpose": "Rename or reorder a building. Levels move with it; nothing repositions.",
+ "example": "curl -X PATCH http://localhost:5001/api/maplevels/buildings/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-mappositions",
+ "endpoints": [
+ {
+ "method": "POST",
+ "path": "/api/mappositions/positions",
+ "auth": "permission:assets.edit",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Set positions for many assets at once.",
+ "example": "curl -X POST http://localhost:5001/api/mappositions/positions -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/mappositions/snapshots",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Snapshots, newest first. Metadata only - the positions are large.",
+ "example": "curl http://localhost:5001/api/mappositions/snapshots -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/mappositions/snapshots//restore",
+ "auth": "permission:assets.edit",
+ "params": "path: snapshotid (int); body: JSON (fields not yet documented)",
+ "purpose": "Put the positions in this snapshot back.",
+ "example": "curl -X POST http://localhost:5001/api/mappositions/snapshots/1/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/mappositions/transform",
+ "auth": "permission:assets.edit",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Move every placed marker on a level by a transform read off landmarks.",
+ "example": "curl -X POST http://localhost:5001/api/mappositions/transform -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/mappositions/verify",
+ "auth": "permission:assets.edit",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Mark markers as reviewed against the current drawing without moving them.",
+ "example": "curl -X POST http://localhost:5001/api/mappositions/verify -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-models",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/models",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List all vendor catalog models.",
+ "example": "curl http://localhost:5001/api/models -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/models",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new model.",
+ "example": "curl -X POST http://localhost:5001/api/models -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/models/",
+ "auth": "jwt + role:admin",
+ "params": "path: model_id (int)",
+ "purpose": "Delete (deactivate) a model.",
+ "example": "curl -X DELETE http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/models/",
+ "auth": "permission:assets.view",
+ "params": "path: model_id (int)",
+ "purpose": "Get a single model.",
+ "example": "curl http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/models/",
+ "auth": "jwt + role:admin",
+ "params": "path: model_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a model.",
+ "example": "curl -X PUT http://localhost:5001/api/models/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/models//image",
+ "auth": "jwt + role:admin",
+ "params": "path: model_id (int)",
+ "purpose": "Clear a model image and delete the uploaded file if we own it.",
+ "example": "curl -X DELETE http://localhost:5001/api/models/1/image -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/models//image",
+ "auth": "jwt + role:admin",
+ "params": "path: model_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Upload (or replace) the photo for a model.",
+ "example": "curl -X POST http://localhost:5001/api/models/1/image -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/models/image/",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "path: filename (path)",
+ "purpose": "Serve an uploaded model image (public - asset detail pages read it).",
+ "example": "curl http://localhost:5001/api/models/image/example.png"
+ }
+ ]
+ },
+ {
+ "surface": "core-modeltypes",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/modeltypes",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List all model types with optional filtering.",
+ "example": "curl http://localhost:5001/api/modeltypes -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/modeltypes",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new model type.",
+ "example": "curl -X POST http://localhost:5001/api/modeltypes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/modeltypes/",
+ "auth": "jwt + role:admin",
+ "params": "path: type_id (int)",
+ "purpose": "Delete (deactivate) a model type.",
+ "example": "curl -X DELETE http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/modeltypes/",
+ "auth": "permission:assets.view",
+ "params": "path: type_id (int)",
+ "purpose": "Get a single model type.",
+ "example": "curl http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/modeltypes/",
+ "auth": "jwt + role:admin",
+ "params": "path: type_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a model type.",
+ "example": "curl -X PUT http://localhost:5001/api/modeltypes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-operatingsystems",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/operatingsystems",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List all operating systems.",
+ "example": "curl http://localhost:5001/api/operatingsystems -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/operatingsystems",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new operating system.",
+ "example": "curl -X POST http://localhost:5001/api/operatingsystems -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/operatingsystems/",
+ "auth": "jwt + role:admin",
+ "params": "path: os_id (int)",
+ "purpose": "Delete (deactivate) an operating system.",
+ "example": "curl -X DELETE http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/operatingsystems/",
+ "auth": "permission:assets.view",
+ "params": "path: os_id (int)",
+ "purpose": "Get a single operating system.",
+ "example": "curl http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/operatingsystems/",
+ "auth": "jwt + role:admin",
+ "params": "path: os_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update an operating system.",
+ "example": "curl -X PUT http://localhost:5001/api/operatingsystems/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-plugins",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/plugins",
+ "auth": "permission:settings.view",
+ "params": "not documented",
+ "purpose": "List all discovered plugins (enabled or not) with their metadata and the framework contract version.",
+ "example": "curl http://localhost:5001/api/plugins -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/plugins/",
+ "auth": "jwt + role:admin",
+ "params": "path: name (string); body: JSON (fields not yet documented)",
+ "purpose": "Enable or disable a plugin. Takes effect on the next app restart for route/navigation changes.",
+ "example": "curl -X PUT http://localhost:5001/api/plugins/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/plugins/enabled",
+ "auth": "jwt-optional (works signed out; the handler decides what shows)",
+ "params": "not documented",
+ "purpose": "Return just the names of enabled plugins as a flat array.",
+ "example": "curl http://localhost:5001/api/plugins/enabled -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/plugins/settings-routes",
+ "auth": "permission:settings.view",
+ "params": "not documented",
+ "purpose": "Return settings routes contributed by all enabled plugins.",
+ "example": "curl http://localhost:5001/api/plugins/settings-routes -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "core-pluginui",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/pluginui/asset-panels",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Return the asset-detail panels matching one asset's type (get_asset_panels).",
+ "example": "curl http://localhost:5001/api/pluginui/asset-panels -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/pluginui/asset-presentation",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Merge enabled plugins' asset-type presentation entries (hook).",
+ "example": "curl http://localhost:5001/api/pluginui/asset-presentation -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/pluginui/asset-type-descriptors",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Merge enabled plugins' asset-type subtype descriptors.",
+ "example": "curl http://localhost:5001/api/pluginui/asset-type-descriptors -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/pluginui/core-type-descriptors",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Return type descriptors for core-owned non-asset types.",
+ "example": "curl http://localhost:5001/api/pluginui/core-type-descriptors -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/pluginui/map-overlays",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Merge enabled plugins' map overlay declarations (get_map_overlays hook).",
+ "example": "curl http://localhost:5001/api/pluginui/map-overlays -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/pluginui/settings-cards",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Merge enabled plugins' settings-catalog cards (get_settings_cards hook).",
+ "example": "curl http://localhost:5001/api/pluginui/settings-cards -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "core-reports",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/reports",
+ "auth": "permission:reports.view",
+ "params": "not documented",
+ "purpose": "List all available reports.",
+ "example": "curl http://localhost:5001/api/reports -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/reports/asset-inventory",
+ "auth": "permission:reports.view",
+ "params": "not documented",
+ "purpose": "Report: Complete asset inventory summary.",
+ "example": "curl http://localhost:5001/api/reports/asset-inventory -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/reports/assets-by-status",
+ "auth": "permission:reports.view",
+ "params": "not documented",
+ "purpose": "Report: Asset count grouped by status.",
+ "example": "curl http://localhost:5001/api/reports/assets-by-status -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/reports/email",
+ "auth": "permission:reports.export",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Email a report's data as an HTML table (on-demand delivery).",
+ "example": "curl -X POST http://localhost:5001/api/reports/email -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/reports/kb-popularity",
+ "auth": "permission:reports.view",
+ "params": "not documented",
+ "purpose": "Report: Most clicked knowledge base articles.",
+ "example": "curl http://localhost:5001/api/reports/kb-popularity -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/reports/machines-by-type",
+ "auth": "permission:reports.view",
+ "params": "not documented",
+ "purpose": "Report: Machine count grouped by machine type.",
+ "example": "curl http://localhost:5001/api/reports/machines-by-type -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/reports/pc-relationships",
+ "auth": "permission:reports.view",
+ "params": "not documented",
+ "purpose": "Report: PCs with relationships to shop floor machines.",
+ "example": "curl http://localhost:5001/api/reports/pc-relationships -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/reports/software-compliance",
+ "auth": "permission:reports.view",
+ "params": "not documented",
+ "purpose": "Report: Required applications vs installed (per PC).",
+ "example": "curl http://localhost:5001/api/reports/software-compliance -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "core-scripts",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/scripts",
+ "auth": "permission:endpoints.runscript",
+ "params": "not documented",
+ "purpose": "List scripts (active by default).",
+ "example": "curl http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/scripts",
+ "auth": "permission:endpoints.runscript",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a script catalog entry (body uploaded separately).",
+ "example": "curl -X POST http://localhost:5001/api/scripts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/scripts/",
+ "auth": "permission:endpoints.runscript",
+ "params": "path: script_id (int)",
+ "purpose": "UNDOCUMENTED: scripts.delete_script has no docstring.",
+ "example": "curl -X DELETE http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/scripts/",
+ "auth": "permission:endpoints.runscript",
+ "params": "path: script_id (int)",
+ "purpose": "UNDOCUMENTED: scripts.get_script has no docstring.",
+ "example": "curl http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/scripts/",
+ "auth": "permission:endpoints.runscript",
+ "params": "path: script_id (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: scripts.update_script has no docstring.",
+ "example": "curl -X PUT http://localhost:5001/api/scripts/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/scripts//body",
+ "auth": "permission:endpoints.runscript",
+ "params": "path: script_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Upload (or replace) the .ps1 body for a script.",
+ "example": "curl -X POST http://localhost:5001/api/scripts/1/body -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/scripts/body/",
+ "auth": "permission:endpoints.runscript",
+ "params": "path: filename (path)",
+ "purpose": "Operator-facing script body download (JWT/RBAC).",
+ "example": "curl http://localhost:5001/api/scripts/body/example.png -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "core-search",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/search",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "Global search across multiple entity types.",
+ "example": "curl http://localhost:5001/api/search -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/search/suggest",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "A better spelling for a query that found nothing, or null.",
+ "example": "curl http://localhost:5001/api/search/suggest -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "core-settings",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/settings",
+ "auth": "jwt-optional (works signed out; the handler decides what shows)",
+ "params": "not documented",
+ "purpose": "List all settings, optionally filtered by category.",
+ "example": "curl http://localhost:5001/api/settings -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/settings",
+ "auth": "permission:settings.edit",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new setting (admin only).",
+ "example": "curl -X POST http://localhost:5001/api/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/settings/",
+ "auth": "jwt-optional (works signed out; the handler decides what shows)",
+ "params": "path: key (string)",
+ "purpose": "Get a single setting by key.",
+ "example": "curl http://localhost:5001/api/settings/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/settings/",
+ "auth": "permission:settings.edit",
+ "params": "path: key (string); body: JSON (fields not yet documented)",
+ "purpose": "Update a setting value.",
+ "example": "curl -X PUT http://localhost:5001/api/settings/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/settings/asset-import/import",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Validate uploaded CSVs and, when commit=true, load them.",
+ "example": "curl -X POST http://localhost:5001/api/settings/asset-import/import -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/settings/asset-import/script",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "Download Collect-ShopDB.ps1 with this site's CSV path embedded.",
+ "example": "curl http://localhost:5001/api/settings/asset-import/script -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/settings/asset-import/templates",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "List the importable CSV tables in the order they must be uploaded.",
+ "example": "curl http://localhost:5001/api/settings/asset-import/templates -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/settings/asset-import/templates/",
+ "auth": "jwt + role:admin",
+ "params": "path: tablename (string)",
+ "purpose": "Download a generated CSV template for a single importable table.",
+ "example": "curl http://localhost:5001/api/settings/asset-import/templates/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/settings/asset-import/winc/batches",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "List applied/undone WINC import batches, newest first (for the undo UI).",
+ "example": "curl http://localhost:5001/api/settings/asset-import/winc/batches -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/settings/asset-import/winc/commit",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Apply a WINC plan reversibly. Requires acknowledgment for needs-create.",
+ "example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/commit -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/settings/asset-import/winc/preview",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Read-only WINC analysis: report what a commit would do. Writes nothing.",
+ "example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/preview -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/settings/asset-import/winc/undo",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Reverse an applied WINC import batch by id. Conservative and reversible.",
+ "example": "curl -X POST http://localhost:5001/api/settings/asset-import/winc/undo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/settings/branding-logo",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Upload a branding logo image and point the matching setting at it.",
+ "example": "curl -X POST http://localhost:5001/api/settings/branding-logo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/settings/branding/",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "path: filename (path)",
+ "purpose": "Serve an uploaded branding logo (public - kiosks/print pages read it).",
+ "example": "curl http://localhost:5001/api/settings/branding/example.png"
+ },
+ {
+ "method": "POST",
+ "path": "/api/settings/map-blueprint",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Upload a floor-map blueprint image and point the setting at it.",
+ "example": "curl -X POST http://localhost:5001/api/settings/map-blueprint -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/settings/map-blueprint/",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "path: filename (path)",
+ "purpose": "Serve an uploaded blueprint image (public - kiosks read it).",
+ "example": "curl http://localhost:5001/api/settings/map-blueprint/example.png"
+ },
+ {
+ "method": "POST",
+ "path": "/api/settings/seed",
+ "auth": "permission:settings.edit",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Seed default settings if they don't exist.",
+ "example": "curl -X POST http://localhost:5001/api/settings/seed -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/settings/test-email",
+ "auth": "permission:settings.edit",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Send a test email to verify SMTP configuration.",
+ "example": "curl -X POST http://localhost:5001/api/settings/test-email -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-setup",
+ "endpoints": [
+ {
+ "method": "POST",
+ "path": "/api/setup/complete",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Flag the first-run setup as finished.",
+ "example": "curl -X POST http://localhost:5001/api/setup/complete -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/setup/create-admin",
+ "auth": "none (first run only: refused once an admin exists)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create the first admin. Only works while there are zero users, setup has not been marked complete, and the caller proves operator control (setup token or a loopback request).",
+ "example": "curl -X POST http://localhost:5001/api/setup/create-admin -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/setup/needs-admin",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "not documented",
+ "purpose": "True when no users exist yet - the app's very first run. Public so the login screen can offer to create the first admin.",
+ "example": "curl http://localhost:5001/api/setup/needs-admin"
+ },
+ {
+ "method": "POST",
+ "path": "/api/setup/seed-reference",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Seed core reference data + permissions + default settings. Idempotent - reuses the same routines as the `flask seed ...` CLI commands.",
+ "example": "curl -X POST http://localhost:5001/api/setup/seed-reference -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/setup/seed-starter",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Add common vendors that are not already present. Idempotent.",
+ "example": "curl -X POST http://localhost:5001/api/setup/seed-starter -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-shopdbbackups",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/shopdbbackups",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "List every retained backup set, newest first, with a compatibility flag.",
+ "example": "curl http://localhost:5001/api/shopdbbackups -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/shopdbbackups",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Kick off an on-demand backup run.",
+ "example": "curl -X POST http://localhost:5001/api/shopdbbackups -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/shopdbbackups/",
+ "auth": "jwt + role:admin",
+ "params": "path: snapshotid (string)",
+ "purpose": "Return one set's full manifest plus a resolved compatibility verdict.",
+ "example": "curl http://localhost:5001/api/shopdbbackups/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/shopdbbackups//restore",
+ "auth": "jwt + role:admin",
+ "params": "path: snapshotid (string); body: JSON (fields not yet documented)",
+ "purpose": "Queue a restore of a set, gated on schema compatibility.",
+ "example": "curl -X POST http://localhost:5001/api/shopdbbackups/x/restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/shopdbbackups/finalize-restore",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Run the post-restore schema upgrade in the app context.",
+ "example": "curl -X POST http://localhost:5001/api/shopdbbackups/finalize-restore -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/shopdbbackups/history",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "Return the recorded history of backup and restore runs, newest first.",
+ "example": "curl http://localhost:5001/api/shopdbbackups/history -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/shopdbbackups/settings",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "Return destination + retention config for the settings pane.",
+ "example": "curl http://localhost:5001/api/shopdbbackups/settings -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/shopdbbackups/settings",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Persist the backup destination kind and its credential/config fields.",
+ "example": "curl -X PUT http://localhost:5001/api/shopdbbackups/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/shopdbbackups/status",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "Return the live status of the most recent backup and restore runs.",
+ "example": "curl http://localhost:5001/api/shopdbbackups/status -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "core-supportteams",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/supportteams",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List support teams (with contacts). ?active and ?teamname filters.",
+ "example": "curl http://localhost:5001/api/supportteams -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/supportteams",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new support team.",
+ "example": "curl -X POST http://localhost:5001/api/supportteams -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/supportteams/",
+ "auth": "jwt + role:admin",
+ "params": "path: team_id (int)",
+ "purpose": "Delete a support team; 409 while any application references it.",
+ "example": "curl -X DELETE http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/supportteams/",
+ "auth": "permission:assets.view",
+ "params": "path: team_id (int)",
+ "purpose": "Get a single support team with its contacts.",
+ "example": "curl http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/supportteams/",
+ "auth": "jwt + role:admin",
+ "params": "path: team_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a support team.",
+ "example": "curl -X PUT http://localhost:5001/api/supportteams/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/supportteams//contacts",
+ "auth": "jwt + role:admin",
+ "params": "path: team_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Add a contact to a support team.",
+ "example": "curl -X POST http://localhost:5001/api/supportteams/1/contacts -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/supportteams//contacts/",
+ "auth": "jwt + role:admin",
+ "params": "path: team_id (int), contact_id (int)",
+ "purpose": "Delete a support-team contact.",
+ "example": "curl -X DELETE http://localhost:5001/api/supportteams/1/contacts/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/supportteams//contacts/",
+ "auth": "jwt + role:admin",
+ "params": "path: team_id (int), contact_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a support-team contact.",
+ "example": "curl -X PUT http://localhost:5001/api/supportteams/1/contacts/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-users",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/users",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "List all users.",
+ "example": "curl http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/users",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new user.",
+ "example": "curl -X POST http://localhost:5001/api/users -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/users/",
+ "auth": "jwt + role:admin",
+ "params": "path: userid (int)",
+ "purpose": "Delete a user.",
+ "example": "curl -X DELETE http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/users/",
+ "auth": "jwt",
+ "params": "path: userid (int)",
+ "purpose": "Get a single user.",
+ "example": "curl http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/users/",
+ "auth": "jwt",
+ "params": "path: userid (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a user.",
+ "example": "curl -X PUT http://localhost:5001/api/users/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/users/permissions",
+ "auth": "jwt",
+ "params": "not documented",
+ "purpose": "List assignable permissions grouped by category.",
+ "example": "curl http://localhost:5001/api/users/permissions -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/users/roles",
+ "auth": "jwt",
+ "params": "not documented",
+ "purpose": "List all roles with their permissions.",
+ "example": "curl http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/users/roles",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new role.",
+ "example": "curl -X POST http://localhost:5001/api/users/roles -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/users/roles/",
+ "auth": "jwt + role:admin",
+ "params": "path: roleid (int)",
+ "purpose": "Delete a role.",
+ "example": "curl -X DELETE http://localhost:5001/api/users/roles/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/users/roles/",
+ "auth": "jwt + role:admin",
+ "params": "path: roleid (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a role.",
+ "example": "curl -X PUT http://localhost:5001/api/users/roles/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "core-vendors",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/vendors",
+ "auth": "permission:assets.view",
+ "params": "not documented",
+ "purpose": "List all vendors.",
+ "example": "curl http://localhost:5001/api/vendors -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/vendors",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new vendor.",
+ "example": "curl -X POST http://localhost:5001/api/vendors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/vendors/",
+ "auth": "jwt + role:admin",
+ "params": "path: vendor_id (int)",
+ "purpose": "Delete (deactivate) a vendor.",
+ "example": "curl -X DELETE http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/vendors/",
+ "auth": "permission:assets.view",
+ "params": "path: vendor_id (int)",
+ "purpose": "Get a single vendor.",
+ "example": "curl http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/vendors/",
+ "auth": "jwt + role:admin",
+ "params": "path: vendor_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a vendor.",
+ "example": "curl -X PUT http://localhost:5001/api/vendors/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "plugin-backups",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/backups/asset/",
+ "auth": "permission:backups.view",
+ "params": "path: assetid (int)",
+ "purpose": "Revision history for one asset, newest first.",
+ "example": "curl http://localhost:5001/api/backups/asset/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/backups/asset//info",
+ "auth": "permission:backups.view",
+ "params": "path: assetid (int)",
+ "purpose": "A kind's 'at a glance' card, built from its LATEST revision.",
+ "example": "curl http://localhost:5001/api/backups/asset/1/info -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/backups/dashboard/stale",
+ "auth": "permission:backups.view",
+ "params": "not documented",
+ "purpose": "Chains whose backup has stopped running.",
+ "example": "curl http://localhost:5001/api/backups/dashboard/stale -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/backups/kinds",
+ "auth": "permission:backups.view",
+ "params": "not documented",
+ "purpose": "Registered backup kinds, for UI that needs to enumerate them.",
+ "example": "curl http://localhost:5001/api/backups/kinds -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/backups/revisions/",
+ "auth": "permission:backups.view",
+ "params": "path: backuprevisionid (int)",
+ "purpose": "One revision, including its parsed payload where there is one.",
+ "example": "curl http://localhost:5001/api/backups/revisions/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/backups/revisions//diff",
+ "auth": "permission:backups.view",
+ "params": "path: backuprevisionid (int)",
+ "purpose": "Diff two revisions of the same asset+kind.",
+ "example": "curl http://localhost:5001/api/backups/revisions/1/diff -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/backups/revisions//download",
+ "auth": "permission:backups.download",
+ "params": "path: backuprevisionid (int)",
+ "purpose": "Render a revision back to its native file format.",
+ "example": "curl http://localhost:5001/api/backups/revisions/1/download -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "plugin-computers",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/computers",
+ "auth": "permission:computers.view",
+ "params": "not documented",
+ "purpose": "List all computers with filtering and pagination.",
+ "example": "curl http://localhost:5001/api/computers -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/computers",
+ "auth": "permission:computers.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create new computer (creates both Asset and Computer records).",
+ "example": "curl -X POST http://localhost:5001/api/computers -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/computers/",
+ "auth": "permission:computers.delete",
+ "params": "path: computer_id (int)",
+ "purpose": "Delete (soft delete) computer.",
+ "example": "curl -X DELETE http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/",
+ "auth": "permission:computers.view",
+ "params": "path: computer_id (int)",
+ "purpose": "Get a single computer with full details.",
+ "example": "curl http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/computers/",
+ "auth": "permission:computers.edit",
+ "params": "path: computer_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update computer (both Asset and Computer records).",
+ "example": "curl -X PUT http://localhost:5001/api/computers/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers//apps",
+ "auth": "permission:computers.view",
+ "params": "path: computer_id (int)",
+ "purpose": "Get all installed applications for a computer.",
+ "example": "curl http://localhost:5001/api/computers/1/apps -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/computers//apps",
+ "auth": "permission:computers.create",
+ "params": "path: computer_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Add an installed application to a computer.",
+ "example": "curl -X POST http://localhost:5001/api/computers/1/apps -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/computers//apps/",
+ "auth": "permission:computers.delete",
+ "params": "path: computer_id (int), app_id (int)",
+ "purpose": "Remove an installed application from a computer.",
+ "example": "curl -X DELETE http://localhost:5001/api/computers/1/apps/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/computers//report",
+ "auth": "permission:computers.create",
+ "params": "path: computer_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Report computer status (for agent-based reporting).",
+ "example": "curl -X POST http://localhost:5001/api/computers/1/report -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/asset//policypanel",
+ "auth": "permission:computers.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Keyvalue panel: which policy governs this endpoint, and what it allows.",
+ "example": "curl http://localhost:5001/api/computers/asset/1/policypanel -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/by-asset/",
+ "auth": "permission:computers.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Get computer data by asset ID.",
+ "example": "curl http://localhost:5001/api/computers/by-asset/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/by-hostname/",
+ "auth": "permission:computers.view",
+ "params": "path: hostname (string)",
+ "purpose": "Get computer by hostname.",
+ "example": "curl http://localhost:5001/api/computers/by-hostname/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/client-script",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "The collector reporter, stamped with THIS site's values.",
+ "example": "curl http://localhost:5001/api/computers/client-script -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/dashboard/quiet",
+ "auth": "permission:computers.view",
+ "params": "not documented",
+ "purpose": "PCs that WERE reporting, have stopped, and are still meant to be in use.",
+ "example": "curl http://localhost:5001/api/computers/dashboard/quiet -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/dashboard/sharedmachines",
+ "auth": "permission:computers.view",
+ "params": "not documented",
+ "purpose": "Machine numbers claimed by more than one PC, with nothing filed under them.",
+ "example": "curl http://localhost:5001/api/computers/dashboard/sharedmachines -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/dashboard/summary",
+ "auth": "permission:computers.view",
+ "params": "not documented",
+ "purpose": "Get computer dashboard summary data.",
+ "example": "curl http://localhost:5001/api/computers/dashboard/summary -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/display-kiosks",
+ "auth": "permission:computers.view",
+ "params": "not documented",
+ "purpose": "Reporting display kiosks, for the Dashboard Defaults picker.",
+ "example": "curl http://localhost:5001/api/computers/display-kiosks -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/policies/actions",
+ "auth": "permission:computers.view",
+ "params": "not documented",
+ "purpose": "The allowlisted action names + labels a policy may reference.",
+ "example": "curl http://localhost:5001/api/computers/policies/actions -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/policies/business",
+ "auth": "permission:computers.view",
+ "params": "not documented",
+ "purpose": "UNDOCUMENTED: computers.list_business_policies has no docstring.",
+ "example": "curl http://localhost:5001/api/computers/policies/business -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/computers/policies/business",
+ "auth": "permission:computers.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: computers.create_business_policy has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/computers/policies/business -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/computers/policies/business/",
+ "auth": "permission:computers.delete",
+ "params": "path: policy_id (int)",
+ "purpose": "UNDOCUMENTED: computers.delete_business_policy has no docstring.",
+ "example": "curl -X DELETE http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/policies/business/",
+ "auth": "permission:computers.view",
+ "params": "path: policy_id (int)",
+ "purpose": "UNDOCUMENTED: computers.get_business_policy has no docstring.",
+ "example": "curl http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/computers/policies/business/",
+ "auth": "permission:computers.edit",
+ "params": "path: policy_id (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: computers.update_business_policy has no docstring.",
+ "example": "curl -X PUT http://localhost:5001/api/computers/policies/business/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/policies/configuration",
+ "auth": "permission:computers.view",
+ "params": "not documented",
+ "purpose": "UNDOCUMENTED: computers.list_configuration_policies has no docstring.",
+ "example": "curl http://localhost:5001/api/computers/policies/configuration -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/computers/policies/configuration",
+ "auth": "permission:computers.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: computers.create_configuration_policy has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/computers/policies/configuration -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/computers/policies/configuration/",
+ "auth": "permission:computers.delete",
+ "params": "path: policy_id (int)",
+ "purpose": "UNDOCUMENTED: computers.delete_configuration_policy has no docstring.",
+ "example": "curl -X DELETE http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/policies/configuration/",
+ "auth": "permission:computers.view",
+ "params": "path: policy_id (int)",
+ "purpose": "UNDOCUMENTED: computers.get_configuration_policy has no docstring.",
+ "example": "curl http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/computers/policies/configuration/",
+ "auth": "permission:computers.edit",
+ "params": "path: policy_id (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: computers.update_configuration_policy has no docstring.",
+ "example": "curl -X PUT http://localhost:5001/api/computers/policies/configuration/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/policies/effective/",
+ "auth": "permission:computers.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Preview the resolved effective policy for one endpoint (debug/UI).",
+ "example": "curl http://localhost:5001/api/computers/policies/effective/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/protocols",
+ "auth": "permission:computers.view",
+ "params": "not documented",
+ "purpose": "List access protocols. ?active=false includes disabled ones.",
+ "example": "curl http://localhost:5001/api/computers/protocols -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/computers/protocols",
+ "auth": "permission:computers.edit",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: computers.create_protocol has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/computers/protocols -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/computers/protocols/",
+ "auth": "permission:computers.edit",
+ "params": "path: protocol_id (int)",
+ "purpose": "UNDOCUMENTED: computers.delete_protocol has no docstring.",
+ "example": "curl -X DELETE http://localhost:5001/api/computers/protocols/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PATCH|PUT",
+ "path": "/api/computers/protocols/",
+ "auth": "permission:computers.edit",
+ "params": "path: protocol_id (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: computers.update_protocol has no docstring.",
+ "example": "curl -X PATCH http://localhost:5001/api/computers/protocols/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/types",
+ "auth": "permission:computers.view",
+ "params": "not documented",
+ "purpose": "List all computer types.",
+ "example": "curl http://localhost:5001/api/computers/types -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/computers/types",
+ "auth": "permission:computers.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new computer type.",
+ "example": "curl -X POST http://localhost:5001/api/computers/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/computers/types/",
+ "auth": "permission:computers.delete",
+ "params": "path: type_id (int)",
+ "purpose": "Delete a computer type. Refused if any PC still uses it.",
+ "example": "curl -X DELETE http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/computers/types/",
+ "auth": "permission:computers.view",
+ "params": "path: type_id (int)",
+ "purpose": "Get a single computer type.",
+ "example": "curl http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/computers/types/",
+ "auth": "permission:computers.edit",
+ "params": "path: type_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a computer type.",
+ "example": "curl -X PUT http://localhost:5001/api/computers/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "plugin-employees",
+ "endpoints": [
+ {
+ "method": "DELETE",
+ "path": "/api/employees//photo",
+ "auth": "jwt + role:admin",
+ "params": "path: sso (int)",
+ "purpose": "Clear an employee photo and delete the uploaded file. External mode 409s.",
+ "example": "curl -X DELETE http://localhost:5001/api/employees/1/photo -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/employees//photo",
+ "auth": "jwt + role:admin",
+ "params": "path: sso (int); body: JSON (fields not yet documented)",
+ "purpose": "Upload (or replace) the photo for a self-hosted directory employee.",
+ "example": "curl -X POST http://localhost:5001/api/employees/1/photo -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/employees/directory",
+ "auth": "permission:employees.view",
+ "params": "not documented",
+ "purpose": "Full self-hosted directory (for the management page).",
+ "example": "curl http://localhost:5001/api/employees/directory -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/employees/directory",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: employees.create_directory_employee has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/employees/directory -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/employees/directory/",
+ "auth": "jwt + role:admin",
+ "params": "path: sso (int)",
+ "purpose": "UNDOCUMENTED: employees.delete_directory_employee has no docstring.",
+ "example": "curl -X DELETE http://localhost:5001/api/employees/directory/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/employees/directory/",
+ "auth": "jwt + role:admin",
+ "params": "path: sso (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: employees.update_directory_employee has no docstring.",
+ "example": "curl -X PUT http://localhost:5001/api/employees/directory/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/employees/directory/import",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Bulk upsert from CSV. Accepts headers SSO,First_Name,Last_Name,Team,Role, Picture (case-insensitive; sso/firstname/... also accepted).",
+ "example": "curl -X POST http://localhost:5001/api/employees/directory/import -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/employees/lookup",
+ "auth": "permission:employees.view",
+ "params": "not documented",
+ "purpose": "Look up multiple employees by SSO list.",
+ "example": "curl http://localhost:5001/api/employees/lookup -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/employees/lookup/",
+ "auth": "permission:employees.view",
+ "params": "path: sso (string)",
+ "purpose": "Look up a single employee by SSO.",
+ "example": "curl http://localhost:5001/api/employees/lookup/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/employees/photo/",
+ "auth": "none (reviewed: named in the public-read or mutation sweep)",
+ "params": "path: filename (path)",
+ "purpose": "Serve an uploaded employee photo (public - kiosk cards read it).",
+ "example": "curl http://localhost:5001/api/employees/photo/example.png"
+ },
+ {
+ "method": "GET",
+ "path": "/api/employees/resolve/",
+ "auth": "permission:employees.view",
+ "params": "path: sso (string)",
+ "purpose": "Resolve one SSO to a name and photo, and say HOW.",
+ "example": "curl http://localhost:5001/api/employees/resolve/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/employees/search",
+ "auth": "permission:employees.view",
+ "params": "not documented",
+ "purpose": "Search employees by name.",
+ "example": "curl http://localhost:5001/api/employees/search -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "plugin-endpoints",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/endpoints",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "List reported endpoint states.",
+ "example": "curl http://localhost:5001/api/endpoints -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "List reported endpoint states.",
+ "example": "curl http://localhost:5001/api/endpoints/ -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Get the current state for one endpoint by asset id.",
+ "example": "curl http://localhost:5001/api/endpoints/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints//agentupgrade",
+ "auth": "permission:endpoints.agentupdate",
+ "params": "path: asset_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Push the published agent build to one endpoint.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/1/agentupgrade -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints//commands",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Recent commands for one endpoint, newest first (operator view).",
+ "example": "curl http://localhost:5001/api/endpoints/1/commands -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints//commands",
+ "auth": "permission:endpoints.command",
+ "params": "path: asset_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Queue an allowlisted command for one endpoint.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/1/commands -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints//events",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "List curated Windows events for one endpoint, newest first.",
+ "example": "curl http://localhost:5001/api/endpoints/1/events -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints//holdableactions",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Desired-mode actions on this endpoint, with current hold state.",
+ "example": "curl http://localhost:5001/api/endpoints/1/holdableactions -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/endpoints//lock",
+ "auth": "permission:endpoints.command",
+ "params": "path: asset_id (int)",
+ "purpose": "Lift the quarantine on this endpoint.",
+ "example": "curl -X DELETE http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints//lock",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "The endpoint lock standing on this endpoint, or null.",
+ "example": "curl http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints//lock",
+ "auth": "permission:endpoints.command",
+ "params": "path: asset_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Quarantine this endpoint against non-admin commands.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/1/lock -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints//logcollections",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Collected-log history for one endpoint, newest first.",
+ "example": "curl http://localhost:5001/api/endpoints/1/logcollections -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints//policyholds",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Active break-glass holds for one endpoint.",
+ "example": "curl http://localhost:5001/api/endpoints/1/policyholds -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints//policyholds",
+ "auth": "permission:endpoints.command",
+ "params": "path: asset_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Deliberately suspend desired-state reconciliation for one or more actions.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/1/policyholds -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/endpoints//policyholds/",
+ "auth": "permission:endpoints.command",
+ "params": "path: asset_id (int), action (string)",
+ "purpose": "End a hold early, resuming reconciliation of one action.",
+ "example": "curl -X DELETE http://localhost:5001/api/endpoints/1/policyholds/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints//scriptruns",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "The last few run_script commands for one endpoint, newest first.",
+ "example": "curl http://localhost:5001/api/endpoints/1/scriptruns -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/adoptiontool/package",
+ "auth": "permission:endpoints.agentpackage",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Bundle the adoption tool as a downloadable ZIP.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/adoptiontool/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/endpoints/agentkey",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "Revoke the endpoint agent key: stop all managed-token endpoint traffic.",
+ "example": "curl -X DELETE http://localhost:5001/api/endpoints/agentkey -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/agentkey",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "Report the endpoint agent key state (no secret).",
+ "example": "curl http://localhost:5001/api/endpoints/agentkey -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/agentkey/regenerate",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Mint a fresh endpoint agent key, revoking any previous one.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/agentkey/regenerate -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/alerts",
+ "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)",
+ "params": "not documented",
+ "purpose": "Fleet alerts for one endpoint by hostname (collector-token auth).",
+ "example": "curl http://localhost:5001/api/endpoints/alerts -H \"X-API-Key: $KEY\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/alerts//acknowledge",
+ "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)",
+ "params": "path: notification_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Agent acknowledges a fleet alert (collector-token auth).",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/alerts/1/acknowledge -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/apppackage/",
+ "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)",
+ "params": "path: appversionid (int)",
+ "purpose": "Agent-facing installer download (collector-token auth).",
+ "example": "curl http://localhost:5001/api/endpoints/apppackage/1 -H \"X-API-Key: $KEY\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/apppackage/app/",
+ "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)",
+ "params": "path: app_id (int)",
+ "purpose": "Agent-facing installer download by APP (collector-token auth).",
+ "example": "curl http://localhost:5001/api/endpoints/apppackage/app/1 -H \"X-API-Key: $KEY\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/apppackage/app//",
+ "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)",
+ "params": "path: app_id (int), filename (path)",
+ "purpose": "Agent-facing installer download by APP (collector-token auth).",
+ "example": "curl http://localhost:5001/api/endpoints/apppackage/app/1/example.png -H \"X-API-Key: $KEY\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//activity",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "List panel: one chronological feed of what happened to this endpoint.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/activity -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//applications",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Table panel: the installed-software (SBOM) snapshot for this endpoint.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/applications -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//diskhealth",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Physical drive health rows.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/diskhealth -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//hardware",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Keyvalue panel: processor, memory and system chassis inventory.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/hardware -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//interfaces",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Table panel: every hardware NIC the agent last reported.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/interfaces -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//iphistory",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Table panel: every address this asset has held, newest change first.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/iphistory -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//listeners",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Table panel: TCP sockets in LISTEN, with the owning process.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/listeners -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//mappeddrives",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "List panel: mapped network drives with a health badge each.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/mappeddrives -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//osinfo",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Keyvalue panel: operating system, firmware and domain membership.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/osinfo -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//recentevents",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "List panel: the endpoint's most recent curated events, newest first.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/recentevents -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//services",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "List panel: monitored services/processes with a run-state badge per row.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/services -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//status",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Keyvalue panel: the endpoint's current status snapshot.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/status -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//storage",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Tabs panel: volume capacity and physical drive health as two sections.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/storage -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//telemetrymeta",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Lightweight freshness probe for the panels.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/telemetrymeta -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/asset//usercontext",
+ "auth": "permission:endpoints.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Keyvalue panel: session-scoped data from the interactive user probe.",
+ "example": "curl http://localhost:5001/api/endpoints/asset/1/usercontext -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/collect",
+ "auth": "api-key (endpoints collector token)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Agent telemetry ingest. Same handler as POST /api/collector/endpoints.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/collect -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/commands//result",
+ "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)",
+ "params": "path: command_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Agent reports a claimed command's outcome (collector-token auth).",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/commands/1/result -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/commands/next",
+ "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)",
+ "params": "not documented",
+ "purpose": "Agent long-poll: claim the next pending command for a hostname.",
+ "example": "curl http://localhost:5001/api/endpoints/commands/next -H \"X-API-Key: $KEY\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/dashboard/enrollmentpending",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "One queue row while enroll requests wait for an admin, else nothing.",
+ "example": "curl http://localhost:5001/api/endpoints/dashboard/enrollmentpending -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/dashboard/quiet",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "Endpoints that reported before, then went quiet past the window.",
+ "example": "curl http://localhost:5001/api/endpoints/dashboard/quiet -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/dashboard/reenrollblocked",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "PCs kept from re-enrolling for more than 7 days.",
+ "example": "curl http://localhost:5001/api/endpoints/dashboard/reenrollblocked -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/dashboard/stability",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "Endpoints with recent crash/shutdown/corruption events.",
+ "example": "curl http://localhost:5001/api/endpoints/dashboard/stability -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/dashboard/summary",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "High-level fleet counts for the endpoints dashboard.",
+ "example": "curl http://localhost:5001/api/endpoints/dashboard/summary -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/enroll",
+ "auth": "none (per-IP rate limited and capped; a valid fleet key only decides auto-enroll)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "A PC asks for its own credential.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/enroll -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/enrollment",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "Credential state of every computer, plus pending and recent requests.",
+ "example": "curl http://localhost:5001/api/endpoints/enrollment -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/enrollment/assets//allow-reenroll",
+ "auth": "jwt + role:admin",
+ "params": "path: assetid (int); body: JSON (fields not yet documented)",
+ "purpose": "Lift a re-enrollment block. The PC enrolls on its next attempt.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/assets/1/allow-reenroll -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/enrollment/assets//revoke",
+ "auth": "jwt + role:admin",
+ "params": "path: assetid (int); body: JSON (fields not yet documented)",
+ "purpose": "Un-enroll a PC. Its agent gets CREDENTIAL_INVALID and enrolls again.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/assets/1/revoke -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/enrollment/migration",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "Per-computer readiness to move onto per-PC credentials, plus waves.",
+ "example": "curl http://localhost:5001/api/endpoints/enrollment/migration -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/enrollment/migration/wave",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Push the agent upgrade to a batch of READY PCs.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/migration/wave -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/enrollment/requests//approve",
+ "auth": "jwt + role:admin",
+ "params": "path: requestid (int); body: JSON (fields not yet documented)",
+ "purpose": "Bind a pending request to the asset the admin picked.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/requests/1/approve -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/enrollment/requests//reject",
+ "auth": "jwt + role:admin",
+ "params": "path: requestid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: endpoints.reject_enroll_request has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/enrollment/requests/1/reject -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "PUT",
+ "path": "/api/endpoints/enrollment/settings",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Change the enrollment settings.",
+ "example": "curl -X PUT http://localhost:5001/api/endpoints/enrollment/settings -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/logcollections/",
+ "auth": "permission:endpoints.updateapp",
+ "params": "path: collection_id (int)",
+ "purpose": "One collected log including its content, for the scrollable pane.",
+ "example": "curl http://localhost:5001/api/endpoints/logcollections/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/logcollections//download",
+ "auth": "permission:endpoints.updateapp",
+ "params": "path: collection_id (int)",
+ "purpose": "Serve a collected log as a file attachment.",
+ "example": "curl http://localhost:5001/api/endpoints/logcollections/1/download -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/monitors",
+ "auth": "permission:endpoints.view",
+ "params": "not documented",
+ "purpose": "List monitor definitions (shipped and local).",
+ "example": "curl http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/monitors",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a user-defined (local) monitor definition.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/monitors -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/endpoints/monitors/",
+ "auth": "jwt + role:admin",
+ "params": "path: monitor_id (int)",
+ "purpose": "Delete a local monitor definition. Shipped rows are protected.",
+ "example": "curl -X DELETE http://localhost:5001/api/endpoints/monitors/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/endpoints/monitors/",
+ "auth": "jwt + role:admin",
+ "params": "path: monitor_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a local monitor definition. Shipped rows are read-only.",
+ "example": "curl -X PUT http://localhost:5001/api/endpoints/monitors/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/monitors/resolve",
+ "auth": "permission:endpoints.view",
+ "params": "not documented",
+ "purpose": "Resolve the monitor set for a PC type (agent runtime fetch).",
+ "example": "curl http://localhost:5001/api/endpoints/monitors/resolve -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/monitors/resolved",
+ "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)",
+ "params": "not documented",
+ "purpose": "Agent watch list: the monitors this endpoint should report on.",
+ "example": "curl http://localhost:5001/api/endpoints/monitors/resolved -H \"X-API-Key: $KEY\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/operatorreports",
+ "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Agent uploads one operator issue report (collector-token auth).",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/operatorreports -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/operatorreports//image",
+ "auth": "permission:endpoints.operatorreports",
+ "params": "path: operatorreportid (int)",
+ "purpose": "Serve one operator report's screenshot.",
+ "example": "curl http://localhost:5001/api/endpoints/operatorreports/1/image -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/panel//operatorreports",
+ "auth": "permission:endpoints.operatorreports",
+ "params": "path: asset_id (int)",
+ "purpose": "Operator Reports panel: this PC's recent operator issue reports.",
+ "example": "curl http://localhost:5001/api/endpoints/panel/1/operatorreports -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/scriptbody/",
+ "auth": "api-key (endpoint agent credential, or the fleet key while sharedkeyallowed is on)",
+ "params": "path: script_id (int)",
+ "purpose": "Agent-facing script body download (collector-token auth).",
+ "example": "curl http://localhost:5001/api/endpoints/scriptbody/1 -H \"X-API-Key: $KEY\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/settings/server-cert",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "Get details about the current server certificate.",
+ "example": "curl http://localhost:5001/api/endpoints/settings/server-cert -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/settings/server-cert/download",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "The PUBLIC server certificate as a DER .cer file, never the key.",
+ "example": "curl http://localhost:5001/api/endpoints/settings/server-cert/download -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/settings/server-cert/generate",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Generate a new self-signed server certificate.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/settings/server-cert/generate -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/settings/server-cert/live",
+ "auth": "permission:endpoints.fleet",
+ "params": "not documented",
+ "purpose": "The certificate PCs actually receive from this site's public address.",
+ "example": "curl http://localhost:5001/api/endpoints/settings/server-cert/live -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/settings/server-cert/upload",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Upload a new server certificate and private key.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/settings/server-cert/upload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/siteagent",
+ "auth": "permission:endpoints.agentpackage",
+ "params": "not documented",
+ "purpose": "Report whether the site agent build has a prebuilt binary staged.",
+ "example": "curl http://localhost:5001/api/endpoints/siteagent -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/siteagent/binaries/",
+ "auth": "permission:endpoints.agentpackage",
+ "params": "path: binkey (string)",
+ "purpose": "List staged versions for a binary (agent or tray), newest first.",
+ "example": "curl http://localhost:5001/api/endpoints/siteagent/binaries/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/siteagent/binaries/",
+ "auth": "permission:endpoints.agentpackage",
+ "params": "path: binkey (string); body: JSON (fields not yet documented)",
+ "purpose": "Stage an uploaded binary as a new version.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/siteagent/binaries//adopt",
+ "auth": "permission:endpoints.agentpackage",
+ "params": "path: binkey (string); body: JSON (fields not yet documented)",
+ "purpose": "Adopt the unmanaged legacy flat file into the version store.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x/adopt -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "PUT",
+ "path": "/api/endpoints/siteagent/binaries//current",
+ "auth": "permission:endpoints.agentpackage",
+ "params": "path: binkey (string); body: JSON (fields not yet documented)",
+ "purpose": "Point 'current' at an already-staged version (roll back or forward).",
+ "example": "curl -X PUT http://localhost:5001/api/endpoints/siteagent/binaries/x/current -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/siteagent/binaries//detect",
+ "auth": "permission:endpoints.agentpackage",
+ "params": "path: binkey (string); body: JSON (fields not yet documented)",
+ "purpose": "Read the PE file version of an uploaded EXE without staging it.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/binaries/x/detect -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/siteagent/bootstrap.ps1",
+ "auth": "api-key (single-use bootstrap token in the X-Endpoint-Bootstrap-Token header, or ?t= while that is enabled; per-IP rate limited, 404 when not usable)",
+ "params": "not documented",
+ "purpose": "The PowerShell the one-line command downloads, runs and deletes.",
+ "example": "curl http://localhost:5001/api/endpoints/siteagent/bootstrap.ps1"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/siteagent/bootstrap/package",
+ "auth": "api-key (single-use bootstrap token in the X-Endpoint-Bootstrap-Token header, or ?t= while that is enabled; per-IP rate limited, 404 when not usable)",
+ "params": "not documented",
+ "purpose": "The keyless agent package the bootstrap script installs.",
+ "example": "curl http://localhost:5001/api/endpoints/siteagent/bootstrap/package"
+ },
+ {
+ "method": "GET",
+ "path": "/api/endpoints/siteagent/bootstrap/tokens",
+ "auth": "jwt + role:admin",
+ "params": "not documented",
+ "purpose": "Every bootstrap token, newest first. Never a secret.",
+ "example": "curl http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/siteagent/bootstrap/tokens",
+ "auth": "jwt + role:admin",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Mint one short-lived bootstrap token. The secret is returned ONCE.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/siteagent/bootstrap/tokens//revoke",
+ "auth": "jwt + role:admin",
+ "params": "path: tokenid (int); body: JSON (fields not yet documented)",
+ "purpose": "Kill a bootstrap token now. Already-revoked is a no-op, not an error.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/bootstrap/tokens/1/revoke -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/endpoints/siteagent/package",
+ "auth": "permission:endpoints.agentpackage",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Bundle the site agent as a downloadable ZIP.",
+ "example": "curl -X POST http://localhost:5001/api/endpoints/siteagent/package -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "plugin-geenforce",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/geenforce/applications",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "not documented",
+ "purpose": "The core Applications catalog (id + name) for the curated entry->app link picker in the entry editor.",
+ "example": "curl http://localhost:5001/api/geenforce/applications -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/config",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "not documented",
+ "purpose": "Plugin config: the on-share export root + the client IP allowlist.",
+ "example": "curl http://localhost:5001/api/geenforce/config -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/geenforce/config",
+ "auth": "permission:geenforce.publish",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: geenforce.put_config has no docstring.",
+ "example": "curl -X PUT http://localhost:5001/api/geenforce/config -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/dashboard/failures",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "not documented",
+ "purpose": "Entries that FAILED on their PC's most recent enforcement cycle.",
+ "example": "curl http://localhost:5001/api/geenforce/dashboard/failures -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/geenforce/entries/",
+ "auth": "permission:geenforce.manage",
+ "params": "path: entryid (int)",
+ "purpose": "UNDOCUMENTED: geenforce.delete_entry has no docstring.",
+ "example": "curl -X DELETE http://localhost:5001/api/geenforce/entries/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/geenforce/entries/",
+ "auth": "permission:geenforce.manage",
+ "params": "path: entryid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: geenforce.update_entry has no docstring.",
+ "example": "curl -X PUT http://localhost:5001/api/geenforce/entries/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/entries//payload",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "path: entryid (int)",
+ "purpose": "Return the stored inline payload bytes for an entry (404 if none).",
+ "example": "curl http://localhost:5001/api/geenforce/entries/1/payload -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/geenforce/entries//payload",
+ "auth": "permission:geenforce.publish",
+ "params": "path: entryid (int); body: JSON (fields not yet documented)",
+ "purpose": "Store an inline payload (<= 1 MB) for an entry and point the entry at it.",
+ "example": "curl -X POST http://localhost:5001/api/geenforce/entries/1/payload -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/manifest",
+ "auth": "api-key (geenforce fetch token, or the IP allowlist)",
+ "params": "not documented",
+ "purpose": "Serve the current published manifest for a scope (fat-client: full scope).",
+ "example": "curl http://localhost:5001/api/geenforce/manifest -H \"X-API-Key: $KEY\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/payload/",
+ "auth": "api-key (geenforce fetch token, or the IP allowlist)",
+ "params": "path: sha256 (string)",
+ "purpose": "Serve a payload blob by content hash over HTTPS.",
+ "example": "curl http://localhost:5001/api/geenforce/payload/x -H \"X-API-Key: $KEY\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/geenforce/report",
+ "auth": "api-key (geenforce report token)",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Record one PC's enforcement cycle: applied version (did it receive the update?) + per-entry self-heal outcomes (installed/skipped/failed).",
+ "example": "curl -X POST http://localhost:5001/api/geenforce/report -H \"X-API-Key: $KEY\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/reports",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "not documented",
+ "purpose": "Latest enforcement report per PC (fleet compliance view).",
+ "example": "curl http://localhost:5001/api/geenforce/reports -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/reports/",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "path: reportid (int)",
+ "purpose": "One report with per-entry outcomes (self-heal / failure detail).",
+ "example": "curl http://localhost:5001/api/geenforce/reports/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/scopes",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "not documented",
+ "purpose": "List imaging PC-type scopes with entry + published-version counts.",
+ "example": "curl http://localhost:5001/api/geenforce/scopes -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/geenforce/scopes",
+ "auth": "permission:geenforce.manage",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: geenforce.create_scope has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/geenforce/scopes -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/geenforce/scopes/",
+ "auth": "permission:geenforce.manage",
+ "params": "path: scopeid (int)",
+ "purpose": "UNDOCUMENTED: geenforce.delete_scope has no docstring.",
+ "example": "curl -X DELETE http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/scopes/",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "path: scopeid (int)",
+ "purpose": "UNDOCUMENTED: geenforce.get_scope has no docstring.",
+ "example": "curl http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/geenforce/scopes/",
+ "auth": "permission:geenforce.manage",
+ "params": "path: scopeid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: geenforce.update_scope has no docstring.",
+ "example": "curl -X PUT http://localhost:5001/api/geenforce/scopes/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/scopes//compliance",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "path: scopeid (int)",
+ "purpose": "Fleet-install coverage per app-linked entry (from collected PC data).",
+ "example": "curl http://localhost:5001/api/geenforce/scopes/1/compliance -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/geenforce/scopes//entries",
+ "auth": "permission:geenforce.manage",
+ "params": "path: scopeid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: geenforce.create_entry has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/entries -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "PUT",
+ "path": "/api/geenforce/scopes//entries/reorder",
+ "auth": "permission:geenforce.manage",
+ "params": "path: scopeid (int); body: JSON (fields not yet documented)",
+ "purpose": "Set entry order from a list of entryids (the ordering contract).",
+ "example": "curl -X PUT http://localhost:5001/api/geenforce/scopes/1/entries/reorder -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/geenforce/scopes//export-share",
+ "auth": "permission:geenforce.publish",
+ "params": "path: scopeid (int); body: JSON (fields not yet documented)",
+ "purpose": "Write the scope's current published JSON to the configured share root (backing up the old file to _meta/history first).",
+ "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/export-share -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/scopes//preview",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "path: scopeid (int)",
+ "purpose": "Render the DRAFT manifest JSON a publish would freeze (review before ship).",
+ "example": "curl http://localhost:5001/api/geenforce/scopes/1/preview -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/geenforce/scopes//publish",
+ "auth": "permission:geenforce.publish",
+ "params": "path: scopeid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: geenforce.publish_scope_route has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/publish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/scopes//publish-preflight",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "path: scopeid (int)",
+ "purpose": "Would publishing this scope outrun the fleet's enforcer lib?",
+ "example": "curl http://localhost:5001/api/geenforce/scopes/1/publish-preflight -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/geenforce/scopes//rollback",
+ "auth": "permission:geenforce.publish",
+ "params": "path: scopeid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: geenforce.rollback_scope_route has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/geenforce/scopes/1/rollback -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/scopes//simulate",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "path: scopeid (int)",
+ "purpose": "Which draft entries apply to a given machine profile, and why the rest are filtered out (reuses the engine-mirror filters).",
+ "example": "curl http://localhost:5001/api/geenforce/scopes/1/simulate -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/scopes//versions",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "path: scopeid (int)",
+ "purpose": "UNDOCUMENTED: geenforce.list_versions has no docstring.",
+ "example": "curl http://localhost:5001/api/geenforce/scopes/1/versions -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/geenforce/scopes//versions/",
+ "auth": "permission:geenforce.view or geenforce.manage",
+ "params": "path: scopeid (int), versionnumber (int)",
+ "purpose": "UNDOCUMENTED: geenforce.get_version has no docstring.",
+ "example": "curl http://localhost:5001/api/geenforce/scopes/1/versions/1 -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "plugin-guides",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/guides",
+ "auth": "permission:guides.view",
+ "params": "not documented",
+ "purpose": "Published guides, plus the caller's own drafts if they can author.",
+ "example": "curl http://localhost:5001/api/guides -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/guides",
+ "auth": "permission:guides.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: guides.create_guide has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/guides -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/guides/",
+ "auth": "permission:guides.delete",
+ "params": "path: guideid (int)",
+ "purpose": "UNDOCUMENTED: guides.delete_guide has no docstring.",
+ "example": "curl -X DELETE http://localhost:5001/api/guides/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/guides/",
+ "auth": "permission:guides.view",
+ "params": "path: guideid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: guides.update_guide has no docstring.",
+ "example": "curl -X PUT http://localhost:5001/api/guides/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/guides//publish",
+ "auth": "permission:guides.publish",
+ "params": "path: guideid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: guides.publish_guide has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/guides/1/publish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/guides//restore/",
+ "auth": "permission:guides.view",
+ "params": "path: guideid (int), revisionid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: guides.restore_revision has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/guides/1/restore/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/guides//revisions",
+ "auth": "permission:guides.view",
+ "params": "path: guideid (int)",
+ "purpose": "UNDOCUMENTED: guides.list_revisions has no docstring.",
+ "example": "curl http://localhost:5001/api/guides/1/revisions -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/guides//unpublish",
+ "auth": "permission:guides.publish",
+ "params": "path: guideid (int); body: JSON (fields not yet documented)",
+ "purpose": "UNDOCUMENTED: guides.unpublish_guide has no docstring.",
+ "example": "curl -X POST http://localhost:5001/api/guides/1/unpublish -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/guides//working",
+ "auth": "permission:guides.view",
+ "params": "path: guideid (int)",
+ "purpose": "The working copy, for the editor.",
+ "example": "curl http://localhost:5001/api/guides/1/working -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/guides/",
+ "auth": "permission:guides.view",
+ "params": "path: slug (string)",
+ "purpose": "What a reader gets: the published revision.",
+ "example": "curl http://localhost:5001/api/guides/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/guides/images",
+ "auth": "permission:guides.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Take a pasted or dropped image.",
+ "example": "curl -X POST http://localhost:5001/api/guides/images -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/guides/images/",
+ "auth": "permission:guides.view",
+ "params": "path: imageid (int)",
+ "purpose": "The image itself, under the same rule as the guide that holds it.",
+ "example": "curl http://localhost:5001/api/guides/images/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/guides/revisions/",
+ "auth": "permission:guides.view",
+ "params": "path: revisionid (int)",
+ "purpose": "One revision by id.",
+ "example": "curl http://localhost:5001/api/guides/revisions/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/guides/tags",
+ "auth": "permission:guides.view",
+ "params": "not documented",
+ "purpose": "Every tag in use, with a count, for the filter chips.",
+ "example": "curl http://localhost:5001/api/guides/tags -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "plugin-knowledgebase",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/knowledgebase",
+ "auth": "permission:kb.view",
+ "params": "not documented",
+ "purpose": "List all knowledge base articles.",
+ "example": "curl http://localhost:5001/api/knowledgebase -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/knowledgebase",
+ "auth": "permission:kb.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new knowledge base article.",
+ "example": "curl -X POST http://localhost:5001/api/knowledgebase -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/knowledgebase/",
+ "auth": "permission:kb.delete",
+ "params": "path: link_id (int)",
+ "purpose": "Delete (deactivate) a knowledge base article.",
+ "example": "curl -X DELETE http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/knowledgebase/",
+ "auth": "permission:kb.view",
+ "params": "path: link_id (int)",
+ "purpose": "Get a single knowledge base article.",
+ "example": "curl http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/knowledgebase/",
+ "auth": "permission:kb.edit",
+ "params": "path: link_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a knowledge base article.",
+ "example": "curl -X PUT http://localhost:5001/api/knowledgebase/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "POST",
+ "path": "/api/knowledgebase//click",
+ "auth": "permission:kb.view",
+ "params": "path: link_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Increment the click counter for an article the caller is allowed to read.",
+ "example": "curl -X POST http://localhost:5001/api/knowledgebase/1/click -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/knowledgebase/stats",
+ "auth": "permission:kb.view",
+ "params": "not documented",
+ "purpose": "Get knowledge base statistics.",
+ "example": "curl http://localhost:5001/api/knowledgebase/stats -H \"Authorization: Bearer $TOK\""
+ }
+ ]
+ },
+ {
+ "surface": "plugin-machines",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/machines",
+ "auth": "permission:machines.view",
+ "params": "not documented",
+ "purpose": "List all machines with filtering and pagination.",
+ "example": "curl http://localhost:5001/api/machines -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/machines",
+ "auth": "permission:machines.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create new machine (creates both Asset and Machine records).",
+ "example": "curl -X POST http://localhost:5001/api/machines -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/machines/",
+ "auth": "permission:machines.delete",
+ "params": "path: machine_id (int)",
+ "purpose": "Delete (soft delete) machine.",
+ "example": "curl -X DELETE http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/machines/",
+ "auth": "permission:machines.view",
+ "params": "path: machine_id (int)",
+ "purpose": "Get a single machine item with full details.",
+ "example": "curl http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/machines/",
+ "auth": "permission:machines.edit",
+ "params": "path: machine_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update machine (both Asset and Machine records).",
+ "example": "curl -X PUT http://localhost:5001/api/machines/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/machines/by-asset/",
+ "auth": "permission:machines.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Get machine data by asset ID.",
+ "example": "curl http://localhost:5001/api/machines/by-asset/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/machines/dashboard/outofservice",
+ "auth": "permission:machines.view",
+ "params": "not documented",
+ "purpose": "Machines whose status says they are not running.",
+ "example": "curl http://localhost:5001/api/machines/dashboard/outofservice -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/machines/dashboard/summary",
+ "auth": "permission:machines.view",
+ "params": "not documented",
+ "purpose": "Get machine dashboard summary data.",
+ "example": "curl http://localhost:5001/api/machines/dashboard/summary -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/machines/types",
+ "auth": "permission:machines.view",
+ "params": "not documented",
+ "purpose": "List all machine types.",
+ "example": "curl http://localhost:5001/api/machines/types -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/machines/types",
+ "auth": "permission:machines.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new machine type.",
+ "example": "curl -X POST http://localhost:5001/api/machines/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/machines/types/",
+ "auth": "permission:machines.delete",
+ "params": "path: type_id (int)",
+ "purpose": "Delete a machine type. Refused if any asset still uses it.",
+ "example": "curl -X DELETE http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/machines/types/",
+ "auth": "permission:machines.view",
+ "params": "path: type_id (int)",
+ "purpose": "Get a single machine type.",
+ "example": "curl http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/machines/types/",
+ "auth": "permission:machines.edit",
+ "params": "path: type_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a machine type.",
+ "example": "curl -X PUT http://localhost:5001/api/machines/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "plugin-measuringtools",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/measuringtools",
+ "auth": "permission:measuringtools.view",
+ "params": "not documented",
+ "purpose": "List measuring tools with filters + pagination.",
+ "example": "curl http://localhost:5001/api/measuringtools -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/measuringtools",
+ "auth": "permission:measuringtools.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a measuring tool: one Asset core row + one extension row.",
+ "example": "curl -X POST http://localhost:5001/api/measuringtools -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/measuringtools/",
+ "auth": "permission:measuringtools.delete",
+ "params": "path: tool_id (int)",
+ "purpose": "Soft delete a measuring tool (deactivates its asset).",
+ "example": "curl -X DELETE http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/measuringtools/",
+ "auth": "permission:measuringtools.view",
+ "params": "path: tool_id (int)",
+ "purpose": "Get one measuring tool (asset core merged with the extension).",
+ "example": "curl http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/measuringtools/",
+ "auth": "permission:measuringtools.edit",
+ "params": "path: tool_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a measuring tool (asset core fields + extension in one payload).",
+ "example": "curl -X PUT http://localhost:5001/api/measuringtools/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/measuringtools/by-asset/",
+ "auth": "permission:measuringtools.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Get a measuring tool by its core asset id.",
+ "example": "curl http://localhost:5001/api/measuringtools/by-asset/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/measuringtools/map-overlay",
+ "auth": "permission:measuringtools.view",
+ "params": "not documented",
+ "purpose": "Calibration-status overlay for active measuring tools.",
+ "example": "curl http://localhost:5001/api/measuringtools/map-overlay -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/measuringtools/report/calibration",
+ "auth": "permission:measuringtools.view",
+ "params": "not documented",
+ "purpose": "Counts + lists bucketed by derived calibration status.",
+ "example": "curl http://localhost:5001/api/measuringtools/report/calibration -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/measuringtools/types",
+ "auth": "permission:measuringtools.view",
+ "params": "not documented",
+ "purpose": "List measuring-tool types.",
+ "example": "curl http://localhost:5001/api/measuringtools/types -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/measuringtools/types",
+ "auth": "permission:measuringtools.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a measuring-tool type (reactivates a soft-deleted same-named one).",
+ "example": "curl -X POST http://localhost:5001/api/measuringtools/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/measuringtools/types/",
+ "auth": "permission:measuringtools.delete",
+ "params": "path: type_id (int)",
+ "purpose": "Delete a measuring-tool type. Refused if any tool still uses it.",
+ "example": "curl -X DELETE http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/measuringtools/types/",
+ "auth": "permission:measuringtools.view",
+ "params": "path: type_id (int)",
+ "purpose": "Get one measuring-tool type.",
+ "example": "curl http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/measuringtools/types/",
+ "auth": "permission:measuringtools.edit",
+ "params": "path: type_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a measuring-tool type.",
+ "example": "curl -X PUT http://localhost:5001/api/measuringtools/types/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ }
+ ]
+ },
+ {
+ "surface": "plugin-network",
+ "endpoints": [
+ {
+ "method": "GET",
+ "path": "/api/network",
+ "auth": "permission:network.view",
+ "params": "not documented",
+ "purpose": "List all network devices with filtering and pagination.",
+ "example": "curl http://localhost:5001/api/network -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/network",
+ "auth": "permission:network.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create new network device (creates both Asset and NetworkDevice records).",
+ "example": "curl -X POST http://localhost:5001/api/network -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/network/",
+ "auth": "permission:network.delete",
+ "params": "path: device_id (int)",
+ "purpose": "Delete (soft delete) network device.",
+ "example": "curl -X DELETE http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/network/",
+ "auth": "permission:network.view",
+ "params": "path: device_id (int)",
+ "purpose": "Get a single network device with full details.",
+ "example": "curl http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/network/",
+ "auth": "permission:network.edit",
+ "params": "path: device_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update network device (both Asset and NetworkDevice records).",
+ "example": "curl -X PUT http://localhost:5001/api/network/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/network/by-asset/",
+ "auth": "permission:network.view",
+ "params": "path: asset_id (int)",
+ "purpose": "Get network device data by asset ID.",
+ "example": "curl http://localhost:5001/api/network/by-asset/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/network/by-hostname/",
+ "auth": "permission:network.view",
+ "params": "path: hostname (string)",
+ "purpose": "Get network device by hostname.",
+ "example": "curl http://localhost:5001/api/network/by-hostname/x -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/network/dashboard/summary",
+ "auth": "permission:network.view",
+ "params": "not documented",
+ "purpose": "Get network device dashboard summary data.",
+ "example": "curl http://localhost:5001/api/network/dashboard/summary -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/network/subnets",
+ "auth": "permission:network.view",
+ "params": "not documented",
+ "purpose": "List all subnets with filtering and pagination.",
+ "example": "curl http://localhost:5001/api/network/subnets -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/network/subnets",
+ "auth": "permission:network.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new subnet.",
+ "example": "curl -X POST http://localhost:5001/api/network/subnets -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/network/subnets/",
+ "auth": "permission:network.delete",
+ "params": "path: subnet_id (int)",
+ "purpose": "Delete (soft delete) a subnet.",
+ "example": "curl -X DELETE http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "GET",
+ "path": "/api/network/subnets/",
+ "auth": "permission:network.view",
+ "params": "path: subnet_id (int)",
+ "purpose": "Get a single subnet plus the network devices whose primary IP falls in its CIDR range.",
+ "example": "curl http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "PUT",
+ "path": "/api/network/subnets/",
+ "auth": "permission:network.edit",
+ "params": "path: subnet_id (int); body: JSON (fields not yet documented)",
+ "purpose": "Update a subnet.",
+ "example": "curl -X PUT http://localhost:5001/api/network/subnets/1 -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "GET",
+ "path": "/api/network/types",
+ "auth": "permission:network.view",
+ "params": "not documented",
+ "purpose": "List all network device types.",
+ "example": "curl http://localhost:5001/api/network/types -H \"Authorization: Bearer $TOK\""
+ },
+ {
+ "method": "POST",
+ "path": "/api/network/types",
+ "auth": "permission:network.create",
+ "params": "body: JSON (fields not yet documented)",
+ "purpose": "Create a new network device type.",
+ "example": "curl -X POST http://localhost:5001/api/network/types -H \"Authorization: Bearer $TOK\" -H 'Content-Type: application/json' -d '{}'"
+ },
+ {
+ "method": "DELETE",
+ "path": "/api/network/types/