docs: what a site needs that no page answered

Four gaps a second site hits and cannot resolve by reading.

**Restoring on Windows** was one sentence - "the standard mysql < dump.sql" -
with no ordering. Restoring a database under running code that expects a
different schema turns a restore into a second incident, so the steps are now
ordered and each says why. It also says what `.env` costs if it is lost, which
is the part nobody discovers until they are already rebuilding: the dump does
not contain it, and without the JWT secrets every issued token dies, so every
collector and every GE-Enforce client on the fleet needs a new key.

**Rolling back** had a paragraph saying downgrades are refused and a backup is
the way back, but not the procedure. Rollback is restoring a matched pair, code
and the schema it expects, in that order - and the doc now separates it from the
case it gets confused with: a migration that failed mid-update has already been
rolled back by the installer, and fixing forward is the only move.

**Sizing, acquisition and support** were absent from the install guide entirely.
A reader could not learn how big a server to ask for, where the .exe comes from,
or where to raise a problem. The sizing is small and the reasons are stated, so
a site does not over-provision a VM for a load that is a few dozen users.

**Credentials** were described in three documents from three ends, so three
answers existed for where a key lives. One table, both ends - server and PC -
plus the two rules behind it: what a shop-floor PC holds is scoped to exactly
what it does, and a credential is delivered rather than typed, because a value
entered per machine is a value that is wrong on some machine.
This commit is contained in:
cproudlock
2026-08-14 16:16:01 -04:00
parent 62c5454f00
commit 928a50c16e
4 changed files with 146 additions and 6 deletions

View File

