L1: the integrity check reads the scroll (tablet 38)

L1Reader.verify_integrity/0 read the hash stored in integrity.sha256
and compared that string with the hash compiled into the module. It
never opened a verse. On that comparison of a constant with its own
copy rested `l1_hash: pass`, the Cloud's "L1 corrupted — the cloud is
not visible", and Construction §1.2. A changed verse would have passed
every one of them. Every existing test passed too, because every test
asked about an untouched L1.

- L1Reader.compute_tree_hash/1: SHA-256 over every file under torah/ —
  relative path, then bytes — in the order scripts/build_l1.py hashed
  them (directories by path, files by name, plain string order). On the
  real L1 it yields the pinned 156b1d27…; for a tampered copy it yields
  the same hash the Python implementation does.
- VavPipeline.L1Integrity (new, supervised): reads and hashes the scroll
  when the pipeline starts (~0.9 s in the image) and again every ten
  minutes; logs and signals the assembly when the answer changes.
- verify_integrity/0 now requires BOTH: the recorded hash is the pinned
  one, AND the scroll on disk hashes to it (answered from the witness's
  last reading; computed on the spot where the witness is not running).
  Callers are unchanged — the Shema context, the health check and the
  status endpoint all inherit the real check.
- scripts/verify_l1.py: the same reading by hand. KOHEN.md and
  KOHEN_PROMPT.md used the hollow comparison as their L1 gate; they now
  run this.
- test/tablet38_l1_integrity_test.exs — 9 tests: the hash and its
  order; one byte / one added file / one rename each change it; the
  witness; and the whole path — with an altered scroll and an untouched
  integrity.sha256, verify_integrity fails, health says l1_hash: fail,
  and /peah/ask does not answer.

Specs: architecture §2.2 (which had been made to confess the hollow
check in d7520cd) and Construction §1.2 now describe the real one — and
its limit: a change is noticed within ten minutes, not at the instant.
deploy/ARCHITECTURE.md likewise. Doorpost @expected_hash 7e5286b9… →
3f6d47a0…, in this commit, with the witnessed files that moved it
(architecture, vayakhel).

Read before committing: Elixir 483/0 on seeds 0 7 42 99 191243. The
built image, run twice: on the engraved scroll — "read and matches
(928ms)", l1_hash pass, /peah/ask 200; on a copy with ONE LETTER changed
in Bereishit 1:1 and integrity.sha256 untouched — L1_INTEGRITY_FAILED,
l1_hash fail, cloud not readable, /peah/ask 503.

Not done: the Haskell side. Luchot.Firmware.l1ExpectedHash is compared
with nothing; the engine reads L1 without hashing it (that would need a
SHA-256 dependency). The engine is reached only through the pipeline,
which now gates every cycle on the real check.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Scott KarpandClaude Fable 5.1 committed 2026-10-04 19:37:40 +00:00
1 parent d7520cd69d
commit bbe2c4b7ba
11 files changed
+579 -17

No files matched your search

+12 -5
View File
@@ -79,16 +79,23 @@ Exact versions are fixed in Construction §1.1; these are the ones built.
| Layer | Technology | Purpose |
|-------|-----------|---------|
| L0 — Luchot | Compiled Haskell constants (`Luchot.Firmware`) | Immutable firmware baked into binary |
| L1 — Torah scroll | Baked into every image at `/mnt/torah`, owned by root with no write bit; its recorded SHA-256 is compared with the pinned hash before every cycle | Fixed specification |
| L1 — Torah scroll | Baked into every image at `/mnt/torah`, owned by root with no write bit; read and SHA-256-hashed at startup and every ten minutes, and no cycle runs on a scroll that does not match its pinned hash | Fixed specification |
| L2 — Urim v'Tumim | A process in the Vav Pipeline (`UrimVTumim`). Not yet exposed over HTTP and not yet called by the cycle (Step 24) | Decision oracle interface |
| L3 — Oral tradition | PostgreSQL 17, managed; append-only by `REVOKE UPDATE, DELETE` from the application role | Accumulated learning |
L1 is read-only by ownership, not by mount: the service user can read the
scroll and cannot change it. It is not a squashfs image and the
container's root filesystem is not mounted read-only. The integrity check
compares the hash recorded in `integrity.sha256` with the hash compiled
into the pipeline; it does not re-hash the verse files, so it witnesses
which L1 was built into the image, not that the files are unaltered since.
container's root filesystem is not mounted read-only.
Its integrity is witnessed by reading it. A process in the pipeline
(`L1Integrity`) hashes every file under `torah/` — path and bytes, in the
order the builder hashed them — when the pipeline starts and again every
ten minutes. Every cycle and every health check is answered from the last
reading: if the scroll does not hash to the value compiled into the
pipeline, the cloud is not visible, no cycle runs, and the vessel reports
itself not ready. A change is therefore noticed within ten minutes, not
at the instant it is made. `scripts/verify_l1.py` does the same reading by
hand.
### 2.3 Infrastructure
+2 -2
View File
@@ -43,7 +43,7 @@ The specification that sits inside the aron must be specified with the same prec
| **Digital edition** | Westminster Leningrad Codex (WLC) 4.22 |
| **Source** | Sefaria Export (text, cantillation, vowels) cross-validated against TANACH.us WLC data |
| **Format** | JSON per verse: `{ "ref": "Genesis 1:1", "hebrew": "...", "consonantal": "...", "cantillation": [...], "ketiv_qere": [...] }` |
| **Integrity** | SHA-256 hash of complete L1 filesystem image, recorded at build time and verified at every mount |
| **Integrity** | SHA-256 over every file of the L1 tree (path, then bytes), recorded at build time in `integrity.sha256`. The pipeline re-reads and re-hashes the tree at startup and every ten minutes |
| **Cantillation marks** | Unicode cantillation codepoints (U+0591–U+05AF) preserved as raw marks, NOT pre-parsed |
| **Ketiv/qere** | Derived from WLC ketiv/qere annotations; stored as `{ "position": [book, ch, vs, word], "ketiv": "...", "qere": "..." }` |
| **Gematria index** | Computed once from the consonantal text at build time, stored as lookup table, verified against independent computation |
@@ -59,7 +59,7 @@ The specification that sits inside the aron must be specified with the same prec
7. Verify SHA-256 matches at every system boot
8. Record the hash in L0 (compiled Haskell constant) — the luchot carry the fingerprint of the scroll
If the hash does not match, the system does not start. The specification's integrity is verified before the system opens its eyes.
If the scroll does not hash to the pinned value, the system does not serve: no cycle runs and the vessel reports itself not ready. The specification's integrity is verified before the system opens its eyes, and again every ten minutes while they are open.
### 1.3 Haskell Dependencies (Exact)