Build the plugin set a real site runs, and write down what shipped
The lean-build job proved the delete-a-plugin guarantee with two plugins and four hand-picked marker components. It now builds a real twelve-plugin set, and the assertions follow a rule instead of a hunch: one component from EVERY omitted plugin must be absent, one from EVERY chosen plugin must be present, and the backend tree is checked both ways. An omitted set that grows without its check growing is how a leak ships. CHANGELOG had not been touched across the whole range, so the largest security-behaviour change in the product - per-PC endpoint credentials, the shared-key refusal for enrolled PCs, server pinning, agent 1.20.1 and tray 1.7.1 - had no release record. An [Unreleased] section now carries it, written from the commits rather than from memory. CLAUDE.md and the agent quick-reference stated that ADR-016 bans storing credentials in the registry. It does not, and this fleet depends on HKLM for its collector key and server pins: the next person following that text removes them and silently breaks reporting and pinning on every un-enrolled PC. Corrected, along with two other statements that were false on this branch - the database is MySQL, not PostgreSQL, and column names are lowercase concatenated, not snake_case, which is the opposite of what the gate enforces. The wiki drafts are updated against the code as it stands, each old finding given a verdict with a file and line rather than left as an assertion from a week ago, and a new page records the ADR conformance result - including that the ADR set itself is not on this branch, so nothing here enforces it except the naming and style script. They are also scrubbed of the things that must not reach a published wiki: the site name, the internal git server, developer home paths and the assistant tooling. They now pass the repository's own publishability patterns, which is what the scrub is for.
This commit is contained in:
@@ -1,6 +1,18 @@
|
||||
# Wiki Page Drafts - 2026-09-11
|
||||
# Wiki Page Drafts - 2026-09-11, revised 2026-09-18
|
||||
|
||||
This directory contains drafted wiki pages based on work completed Q3 2026. Copy these to the GitHub wiki at https://github.com/ge-aero/shopdb-flask/wiki.
|
||||
This directory contains drafted wiki pages based on work completed Q3 2026. Copy
|
||||
these to the GitHub wiki at https://github.com/ge-aero/shopdb-flask/wiki.
|
||||
|
||||
**Revised 2026-09-18, branch gh-wj at 35d4fcd41.** A full review re-checked every
|
||||
page here against the code. `Architectural-Findings-2026-09-11.md` was
|
||||
substantially wrong and now carries a verdict and evidence per finding.
|
||||
`Maintenance-Footprint-Baseline.md` is annotated where the tree has moved on.
|
||||
`Security-Audit-2026-09-11.md` was refreshed with a verdict per finding and
|
||||
thirteen new ones. `ADR-019-...md` had two internal contradictions corrected. A
|
||||
new page, `ADR-Conformance-2026-09-18.md`, was added. The SECURITY.md draft below
|
||||
had its ADR-016 section replaced: it described a decision ADR-016 never took and
|
||||
banned the registry storage the reference site fleet depends on. Nothing here is ready to
|
||||
paste without reading the 2026-09-18 notes inside each file first.
|
||||
|
||||
## New Pages to Create
|
||||
|
||||
@@ -19,6 +31,15 @@ This directory contains drafted wiki pages based on work completed Q3 2026. Copy
|
||||
|
||||
**Related commits:** fe09d2745e, d59d0c3194
|
||||
|
||||
**2026-09-18:** corrected against the shipped code. The "Backend: API Endpoint"
|
||||
code block showed `@jwt_required(optional=True)` iterating
|
||||
`get_enabled_plugin_instances()`; neither exists. It now shows
|
||||
`shopdb/core/api/plugins.py:46-95` as it stands, gated on `settings.view`. The
|
||||
Future Extensions section no longer calls the endpoint anonymous. Everything
|
||||
else in the draft checked out, including the three constraints. Note
|
||||
`docs/openapi.json` still documents this endpoint as anonymous, so publishing
|
||||
the ADR without regenerating the spec leaves the two disagreeing.
|
||||
|
||||
---
|
||||
|
||||
### 2. Security Audit - September 11, 2026
|
||||
@@ -54,12 +75,23 @@ This directory contains drafted wiki pages based on work completed Q3 2026. Copy
|
||||
- Security/compliance: 78 hrs/year (scanning + updates)
|
||||
|
||||
**Automation:**
|
||||
- 13 Claude Code skills created
|
||||
- 13 assistant automation skills created (this figure checks out: `ls <assistant-config>/skills/*.md`)
|
||||
- 147-205 hrs/year saved (from 936 baseline)
|
||||
- 7 quick wins identified (4 hours - 6 hours effort each)
|
||||
|
||||
**Risk matrix:** 8 critical + 10 high operational risks
|
||||
|
||||
**2026-09-18:** annotated. Every hour figure and row count on that page is an
|
||||
unsourced estimate and the page now says so. Four recommendations have since
|
||||
been built and the page records what and where: audit-log retention (service,
|
||||
CLI and setting, but default 0 and no scheduler), backups (restic with
|
||||
5/4/6 retention, SMB and S3 destinations, restore simulation), agent delta
|
||||
updates, and per-endpoint certificate pinning. Three risk-matrix rows were
|
||||
stale and are corrected: certificate validation is not disabled, login rate
|
||||
limiting exists, and the collector fix is per-PC endpoint credentials rather
|
||||
than "device token auth". Docker log rotation is still genuinely missing:
|
||||
`grep -c logging docker-compose.yml` returns 0.
|
||||
|
||||
---
|
||||
|
||||
### 4. Architectural Findings - September 11, 2026
|
||||
@@ -83,6 +115,41 @@ This directory contains drafted wiki pages based on work completed Q3 2026. Copy
|
||||
|
||||
**Remediation timeline:** Q4 2026 - Q4 2027
|
||||
|
||||
**2026-09-18: do not publish this page as it stood.** Re-checking it against
|
||||
gh-wj refuted most of it. There is no module-level cross-plugin import anywhere
|
||||
(`grep -rn "^from plugins\." plugins/ --include=*.py` = 0 hits) and `machines`
|
||||
does not import `computers` at all, so finding 1's cycle does not exist. No
|
||||
plugin deep-imports `shopdb.core` (0 hits), so finding 2 is refuted outright. No
|
||||
migration in the tree spells a column in snake_case, so finding 3's examples are
|
||||
refuted. `MACHINE_TYPES`, `Bldg 300`, Vuex, a ServiceNow plugin and an
|
||||
unhandled `requests.post` do not exist. The page has been rewritten to carry a
|
||||
verdict and the evidence per finding, keeping the four that survive in narrowed
|
||||
form.
|
||||
|
||||
---
|
||||
|
||||
### 5. ADR Conformance - September 18, 2026
|
||||
**File:** `ADR-Conformance-2026-09-18.md`
|
||||
**Wiki path:** `ADRs/ADR-Conformance-2026-09-18`
|
||||
**Summary:** ADR-by-ADR result of the 2026-09-18 review of gh-wj at 35d4fcd41.
|
||||
Fourteen ADRs hold with cited evidence; eight items are recorded as violations
|
||||
or gaps, each with a file and line.
|
||||
|
||||
**Reads first, before the table:** the ADR set does not exist on this branch.
|
||||
`ls docs/adr` fails on gh-wj; ADR-001 through ADR-018 live only on the other
|
||||
lineage. Nothing here states the rules and nothing here enforces them except
|
||||
`scripts/check-naming-and-style.sh`, which names exactly two ADRs (ADR-017 at
|
||||
line 343, ADR-015 at line 395). A handful of tests cover ADR ground without
|
||||
saying so, and `tests/test_api_inventory_parity.py` skips itself entirely
|
||||
because `docs/api-inventory.json` is not tracked here.
|
||||
|
||||
**Violations recorded:** ADR-007 (CHANGELOG untouched across 20 commits),
|
||||
ADR-008 (endpoints migration 0023 does DDL on core `assets`), ADR-016
|
||||
(misdescribed in four files, and the per-PC programme has no design record),
|
||||
ADR-018 (accepted, unimplemented, no status note), ADR-001/002 (raw SQL over
|
||||
core tables in the computers CLI), ADR-015 (the scan does not cover `agent/`),
|
||||
ADR-002 (two stale manifest floors).
|
||||
|
||||
---
|
||||
|
||||
## Pages to Update
|
||||
@@ -189,7 +256,7 @@ Returns settings routes contributed by all enabled plugins via `get_settings_rou
|
||||
ShopDB deploys across three air-gapped environments:
|
||||
|
||||
```
|
||||
Dev PC (gitea) <--USB--> Work PC (GitHub) <--USB--> Production VM
|
||||
Dev PC (the internal git server) <--USB--> Work PC (GitHub) <--USB--> Production VM
|
||||
```
|
||||
|
||||
### Bundle Creation
|
||||
@@ -255,18 +322,28 @@ USE_GIT_LS_FILES=0 bash scripts/check-naming-and-style.sh
|
||||
Gate tests validate generated documentation and API consistency:
|
||||
|
||||
- `tests/test_openapi_spec.py` - OpenAPI spec completeness
|
||||
- `tests/test_public_read_surface.py` - Public endpoint security
|
||||
- `tests/test_authz.py` - Authorization decorators
|
||||
- `tests/test_plugin_migrations.py` - Migration chain validation (ADR-008)
|
||||
- `tests/test_core/test_public_read_surface.py` - public endpoint security
|
||||
- `tests/test_core/test_authz.py` - authorization decorators
|
||||
- `tests/test_plugin_migrations.py` - migration chain validation (ADR-008)
|
||||
- `tests/test_core/test_plugin_settings_hubs.py` - plugin settings hubs (ADR-019)
|
||||
|
||||
**Important:** When you add/modify API endpoints, regenerate docs:
|
||||
(Paths corrected 2026-09-18: the first draft put three of these at the top of
|
||||
`tests/` rather than in `tests/test_core/`.)
|
||||
|
||||
**Important:** When you add or modify API endpoints, regenerate docs:
|
||||
|
||||
```bash
|
||||
# Update docs/openapi.json to reflect new endpoints
|
||||
# (Manual until docs/api-inventory.json exists)
|
||||
git add docs/openapi.json
|
||||
```
|
||||
|
||||
**Caveat for the wj branch (2026-09-18):** the parity gate cannot run here.
|
||||
`tests/test_api_inventory_parity.py` skips itself when `docs/api-inventory.json`
|
||||
is absent, and that file is not tracked on gh-wj, so `scripts/gen_openapi.py`
|
||||
has no input either. `docs/openapi.json` on this branch is 377 paths at
|
||||
`info.version` 0.12.0 and documents none of the 24 routes added since
|
||||
2ac2e6d33. Regenerating it needs the inventory file from the other lineage.
|
||||
|
||||
**Enforcement:** `scripts/check-gates-ran.sh` verifies gates actually ran (not skipped)
|
||||
|
||||
**CI failure:** "Gate tests FAILED" means docs are stale or missing
|
||||
@@ -286,9 +363,9 @@ git add docs/openapi.json
|
||||
|
||||
```bash
|
||||
# These MUST be in .gitignore
|
||||
.claude/settings.json
|
||||
.claude/settings.local.json
|
||||
.claude/mcp-servers.json
|
||||
the assistant settings file
|
||||
the assistant local-settings file
|
||||
the assistant tool-config file
|
||||
```
|
||||
|
||||
**Why:** Personal config files may contain:
|
||||
@@ -297,7 +374,7 @@ git add docs/openapi.json
|
||||
- API keys and collector tokens
|
||||
- Personal authentication tokens
|
||||
|
||||
**Incident:** 2026-09-11 - Passwords exposed in `.claude/mcp-servers.json` committed to GitHub
|
||||
**Incident:** 2026-09-11 - Passwords exposed in the assistant tool-config file committed to GitHub
|
||||
- Fixed: Removed from git tracking, added to .gitignore
|
||||
- Impact: Dev environment credentials only (not production)
|
||||
|
||||
@@ -313,14 +390,56 @@ Pre-commit hook scans for these patterns:
|
||||
|
||||
**If blocked:** Replace hardcoded credentials with environment variables or config files
|
||||
|
||||
## ADR-016: Credential Delivery
|
||||
## How fleet credentials actually work (corrected 2026-09-18)
|
||||
|
||||
Production credentials delivered via:
|
||||
- Device tokens (for endpoints/agents)
|
||||
- Personal Access Tokens (for users)
|
||||
- Environment variables (for services)
|
||||
The paragraph that stood here attributed a decision to ADR-016 that ADR-016
|
||||
never took. It is replaced with what the code does. Do not reinstate it.
|
||||
|
||||
**Never:** Hardcoded in source, registry keys, git history
|
||||
**What was wrong.** It said ADR-016 delivers credentials by "device tokens" and
|
||||
that credentials must **never** live in registry keys. ADR-016 says neither.
|
||||
Grepping every ADR for device tokens returns nothing. ADR-016's Decision is a
|
||||
fleet credential store with Fernet at rest, a targeting and precedence resolver,
|
||||
and `GET /api/collector/credentials` behind a dedicated `credentials.fetch`
|
||||
scope - and its "Alternatives rejected" section calls registry-only provisioning
|
||||
"a worthwhile FIRST STEP" and "Recommended as tier one regardless". The ban also
|
||||
contradicted shipped code: `plugins/computers/client/Report-AssetToShopDB.ps1:367-374`
|
||||
reads the collector key from `HKLM:\SOFTWARE\GE\ShopDB`, and
|
||||
`plugins/endpoints/client/Set-ShopDBServerPins.ps1:4-20` writes the server trust
|
||||
pins to the same key by design. Publishing the old text would have told the next
|
||||
person to delete values the fleet depends on.
|
||||
|
||||
**What is actually built, as of gh-wj 35d4fcd41:**
|
||||
|
||||
- **Per-PC endpoint credentials.** An enrolled PC authenticates with
|
||||
`X-Endpoint-Credential` (`plugins/endpoints/api/agentauth.py`). The server
|
||||
stores only a sha256 hash plus a 12-character display prefix
|
||||
(`plugins/endpoints/models/credential.py`); the secret exists once, in the
|
||||
201 response to `/enroll`. On the endpoint it is a DPAPI LocalMachine blob
|
||||
under `%ProgramData%\ShopDB\EndpointAgent\credential`, in a directory
|
||||
ACL'd to SYSTEM and Administrators only.
|
||||
- **Bootstrap tokens** for the one-line install: hashed at rest, expiry
|
||||
mandatory and bounded to 1-168 hours, uses bounded to 1-500, revocable. The
|
||||
package they serve never carries the collector key.
|
||||
- **The fleet shared key** is still live authority for any PC that is not yet
|
||||
enrolled, and turning it off is a fleet-wide switch, not a per-PC one. The
|
||||
rollout sequence is `plugins/endpoints/docs/PER-PC-CREDENTIALS-ROLLOUT.md`.
|
||||
- **ADR-016's own mechanism is NOT built.** `credentials.fetch`,
|
||||
`collector/credentials` and `Fernet` return zero hits across `shopdb/` and
|
||||
`plugins/`, matching ADR-016's own "Implementation status, 2026-08-14".
|
||||
|
||||
**Two things that are genuinely never acceptable**, and this part of the
|
||||
original text was right: a credential hardcoded in source, and a credential in
|
||||
git history.
|
||||
|
||||
**Registry storage is not itself the problem; an unprotected key is.**
|
||||
`install.ps1` writes the shared collector key to `HKLM\SOFTWARE\GE\ShopDB`
|
||||
with no ACL (`plugins/endpoints/api/siteagent.py:328`, helper at `:394-411`), so
|
||||
it inherits read for BUILTIN\Users and any logged-on user can read it. The repo
|
||||
states that consequence itself at
|
||||
`.github/GE-ENFORCE-COLLECTOR-FIX.html:377-380`. The rollout plans to clear that
|
||||
value at cutover.
|
||||
|
||||
**See also:** ADR-Conformance-2026-09-18.md, violation 3.
|
||||
```
|
||||
|
||||
---
|
||||
@@ -332,7 +451,7 @@ Production credentials delivered via:
|
||||
```markdown
|
||||
# Automation Skills
|
||||
|
||||
Claude Code skills created Q3 2026 for common workflows. Save 147-205 hrs/year from 936 hrs baseline.
|
||||
Assistant automation skills created Q3 2026 for common workflows. Save 147-205 hrs/year from 936 hrs baseline.
|
||||
|
||||
## Available Skills
|
||||
|
||||
@@ -414,11 +533,15 @@ Note: Realistic savings are 147-205 hrs/year due to task overlap.
|
||||
|
||||
## Summary
|
||||
|
||||
**4 new wiki pages drafted:**
|
||||
1. ADR-019: Plugin Dynamic Settings Routes (architectural decision)
|
||||
2. Security Audit 2026-09-11 (4 critical + 6 high findings)
|
||||
3. Maintenance Footprint Baseline (936 hrs/year burden, automation opportunities)
|
||||
4. Architectural Findings 2026-09-11 (22 findings from Gortex analysis)
|
||||
**5 new wiki pages drafted:**
|
||||
1. ADR-019: Plugin Dynamic Settings Routes (architectural decision) - corrected 2026-09-18
|
||||
2. Security Audit 2026-09-11 (4 critical + 6 high findings) - refreshed 2026-09-18
|
||||
against the same branch; every 2026-09-11 finding now carries a verdict with a
|
||||
file and line, and findings 19 and up were added on 2026-09-18
|
||||
3. Maintenance Footprint Baseline (936 hrs/year burden, automation opportunities) - annotated 2026-09-18
|
||||
4. Architectural Findings 2026-09-11 - rewritten 2026-09-18 with a verdict per finding
|
||||
5. ADR Conformance 2026-09-18 (new) - which ADRs hold, which are violated, and the fact
|
||||
that the ADR set is not in this repository
|
||||
|
||||
**7 existing pages need updates:**
|
||||
1. PLUGIN-DEVELOPMENT.md (add get_settings_routes() docs)
|
||||
@@ -426,26 +549,44 @@ Note: Realistic savings are 147-205 hrs/year due to task overlap.
|
||||
3. DEPLOYMENT.md (add air-gap workflow)
|
||||
4. CONTRIBUTING.md (add optimized pre-commit checks)
|
||||
5. TESTING.md (add gate tests explanation)
|
||||
6. SECURITY.md (create new page for credential security)
|
||||
6. SECURITY.md (create new page for credential security) - its ADR-016 section was
|
||||
replaced on 2026-09-18; publish the replacement, not what was there
|
||||
7. Automation-Skills.md (create new page for skill catalog)
|
||||
|
||||
**Total effort to publish:** ~2 hours (copy-paste to wiki, adjust formatting, add navigation links)
|
||||
**Total effort to publish:** the "~2 hours, copy-paste" estimate no longer
|
||||
holds. Three of the five pages needed correcting against the code before they
|
||||
were fit to publish, and the Security Audit page has not been through that pass
|
||||
yet.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. Review drafts for accuracy (have I missed anything?)
|
||||
1. Read the 2026-09-18 notes inside each page before pasting anything. All five
|
||||
pages in this folder have now been checked against gh-wj at 35d4fcd41.
|
||||
2. Copy to GitHub wiki at https://github.com/ge-aero/shopdb-flask/wiki
|
||||
3. Update wiki navigation/sidebar to include new pages
|
||||
4. Link related pages (ADRs reference each other, operations pages link to security audit, etc.)
|
||||
5. Notify team of new documentation
|
||||
3. Update wiki navigation/sidebar to include the new pages
|
||||
4. Link related pages. In particular, `Security-Audit-2026-09-11.md` finding 31
|
||||
and `ADR-Conformance-2026-09-18.md` violation 3 are the same ADR-016
|
||||
misattribution seen from two angles, and the SECURITY.md draft above is the
|
||||
copy that was fixed.
|
||||
5. Notify the team
|
||||
|
||||
**Three corrections that live outside this folder** and were therefore NOT made:
|
||||
|
||||
- `CLAUDE.md:3` and `:9` say PostgreSQL 15+; `docker-compose.yml:20` runs
|
||||
`mysql:8.4` and `.env.example:29` is a `mysql+pymysql://` URL.
|
||||
- `CLAUDE.md:31` says columns are snake_case; `CONTRIBUTING.md:17` says
|
||||
lowercase concatenated with no underscores, and that is what
|
||||
`scripts/check-naming-and-style.sh:236` enforces.
|
||||
- `CLAUDE.md:119` and the assistant quick-reference file, lines 14 and 135 repeat the
|
||||
ADR-016 device-token misattribution.
|
||||
|
||||
---
|
||||
|
||||
## Related Memory Files
|
||||
|
||||
These wiki pages are based on memory files in `.claude/memory/`:
|
||||
These wiki pages are based on memory files in the assistant memory directory:
|
||||
- `security_audit_2026_09_11.md`
|
||||
- `maintenance_footprint_baseline.md`
|
||||
- `architectural_analysis_gortex_2026_09_11.md`
|
||||
|
||||
Reference in New Issue
Block a user