docs(deploy): QA pack hash-chain enablement uses the #139 cutoff (52da02a7, no migration)

Section 7: the enablement needs a build containing 52da02a7 (3a59c3f6 would
still chain the backlog); keep ChainBacklog unset; verify the anchor and that
no pre-cutoff row is chained; enablement-only rollback.

Claude-Session: https://claude.ai/code/session_01GT67ACmpMR7b9LGRPLijMi
This commit is contained in:
Joseph Doherty
2026-11-13 03:15:49 -05:00
parent 52da02a76a
commit 3badd1f9fe
@@ -258,9 +258,18 @@ Run them from the Mac over HTTP, SQL, SMB and the CLI. No ad-hoc WinRM, except t
- **In this window: the chain stays OFF on QA** (`AuditLog:HashChain:Enabled` is unset, so false). The migration only adds the columns, tables and roles.
- **After the window, as a separate step:**
1. SourceNode backfill first, because a chained row's `SourceNode` is frozen. Run `scadabridge audit backfill-source-node --before 2026-07-08T00:00:00Z --sentinel unknown`, which covers the 843 pre-M5.6 rows (Admin; idempotent). Then confirm that `preflight.sql` §6 shows 0 NULLs.
2. Then set `AuditLog:HashChain:Enabled=true` on both centrals and restart them **together**.
3. **"No backlog chaining" (only rows written after enablement): ⚠ BLOCKED, #139.** On `main` the chaining job links *every* unchained row, oldest first (`AuditLogHashChain.cs:102`), and has no cutoff option. Enabling it on QA as the code stands would chain all 3,441+ historical rows, which is the opposite of the decision. Wait for #139 (a cutoff option, or a revised decision) before step 2.
0. **Build: needs the #139 cutoff.** "No backlog chaining" is implemented on `main` from `52da02a7` (#139): the first chaining pass records a chain-start anchor at the database's `SYSUTCDATETIME()` and only rows dated from then on are chained. **`3a59c3f6` does not have it**: enabling the chain on `3a59c3f6` would chain all 3,441+ historical rows. So either run this window at a tip that contains `52da02a7` (it adds **no migration**: the script in §2.2 is unchanged, re-check only the option delta), or ship such a build before step 2. Confirm with `Host.dll` ProductVersion.
1. SourceNode backfill first, because a chained row's `SourceNode` is frozen. Run `scadabridge audit backfill-source-node --before 2026-07-08T00:00:00Z --sentinel unknown`, which covers the 843 pre-M5.6 rows (Admin; idempotent). Then confirm that `preflight.sql` §6 shows 0 NULLs. (With the cutoff the backlog is never chained, so its `SourceNode` is never frozen: the order no longer matters, but running it first keeps the documented #107 sequence.)
2. Then set `AuditLog:HashChain:Enabled=true` on both centrals and restart them **together**. **Do not set `AuditLog:HashChain:ChainBacklog`** (default `false` = no backlog chaining, the owner decision). It is only read on the very first enablement; the first pass fixes the cutoff in the database and both nodes use it from then on.
3. **Verify the cutoff** (direct SQL, read-only, a minute after start):
```sql
SELECT ChainMonth, PurgedAtUtc AS ChainFromUtc FROM dbo.AuditLogChainTombstone WHERE ChainSeq = 0; -- exactly 1 row, at about the restart time
SELECT COUNT_BIG(*) FROM dbo.AuditLog WHERE ChainSeq IS NOT NULL
AND OccurredAtUtc < (SELECT PurgedAtUtc FROM dbo.AuditLogChainTombstone WHERE ChainSeq = 0); -- 0: no backlog row chained
SELECT COUNT_BIG(*) FROM dbo.AuditLog WHERE ChainSeq IS NULL; -- about the 3,441+ backlog, unchanged
```
Then `scadabridge audit verify-chain --month <current YYYY-MM> --format table`: `Intact`, `Chain from` = the anchor time, `Pre-cutoff rows` = this month's backlog, `Unchained rows` 0 (or a minute's worth). An earlier month shows `NotChained` with its rows as `Pre-cutoff rows`. Exit code 0 throughout.
4. **Rollback of the enablement only:** unset `Enabled` and restart. The anchor and any chained rows stay (harmless; chaining resumes from them if re-enabled). Do not delete the anchor row: that is exactly what `verify-chain` reports as tampering.
- **Head export: File sink to a UNC share** that the DB login cannot write. The service account gets create + append-data only. To add once the share is named, with `HashChain:Enabled`:
```jsonc
"AuditLog": { "HashChain": { "HeadExport": {
@@ -304,4 +313,4 @@ Run them from the Mac over HTTP, SQL, SMB and the CLI. No ad-hoc WinRM, except t
- **#131:** QA has **nothing** to change (deploy-prep `2026-11-12-gates-52-131`).
- **#52:** `CvdReactor.ProcessRecipeDownload` on QA still addresses `Children["DelmiaRecv"]`. The fix (`2026-11-12-gates-52-131/apply.sh apply 52`) is binary-independent, but it **redeploys Z28061–Z28069**, including **Z28061**. **Not bundled by default.** If the owner agrees a Z28061 slot in the same go, run it after G12, so there is still only one redeploy.
- **#139 (new):** hash-chain "no backlog" gate, §7.
- **#139:** hash-chain "no backlog" cutoff, fixed on `main` at `52da02a7` (no migration). The enablement steps in §7 need a build that contains it.