Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ The teaching implementation supports domains from $2^4$ through $2^{16}$. Its ps
4. **Collusion** — deliberately give both keys to one server and watch α become visible inside that server’s view. With collusion off, the page states the one-key case as a count: how many leaves the single expanded share lights, which is what “no distinguished target” means.
5. **Tampering** — flip one bit in a server answer. Every privacy condition still passes, but the client retrieves the wrong record because this PIR does not authenticate answers. The page names the damage from that run: how many bytes of the record differ, and that reconstruction raised nothing.

Every verdict the page renders carries a `data-verdict` marker and turns on a value the page computed. None of them is a fixed string.
Every verdict the page renders carries a `data-verdict` marker and turns on a value the page computed, and every number it renders **in a result region** carries a `data-claim` marker with the measurement behind the words in `data-value`. One figure sits outside that rule on purpose: the elapsed time in `#fetch-status`, which reports how long this device took rather than anything the protocol determines, and which `verdict-scan.ts` excludes by scanning result regions rather than status prose. The scope is written into the checker, not assumed. None of the marked claims is a fixed string, and the key-size meter states the parts it measured for the domain actually selected, not the default one.

## When to Use It

Expand Down Expand Up @@ -73,13 +73,23 @@ npm run test:a11y

## Build & Verify

The repository has **45 automated checks**: 24 Vitest unit/property tests and 21 Playwright browser tests. Unit tests cover every supported domain size, independent point-vector reconstruction, single-point versus full-domain evaluation, strict serialization, server API isolation, progressive and synchronous PIR reconstruction, the measured fold count each server's own share produces, the single-key view, record widths beyond one AES block, malformed inputs, collusion, and answer tampering. Current measured core coverage is 95% statements, 96.51% lines, 97.77% functions, and 88.88% branches.
The repository has **47 automated checks**: 24 Vitest unit/property tests and 23 Playwright browser tests. Unit tests cover every supported domain size, independent point-vector reconstruction, single-point versus full-domain evaluation, strict serialization, server API isolation, progressive and synchronous PIR reconstruction, the measured fold count each server's own share produces, the single-key view, record widths beyond one AES block, malformed inputs, collusion, and answer tampering. Current measured core coverage is 95% statements, 96.51% lines, 97.77% functions, and 88.88% branches.

There are **0 published known-answer tests** for this exact randomized BGI tree-DPF variant: a search of the primary paper and appendices found proofs and algorithms but no fixed seed/key/output vectors. No vectors were invented. Correctness instead uses randomized properties at every supported domain size plus an independent naive point vector compared leaf by leaf.

The Playwright suite builds before serving and checks the production bundle on the fleet-unique port `4698`. It includes rendered mathematical claims, honest/tampered/colluding flows, retirement and no-op behavior, `[hidden]` containment, desktop/mobile reflow, axe WCAG 2.1 A/AA, arithmetic text contrast, and per-side non-text control contrast.

A separate **verdict coverage** gate walks the page through every state that can render an outcome and derives coverage from the DOM rather than from a list. It fails when a `data-verdict` marker has no mutation recorded in `e2e/verdict-mutations.json`, when a registered mutation names a verdict the page never renders, when a marker renders without a status, and when verdict wording or verdict styling appears outside a marker — the raw banner a careless builder adds later. It also injects such a banner and asserts the scan catches it, so the scan cannot pass by being blind. Each entry in that file names a mutation that has been applied and watched to turn its own assertion red, with the unmutated baseline passing in the same run.
A separate **verdict coverage** gate walks the page through every state that can render an outcome and derives coverage from the DOM rather than from a list. The walk visits every option of every control that changes what renders — each control on its own, never the cross-product — because that walk is the denominator both coverage rules and both stray scans enumerate over: anything reachable only at a setting it never visits is outside the set they judge.

It fails when a `data-verdict` verdict or a `data-claim` measurement has no mutation recorded in `e2e/verdict-mutations.json`, when a registered mutation names a marker the page never renders, when a marker renders without a status or without a numeric value, and when verdict wording, verdict styling or a rendered measurement appears outside a marker — the raw banner, or the stray number, a careless builder adds later. It injects both and asserts the scans catch them, so neither scan can pass by being blind.

Every recorded mutation must also be killed through `expectVerdict`/`expectClaim`, and that is asserted over the run rather than over the spec's source. Those helpers assert a marker's words, its machine-readable state or value, and its painted plate in one call — the plate **derived** from the asserted outcome, never supplied beside it, because a reader is never shown an alarm outcome on the success plate. Text alone is not an assertion of state: a mutation that pinned the record verdict's plate green while its words read RETRIEVED — AND WRONG used to pass the whole suite.

Each helper appends the `(spec, test, marker)` triple of every call it executes to a run-scoped ledger, cleared once per run, and `e2e/verdict-ledger.spec.ts` reads those back and fails when a record's triple is not among them. It is its own Playwright project with `dependencies:` on the specs that write the ledger, because Playwright runs tests in separate worker processes and a module-level set cannot aggregate across them. `npm run test:verdicts` runs that project, and its dependency with it.

The rule this replaces was a regex over spec source naming a FILE, and a regex over source cannot tell a live assertion from one commented out, from one in an unrelated test elsewhere in the same file, or from one handed the page's own values. All three shipped a live mutation green here. Each record therefore also declares the outcome and one text fragment its killing assertion must require, which is what an expectation lifted out of the marker it judges fails against: on an unmutated tree a tautological expectation and an honest one are the same values, so only something decided before the run can separate them.

Each entry in that file names a mutation that has been applied and watched to turn its own assertion red, with the unmutated baseline passing in the same run.

That gate is its own job in `deploy.yml` and a required check on `main`, and both `deploy` and `dependabot-auto-merge` declare `needs: [build, verdict-coverage]`, so nothing ships past it. GitHub Pages deploys only after the unit, build, browser, and verdict-coverage gates pass.

Expand Down
Loading
Loading