docs(series4): pin down what the ACH config's volatile field is NOT

Followed up the write[118:120] field flagged in the previous commit.  A third
sample plus a brute-force sweep rules out most of the obvious explanations.

Samples: 43 23 at 00:50 (idle), still 43 23 at 01:16 twenty-six minutes later,
then 2e 5e after a config write.  Thor echoes back whatever it last read --
including a value that no longer matches the config it is sending -- and the
write is accepted regardless.

Ruled out:
  * a clock or timer  -- identical across 26 minutes of idle; only a write moved it
  * a counter         -- it decreased, 17187 -> 11870
  * computed by Thor  -- Thor demonstrably sends a stale value
  * a standard CRC16  -- swept all 65,536 polynomials x init {0x0000,0xFFFF} x all
                         four reflection combinations over four candidate regions.
                         No match.  Recorded so the sweep is not repeated.

It behaves like a unit-computed hash: a one-byte input change scattered the output
(XOR 0x6D7D) where a sum would move by 1.  But two samples cannot separate that
from a nonce regenerated per write.

Operationally it does not matter, which is the point: the unit does not validate
the field on input, so read-modify-write with the rest of the block echoed
verbatim is provably safe.  Never synthesise or zero it.

Notes what would resolve it -- several config writes with times recorded, cheap to
collect during any future ACH capture.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ru8Lg9HkkYvX9VWWo65SmL
This commit is contained in:
2026-09-25 01:56:21 -04:00
co-authored by Claude Opus 5
parent 8e37803d40
commit 96a8831472
+39 -6
View File
@@ -1432,12 +1432,45 @@ bit 2 set as well in both states. Do not test for `0x01`/`0x00`.
`write[6:]` holds the dial string, null-padded — `"RADIO RING"` on this unit,
matching Series III's `raw[6:46]`.
⚠ **`write[118:120]` changed on its own between the two sessions** (`43 23` →
`2e 5e`) without the operator touching anything. Thor reads it and writes back
whatever it read. Its meaning is unknown — plausibly a counter, timer or
session value. **Round-trip this field; never synthesise it.** `write[117]` is
`0x03`, which lines up with Series III's `num_retries` of 3, and `write[120:122]`
is `00 3c` = 60.
#### ⚠ `write[118:120]` — a field the UNIT maintains
Three samples:
| when | enable | field |
|---|---|---|
| 00:50, idle (schedule session) | `0x04` | `43 23` |
| 01:16, ACH read #1 | `0x04` | `43 23` — **unchanged after 26 min** |
| 01:16, ACH read #2, after a config write | `0x05` | `2e 5e` |
Thor **echoes back whatever it last read**, including a value that no longer
matches the config it is sending — and the write is accepted anyway.
What that rules out:
- **Not a clock or timer** — identical across 26 minutes of idle. Only a config
write moved it.
- **Not a counter** — it went *down*, 17187 → 11870.
- **Not computed by Thor** — Thor demonstrably sends a stale value.
- **Not a standard CRC16.** Brute-forced all 65,536 polynomials × init
{`0x0000`, `0xFFFF`} × all four reflection combinations, over four candidate
regions (`[0:118]`, `[0:118]+[120:]`, `[5:118]`, and the payload with the field
zeroed). **No match.** Recorded so nobody repeats the sweep.
It behaves like a hash or checksum the unit computes over the stored config: a
one-byte input change (`0x04`→`0x05`) scattered the output completely, XOR
`0x6D7D`, where a sum would have moved by 1. But two samples cannot separate
that from a nonce regenerated on each write.
✅ **Operationally this does not matter.** The unit does not validate the field
on input — Thor proved that twice in one capture. **Read the config, change your
field, send everything else back byte-for-byte.** Never synthesise or zero it.
Same read-modify-write shape as the setup block.
Resolving it properly needs more samples: several config writes with the times
noted, which is cheap to collect during any future ACH capture.
Neighbouring bytes: `write[117]` is `0x03`, lining up with Series III's
`num_retries` of 3, and `write[120:122]` is `00 3c` = 60.
⚠ Beyond the enable byte and the dial string, **the field map is not
established** — only one setting was varied. Series III's offsets are a starting