@@ -166,10 +166,53 @@ Two Windows-specific notes:
using a remote MySQL needs `mysqlclient\` in its installer bundle, or the using a remote MySQL needs `mysqlclient\` in its installer bundle, or the
pre-upgrade backup is skipped. `shopdb-admin.ps1 check` reports this. pre-upgrade backup is skipped. `shopdb-admin.ps1 check` reports this.
Restoring is the standard `mysql < dump.sql`, then ### Restoring on Windows, in order
`.\shopdb-admin.ps1 restart`. Also restore `C:\shopdb-flask\instance\` if you are
rebuilding a server - it holds uploaded branding and map blueprints, which the Order matters. Restoring the database under running code that expects a
database does not. different schema is how a restore turns into a second incident.
```powershell
cd C:\shopdb-flask
# 1. Stop serving. The pool, not the whole site: other applications on this
# IIS server are unaffected.
.\shopdb-admin.ps1 stop
# 2. Restore the database. Use the dump taken closest BEFORE the problem,
# not the newest one - the newest may already contain it.
mysql -u root -p shopdb_flask < C:\ProgramData\ShopDB-Flask\backups\shopdb_flask-pre-upgrade-20260814-0730.sql
# 3. Restore instance\ if you are rebuilding a server rather than just
# reverting data. It holds uploaded branding, map blueprints, application
# images and warranty proofs - none of which are in the database.
robocopy D:\backups\instance C:\shopdb-flask\instance /MIR
# 4. Start, then prove it.
.\shopdb-admin.ps1 start
.\shopdb-admin.ps1 check -Json
```
`check -Json` reports version, publishing method, IIS and pool state, HTTP
reachability, database reachability, Python version and installed plugins. If it
passes, the restore worked; if the version it reports is not the version you
expect, see "Rolling back a release" in [UPDATES-WINDOWS.md](UPDATES-WINDOWS.md),
because a restored database and newer code is the one combination the installer
cannot fix for you.
### What is lost if `.env` is lost
`.env` is not in the database dump, and rebuilding it is not simply retyping it:
| Value | If it is lost |
|---|---|
| `SECRET_KEY`, `JWT_SECRET_KEY` | Every issued token and session becomes invalid. Users log in again; managed API tokens must be reissued, which means every collector and GE-Enforce client needs its key replaced. Recoverable, but it is a fleet-wide job. |
| `DATABASE_URL` password | Recoverable: reset the MySQL user's password and write the new one in. |
| `MYSQL_ROOT_PASSWORD` | Recoverable through MySQL's own reset procedure, which requires stopping the server. |
| `ZABBIX_TOKEN` and similar integration tokens | Reissue at the far end. Nothing else breaks. |
So back it up with the database, to somewhere as protected as the dump - it is
ACL'd to Administrators and SYSTEM on the server for the same reason. A dump
without its `.env` restores the data and locks everyone out of it.
See [OPERATE-WINDOWS.md](OPERATE-WINDOWS.md). See [OPERATE-WINDOWS.md](OPERATE-WINDOWS.md).

View File

@@ -68,6 +68,25 @@ takes everything over HTTPS, because those PCs have no share.
credentials at all, because a screensaver and a wall display have nowhere to credentials at all, because a screensaver and a wall display have nowhere to
keep one. They are read-only and carry nothing a floor visitor should not see. keep one. They are read-only and carry nothing a floor visitor should not see.
## Where the credentials live
Three documents each named a different place, which is what happens when a
credential is described from whichever end the author was working on. Both ends
are here.
| Credential | On the server | On the PC | Rotate by |
|---|---|---|---|
| Collector key | A managed token scoped to `collector.ingest` only. `COLLECTOR_API_KEY` in `.env` also works and is simpler for a pilot, but cannot be revoked per fleet. | `HKLM:\SOFTWARE\GE\ShopDB` value `CollectorKey` | Replacing the token file the fleet reads from, so every PC picks it up on its next cycle. Never put it in the manifest - manifests sync broadly. |
| GE-Enforce fetch token | A managed service token scoped to the GE-Enforce fetch scope | `HKLM:\SOFTWARE\GE\ShopDB` value `ApiToken` | Same path as the collector key |
| Server URL | n/a | `HKLM:\SOFTWARE\GE\ShopDB` value `BaseUrl` | Setting it once at install; nothing should overwrite a bay's own value |
| Application secrets | `.env`: `SECRET_KEY`, `JWT_SECRET_KEY`, `DATABASE_URL`, integration tokens | n/a | See [BACKUP-RESTORE](BACKUP-RESTORE.md) - losing these invalidates every issued token |
Two rules behind that table. A shop-floor PC is not a trusted place, so what it
holds is scoped to exactly what it does: a collector key that leaks buys the
reader the ability to file a PC report, not to read the asset register. And a
credential is delivered, never typed - a value that has to be entered per machine
is a value that is wrong on some machine.
## What owns what ## What owns what
The question that decides most arguments: **who is allowed to change this?** The question that decides most arguments: **who is allowed to change this?**

View File

@@ -27,6 +27,47 @@ Installing IIS, if it is missing (this needs no internet):
Install-WindowsFeature -Name Web-Server -IncludeManagementTools Install-WindowsFeature -Name Web-Server -IncludeManagementTools
``` ```
### Sizing the server
Small, and the reasons are worth stating so a site does not over-provision.
ShopDB serves one plant's own staff, not the internet: a busy instance handles a
few dozen concurrent users and a fleet reporting a few hundred PCs a day, and
both are trivial loads. What actually consumes resources is MySQL and the
uploads.
| | Minimum | Comfortable | Why |
|---|---|---|---|
| Disk | 5 GB free (the installer refuses below this) | 40 GB | The application and Python are about 1 GB. The rest is MySQL, config-backup revisions, and uploads - branding, floor-plan blueprints, application installers and warranty proofs, which are the only ones that grow without bound. |
| RAM | 4 GB | 8 GB | MySQL wants most of it. The application itself is a Python worker per request. |
| CPU | 2 cores | 4 cores | Reports and the map are the heaviest reads and are cached. |
A VM is entirely normal. The one thing worth insisting on is that the database
directory and the backup directory are not on the same disk as anything you
would be sad to fill up.
### Getting the installer
The `.exe` is a release asset on the GitHub repository, not a file in it - it is
around 240 MB, which is well past what a repository accepts and well inside what
a release asset does. Take it from the release page for the version you want,
and keep it: you need the previous one to roll a release back, and you cannot
download it from your own server.
Every build is stamped with its version and ships a CycloneDX SBOM onto the
server, so `shopdb-admin.ps1 verify -Path <name>` answers "does this server
carry component X" without a network call - which is the question that arrives
on a Friday afternoon after a CVE announcement.
### If you get stuck
Raise an issue on the repository. Include the output of
`shopdb-admin.ps1 check -Json`, which reports version, IIS and pool state,
database reachability, Python version and installed plugins, and contains no
secrets - it is the single most useful thing you can attach, and it is designed
to be pasted.
---
The installer **checks all of this before it changes anything**, and it will not The installer **checks all of this before it changes anything**, and it will not
let you continue until the check passes. You do not have to get it right first let you continue until the check passes. You do not have to get it right first
time. time.

View File

@@ -83,8 +83,45 @@ Installing an older build over a newer one is blocked outright. Once migrations
have moved the schema forward, older code cannot read it, and the failures are have moved the schema forward, older code cannot read it, and the failures are
difficult to unpick. difficult to unpick.
To go back you restore a backup taken before the update. Keep the previous Keep the previous `.exe` until you are satisfied with a release. You cannot
`.exe` until you are satisfied with a release. download it again from the server, and it is what a rollback needs.
### Rolling back a release
Rolling back is restoring a matched pair: the code AND the database it expects.
Doing one without the other is the state the installer refuses to create, and it
is worth understanding why - newer code against an older schema fails at the
first query that touches a new column, while older code against a newer schema
often appears to work until something silently misreads a column that changed
meaning.
The update takes its own backup before touching the schema, so the pair you need
already exists.
```powershell
cd C:\shopdb-flask
.\shopdb-admin.ps1 stop
# 1. Database first, from the pre-upgrade dump the update took.
mysql -u root -p shopdb_flask < C:\ProgramData\ShopDB-Flask\backups\shopdb_flask-pre-upgrade-<stamp>.sql
# 2. Then the code: run the PREVIOUS installer .exe. It is an install over a
# restored older schema, which is the ordinary case, not a downgrade.
.\ShopDBFlask_Installer_<previous-version>.exe
# 3. Prove the pair matches.
.\shopdb-admin.ps1 check -Json
```
If a migration failed mid-update, the installer has already restored its own
backup and there is nothing to roll back - read the log it names and fix
forward. Rollback is for a release that installed cleanly and then behaved
badly, which is a different situation and the only one this procedure is for.
**Uploaded files are not in the dump.** If the release you are leaving behind
accepted uploads, restore `instance\` from your own backup as well, or those
files stay while the rows referring to them do not. See
[BACKUP-RESTORE.md](BACKUP-RESTORE.md).
## Will an update affect other sites on the same IIS server? ## Will an update affect other sites on the same IIS server?