mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
docs(nip-fi): make the conformance oracles derivable
Two of the seven enumeration checks were red on the conforming document. Check 7 compared a set of rows against a set of symbols, and two core-attributed rows name no symbol, so it could not pass at any head. Check 5's head clause admitted the one row its own qualifier excluded. A check that is red on a conforming document detects nothing: it cannot be observed to flip, so every mutant reads as caught. Both are corrected and the requirement to run the checks green before scoring a mutant is now written where the checks live. Address the allowlist and the anonymity set by symbol instead of row number. This table is required to grow, and a profile appending one row renumbers the positional references: the allowlist would name the wrong condition and a new denied condition would fall outside the byte-identity requirement without any check firing. State the three readings the compared object left to convention. An independently written conforming implementation diverges on each by language default: a canonicalizing HTTP library emits Www-Authenticate, a server that sets no Content-Length frames the body as chunked, and a JSON encoder spaces its separators. Header names match case-insensitively per RFC 9110 Section 5.1, the compared body is the content per Section 6.4, and the Nostr message is compact JSON per NIP-01's serialization rules. Content-Length stays unpinned; framing is the sender's choice and pinning it would widen the object for no privacy gain. An exit test whose result depends on an unstated convention is the same defect as an oracle no pair can satisfy, one layer down. Co-authored-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz> Signed-off-by: Dawn <c6237ef84fa537c78dcee78efd2d4e59f728859c7f194da42ac51ededfa0be05@buzz.block.builderlab.xyz>
This commit is contained in:
+49
-20
@@ -121,47 +121,59 @@ The names in the private-condition column are fixture identifiers for this
|
||||
enumeration. Six of them — `key_mismatch`, `attestation_required`,
|
||||
`binding_conflict`, `pair_retired`, `key_revoked`, and `binding_required` — are
|
||||
the symbols core's preparation pseudocode denies by name, and that list MUST
|
||||
equal core's set exactly. Rows 8 and 14, `policy_denied` and
|
||||
`dependency_unreadable`, are core's conditions expressed only in prose — a bare
|
||||
policy denial and `FI-INV-14` fail-closed — and core is not required to name
|
||||
equal core's set exactly. `policy_denied` and `dependency_unreadable` are core's
|
||||
conditions expressed only in prose — a bare policy denial and `FI-INV-14`
|
||||
fail-closed — and core is not required to name
|
||||
them symbolically; they are the only two core rows so exempted. The remaining
|
||||
rows name conditions the profiles define in prose. None is a wire value, and a
|
||||
deployment MAY use different private reason codes internally as long as every
|
||||
enumerated condition has a fixture.
|
||||
|
||||
Rows 3–13 are the private-state anonymity set. Their public responses MUST
|
||||
compare byte-identical to each other, not merely equal in prefix or status.
|
||||
Every row whose public class is `authorization_denied` is in the private-state
|
||||
anonymity set. Their public responses MUST compare byte-identical to each other,
|
||||
not merely equal in prefix or status.
|
||||
Rows for an unclaimed profile are `not-applicable` with absence evidence. A
|
||||
profile that introduces a new private condition MUST add its row; an
|
||||
unenumerated condition escapes this oracle entirely.
|
||||
|
||||
The anonymity comparison is **wider than the interoperability compared object
|
||||
defined below, and deliberately so**. Between two private conditions on the same
|
||||
implementation, every response byte MUST agree except values a server cannot
|
||||
implementation, every response byte as transmitted MUST agree — including
|
||||
transfer framing, and not only the content — except values a server cannot
|
||||
hold constant across two instants, such as `Date`. It is not limited to the
|
||||
header fields core names. The narrower object below exists because two
|
||||
*different* implementations cannot be required to agree on fields core does not
|
||||
pin; that reasoning does not apply within one implementation, where any field
|
||||
varying by private condition is a disclosure whatever its name. A suite that
|
||||
reuses the interoperability object here would pass an implementation that
|
||||
returns its private reason code in an unnamed header.
|
||||
returns its private reason code in an unnamed header, or one that varies its
|
||||
chunk boundaries by private condition.
|
||||
|
||||
**Enumeration agreement.** The preceding paragraph makes a quantified claim
|
||||
about this table, and a fix verified against the one row it changes can still
|
||||
falsify it. Rows 8 and 14 are the **prose-only allowlist**: core's conditions
|
||||
that core is not required to name symbolically. The suite MUST check,
|
||||
mechanically at the claimed head (`FI-CONF-DENIAL-FIXTURES`):
|
||||
falsify it. `policy_denied` and `dependency_unreadable` are the **prose-only
|
||||
allowlist**: core's conditions that core is not required to name symbolically.
|
||||
Both sets here are named by symbol, never by row number, because this table is
|
||||
required to grow and every positional reference silently retargets when it does.
|
||||
The suite MUST check, mechanically at the claimed head
|
||||
(`FI-CONF-DENIAL-FIXTURES`):
|
||||
|
||||
1. every symbol core denies by name has a row here;
|
||||
2. every symbol core denies by name is attributed to core;
|
||||
3. the naming paragraph above lists exactly the symbols core denies by name;
|
||||
4. the count word in that paragraph equals the number of symbols it lists;
|
||||
5. every core-attributed row that is a named symbol carries the same public
|
||||
class — quantified over core's symbolic set, not over all core-attributed
|
||||
rows, since `dependency_unreadable` is correctly `authorization_unavailable`;
|
||||
5. every symbol core denies by name carries the same public class — quantified
|
||||
over core's symbolic set, not over all core-attributed rows, since
|
||||
`dependency_unreadable` is core-attributed and correctly
|
||||
`authorization_unavailable`;
|
||||
6. no allowlist entry appears in core's symbolic denial set; and
|
||||
7. the set of core-attributed rows equals core's symbolic denial set together
|
||||
with the allowlist, exactly.
|
||||
7. the set of symbols named by core-attributed rows equals core's symbolic
|
||||
denial set together with the allowlist, exactly.
|
||||
|
||||
Every check above MUST be run against the unmutated document and be green before
|
||||
any mutant is scored. A check that is red on a conforming document detects
|
||||
nothing: it cannot be observed to flip, so every mutant reads as caught. Two of
|
||||
these checks shipped red for exactly that reason.
|
||||
|
||||
Checks 3 and 4 are independent and neither implies the other: an editor who
|
||||
corrects the count without the names is caught by 3, and one who corrects the
|
||||
@@ -183,11 +195,28 @@ two implementations. The anonymity comparison above is wider. Byte-identity is
|
||||
asserted over the response bytes an implementation chooses, which excludes bytes
|
||||
a conforming HTTP server cannot hold constant. Over Nostr the compared object is
|
||||
the complete relay message excluding only the event or subscription identifier
|
||||
echoed from the request. Over HTTP the compared object is exactly what NIP-FI
|
||||
core pins: the status code, the complete body, and the exact values of only the
|
||||
header fields core's denial table names. Header order and unnamed header fields
|
||||
are outside it, and their values MUST NOT depend on the private condition —
|
||||
which the anonymity requirement above already demands and tests directly.
|
||||
echoed from the request, encoded as compact JSON with no insignificant
|
||||
whitespace, per NIP-01's serialization rules. Over HTTP the compared object is
|
||||
exactly what NIP-FI core pins: the status code, the complete body, and the exact
|
||||
values of only the header fields core's denial table names. Header field *names*
|
||||
are matched case-insensitively per RFC 9110 Section 5.1; their values are
|
||||
compared exactly. The compared body is the *content* per RFC 9110 Section 6.4 —
|
||||
after transfer-decoding, chunk framing and trailer fields excluded — not the
|
||||
message body on the wire. That reading is scoped to this interoperability object
|
||||
and does not reach the anonymity comparison above, which stays over transmitted
|
||||
octets. `Content-Length` is deliberately not pinned: framing is the sender's
|
||||
choice and pinning it would widen the object for no privacy gain.
|
||||
Header order and unnamed header fields are outside it, and their values MUST NOT
|
||||
depend on the private condition — which the anonymity requirement above already
|
||||
demands and tests directly.
|
||||
|
||||
Each of these three readings is stated because an independently written
|
||||
conforming implementation diverges on it by language default, not by error: a
|
||||
canonicalizing HTTP library emits `Www-Authenticate`, a server that sets no
|
||||
`Content-Length` frames the body as chunked, and a JSON encoder inserts spaces
|
||||
after `,` and `:`. An exit test whose result depends on a convention this
|
||||
document does not state is the same defect as an oracle no pair can satisfy,
|
||||
one layer down.
|
||||
|
||||
Comparing the ordered sequence of header field names, or every header value
|
||||
except `Date`, would fail every conforming pair. Two independent servers emit
|
||||
|
||||
Reference in New Issue
Block a user