[SCITT] Re: Closing omission from the receiver's vantage — what a record must carry

Pablo Play <playplay2736@gmail.com> Fri, 21 August 2026 09:32 UTC

Return-Path: <playplay2736@gmail.com>
X-Original-To: scitt@mail2.ietf.org
Delivered-To: scitt@mail2.ietf.org
Received: from localhost (localhost [127.0.0.1]) by mail2.ietf.org (Postfix) with ESMTP id 17DDB12D44EF7 for <scitt@mail2.ietf.org>; Fri, 21 Aug 2026 02:32:50 -0700 (PDT)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ietf.org; s=ietf1; t=1787304770; bh=hHcRexyGdCg/Gaxz2zuxdH+4xuwsYQ8fw0J+CtLD9B8=; h=References:In-Reply-To:From:Date:Subject:To:Cc; b=vC6qWPQvQsOUqK7hTR+mTJdniyurfDy7hE50jMyFgcosKHNLEXRRGXXiHq/h9Nbb8 e+MBN0KQ7Uw9tl69DdMQc/lKbMTReR6/O24jsbhYKa0YMa36JrKy7k3LIp+eoUzcov ADFLRem4sM0bfaKlsgpvsCwwBLsyT3Rb1aBXlL+k=
X-Virus-Scanned: amavisd-new at ietf.org
X-Spam-Flag: NO
X-Spam-Score: 0.152
X-Spam-Level:
X-Spam-Status: No, score=0.152 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FORGED_GMAIL_RCVD=1, FREEMAIL_ENVFROM_END_DIGIT=0.25, FREEMAIL_FROM=0.001, GB_AFFORDABLE=1, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001] autolearn=no autolearn_force=no
Authentication-Results: mail2.ietf.org (amavisd-new); dkim=pass (2048-bit key) header.d=gmail.com
Received: from mail2.ietf.org ([166.84.6.31]) by localhost (mail2.ietf.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id hG65_MSGZ4se for <scitt@mail2.ietf.org>; Fri, 21 Aug 2026 02:32:47 -0700 (PDT)
Received: from mail-qv1-xf2e.google.com (mail-qv1-xf2e.google.com [IPv6:2607:f8b0:4864:20::f2e]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits) key-exchange X25519 server-signature ECDSA (P-256) server-digest SHA256) (No client certificate requested) by mail2.ietf.org (Postfix) with ESMTPS id 8B30D12D44EEE for <scitt@ietf.org>; Fri, 21 Aug 2026 02:32:47 -0700 (PDT)
Received: by mail-qv1-xf2e.google.com with SMTP id 6a1803df08f44-8efcfdb2b43so5708086d6.3 for <scitt@ietf.org>; Fri, 21 Aug 2026 02:32:47 -0700 (PDT)
ARC-Seal: i=1; a=rsa-sha256; t=1787304767; cv=none; d=google.com; s=arc-20260327; b=ox+0JeGtSbnt9p1eH6sjlH73sgMnPamazEHDMRqBPpA2M2a1Sr29aAtR9dVd/d78YO wKsvPKGzoRc2WoW+hPtmuDqHPM3uG5xY0wbJZTLV4fnimU68cmJjtjQc3/XCI+B3pBS8 461Bn9OCRftB5AuMQyEDEkbHZtrWeZb2OW2sAoHS/hdAPvhwvRGqulVgiGlrRIKjoAWD L9QFIxI2rHSlizC7GFipRezUyB0gROec7z4SY6UIy3D40Jpt6PrSp+iQotgMQddxZ0j7 Gk3K3+IA0so8rJpr/uCFLuskpUiXFHr14jYESHhdEP7CSJOrHVgU0QhKuoArLfNk3Vdg tKmw==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20260327; h=cc:to:subject:message-id:date:from:in-reply-to:references :mime-version:dkim-signature; bh=uE4Te8YBdV8ndxTWcrsM5cc0JiGyYPykPVzKdWZrnS0=; fh=0jWVM3560GfZlKB8EBvznSTmeDtGENQSsa2HUTLd0LA=; b=sf3FucIS5qYgtFkST2rWCENC/AvzUKTrbX+GW607sKhtLXZ1KZ1AzONXqeF3ZYxOFP RUP6NOBiICeOvpka6cpR1Esxw1/qWIG5xoRuiy98G+fPqVwt1zLMXF4h7o+lLN79Cva1 dbVBrlqVEeJ7DLX2uy0U6j6DVmQO3Smb33RvB+HY+PkbVcqllmvj87t4ev5LhkSX4K+V LKNnXRR8QGbBehouVEYHufF6DhzCkyjRjACaUOI4h1jDYhxvaQcjb1N3Y8aPHaVlnNGE CGjfppa/UTe8Q1LbPEmHbGjTcqBr7dtlxW47IMNqY0eu6E3xuWkiLpAv2JndHnFGit5Y RlyA==; darn=ietf.org
ARC-Authentication-Results: i=1; mx.google.com; arc=none
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1787304767; x=1787909567; darn=ietf.org; h=content-type:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:from:to:cc:subject:date:message-id:reply-to :content-type; bh=uE4Te8YBdV8ndxTWcrsM5cc0JiGyYPykPVzKdWZrnS0=; b=K/lbpZ0wzo/vtBM/Tb0lOGt3CGKs8yv2RR+RbzYJ8CedouZ4MbuAW2kwcfGDbopk5u kMIsSWz3817/lC09v3rkDVb3ZNO6GDWUXqfjXWvqija8XDjBvDZR2dMKulAFNLd/iBhx evQEVnfVOutTzxO/Gg80vCkWClSt/468nIS9b3GIPe6YZ1GHBzH8mVo43TCmbjjs5FLe 5Kjdhw7KlwBKzMg/2mT4iwQB7jprGIpnikBdUE5PY99DD8qO94bawfzNuCVNoslGiiQZ lBpMN8yDLxRdWnoosXLA4UDINvHddE73bvzeq2vtPI4PbOF1ggnFqz6XLeh37PYqwLs8 PjqQ==
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1787304767; x=1787909567; h=content-type:cc:to:subject:message-id:date:from:in-reply-to :references:mime-version:x-gm-gg:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to:content-type; bh=uE4Te8YBdV8ndxTWcrsM5cc0JiGyYPykPVzKdWZrnS0=; b=itst0KQ+UvpSpqRHXCsztAx/oK0YxYZnQ9RgA1Ws2Ncb3ejFJ0oYH9beSTEpgOTPYm WW33LVf8pe7iSvVmKCGUz+T/2FqpKSEkn33sy3CazF4fz7/LF1erTyG95kaEb7bF/rIV v3zJgb9mafyWDRbmqi5Ql1P+uQfpGH811nUOS3Tuai3OJ1mwJEt0sOnrcbvLCu0B98Jo jU7RyAiQEE26E8EMzRky1wk2ibRjycCZdC2lzKjRSNX0cs4Ob6xXpa8WBzHh5Ep8YUZA gHJyKCyn1fi87FS1Z+lZ9MrGek6siXM2R2RUlQ6PxN41iq3H+74mXFPw9ZZjuMT4MQc3 SVKQ==
X-Forwarded-Encrypted: i=1; AHgh+RqREFpVnw9Glc9YR4jnccwO7hbj3eyG3j1PJ5ASIWUx/tZDAALvgutiM4HDs9K7pN1VW18Aww==@ietf.org
X-Gm-Message-State: AFuF++lzXLnO54uq4Cwyjf9U83VtMGJYlUK/FIqSJhWBj0unR56CpbkM 8057tlpnX9Ndp06hnjUhWb3sXF5byZqpJ96MmohIgyEIc+2tfAErxG5AZR8NUqgteFf4bOTYDcm GIQcSw7pMlsNB0psSHLJaL0POluQvd9g=
X-Gm-Gg: AR+sD13n1EOqAvh5fj8/TSSVvwZP73SSISiHPr+Pz6UXx8DBrW8mya9Rfj0jhcb5PDF k8yUO5uOLv/YHbs3KzuT47y0S6l6igWW9zMzGxNRCDnqdeqTuHkR1xLQrKKkkROfmzsUcPW3YOB BMnwhbUUT401yUKM7n6KfsTmhFQraxZ3ZkXi/Bd9wUOKyEwfu53eO0bX5j2Quz8KtoHTzItepdv pYn5Ysi5qzQO0WXBhA968E7PflpGnzz1A1uoyLe2Ys+kD5S4s0IX+JOtAGUiyl7WcG42WUoa7dL veQmjpGTwBua8m1b3CTgyYs2zd6mMK4U5B2k9HV4WCVhsqmvj2KcNaFrgWdlikfqdENeBwUsbqy 5K/Wvqi+/EZuEd1GL7GcETwzc3v9TmWSqK6awIHV1Ew4Htvxws7GeCGTRVp8IoCDOO/eOC3CXBs vtKKIZcTDRivQGJO3O0oKUC7a+OAlnryvmMafzE+k9HL49lDs0XB8QB2fvkgKOgvnUHoSzGM783 rZ0qb9+
X-Received: by 2002:ad4:5d43:0:b0:8f3:ba0f:e7de with SMTP id 6a1803df08f44-90c809737b9mr41752976d6.11.1787304766675; Fri, 21 Aug 2026 02:32:46 -0700 (PDT)
MIME-Version: 1.0
References: <1787262784652143705.1787262784@conarium.dev> <CALc05oFWK2_mQEB9JLRorCS5QLc0rPrJxj3BMc0r0B82ZcH_mg@mail.gmail.com> <1787272059233193813.1787272059@conarium.dev> <CALc05oGzHYVzZjWYDFCa0FYzSTHAz4_xY0wMr80j-UbPE5-UoA@mail.gmail.com> <z7XRDPPaX3By1sxJwdNM8-FqQMDTOW43O10genKixWn3mEz5m_xkYFwdQ324tf5_ATaSPdhnIgKg-uSwdLGy9twqj8odSf1ZqeUtNpFzzKk=@vaara.io>
In-Reply-To: <z7XRDPPaX3By1sxJwdNM8-FqQMDTOW43O10genKixWn3mEz5m_xkYFwdQ324tf5_ATaSPdhnIgKg-uSwdLGy9twqj8odSf1ZqeUtNpFzzKk=@vaara.io>
From: Pablo Play <playplay2736@gmail.com>
Date: Fri, 21 Aug 2026 06:32:34 -0300
X-Gm-Features: AcwNN1WMmwtgynpUgmUs0W1YucnBEn38LM0hPrTfV_FkRMdXNEGVD778WYm1Iyk
Message-ID: <CANWAHpowbgTkTmMQ-sDs1pgZF-UAgfLxZN+DqfWLxLyMPY_E-w@mail.gmail.com>
To: Henri Sirkkavaara <hello@vaara.io>
Content-Type: multipart/alternative; boundary="000000000000b920d906598b51d0"
Message-ID-Hash: M3ALI6HO5NJ37JUYY3RD6T2QT6S3JZMF
X-Message-ID-Hash: M3ALI6HO5NJ37JUYY3RD6T2QT6S3JZMF
X-MailFrom: playplay2736@gmail.com
X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header
CC: Walter Hawkins <wdhawkins46@gmail.com>, e.dogru@conarium.dev, scitt@ietf.org, jhillier@certisyn.com, nenadvasic@protonmail.com, Todd.Gibson@t-mobile.com
X-Mailman-Version: 3.3.9rc6
Precedence: list
Subject: [SCITT] Re: Closing omission from the receiver's vantage — what a record must carry
List-Id: "Supply Chain Integrity, Transparency, and Trust" <scitt.ietf.org>
Archived-At: <https://mailarchive.ietf.org/arch/msg/scitt/LoudEdvGtL5RgnVdo0wRtIbeTW4>
List-Archive: <https://mailarchive.ietf.org/arch/browse/scitt>
List-Help: <mailto:scitt-request@ietf.org?subject=help>
List-Owner: <mailto:scitt-owner@ietf.org>
List-Post: <mailto:scitt@ietf.org>
List-Subscribe: <mailto:scitt-join@ietf.org>
List-Unsubscribe: <mailto:scitt-leave@ietf.org>

Henri, all —

  Second empirical data point under the closed-enum property, from a
different mechanism than
  Vaara's release condition but the same shape: a binary result whose
reasons already
  partition into two non-overlapping categories, documented additively
rather than widening
  the enum.

  verify_chain() in our stack returns valid: false for two structurally
different situations
  — a trail that was never reachable to evaluate, and one that was read
completely and failed
  a real check. The read path is binary by construction (single-row lookup,
no partial
  reads), so trail_not_found always runs first against the raw result, and
everything
  evaluated after it — including missing_signature_ref — is a check against
a record we
  already read in full. That gives a clean partition without touching the
valid contract:

    trail_not_found                  -> unreached
    missing_signature_ref            -> ran-and-failed
    delegation_ref_parent_mismatch   -> ran-and-failed
    cycle_detected                   -> ran-and-failed

  Merged as-is, 69 lines added, 0 removed (commit e12658d,
  github.com/giskard09/argentum-core, negotiation-ref.md).

  Same underlying rule as your blocked-but-verified receipt: a value that
resolves cleanly
  deserves its own place in the partition, not a collapse into whichever
adjacent state is
  easiest to code against.

  — Pablo

El vie, 21 ago 2026 a la(s) 2:28 a.m., Henri Sirkkavaara (hello@vaara.io)
escribió:

> Nenad, Walter, Joel, Pablo, Emek, Todd,
>
> The principal row is right and I would take it further than a row. Your
> revocation case and the receiver's settlement case are the same shape at
> two different levels, and neither party can be substituted for the other,
> which by this thread's own argument puts the vantage question in the
> substrate rather than in any one draft.
>
> On the closed-enum property, I have something to put under it rather than
> another agreement.
>
> Vaara 1.71.0 ships a release condition: money is held against a signed
> statement of what must be proved, and a receipt proving the authorised
> action happened is what releases it. Evaluation returns one of four states,
> each carrying a reason from a closed set. The mapping from reason to state
> is a table, not control flow, so no code path can file a forgery under a
> hold.
>
> The partition runs on one axis. A broken signature, a receipt under a key
> the condition does not pin, or evidence that does not resolve to the digest
> the receipt signed are failures as evidence, and they refuse. A missing
> receipt, a receipt for another action, another authorization, or one that
> soundly proves a refusal are sound and insufficient, and they hold.
> Soundness is checked before the clock, so an expired window cannot swallow
> a tampering finding.
>
> Eight vectors in tests/vectors/release_condition_v0, all four states
> present, and a checker that imports no Vaara and recomputes every verdict
> from the case bytes. Among them is a receipt that verifies correctly and
> proves the action was blocked. It must not release, and treating it as a
> plain false would put it in the same state as a forgery.
>
> DOI 10.5281/zenodo.22029444 if it is useful to cite the exact bytes.
>
> *Henri Sirkkavaara*
> *Vaara *- Runtime execution layer for AI agents
> *Built to see over the noise.*
> vaara.io
> Helsinki, Finland
>
> On Friday, August 21st, 2026 at 04:10, Walter Hawkins <
> wdhawkins46@gmail.com> wrote:
>
> Emek,
>
> You came back with the check and what it caught, so here is mine — same
> shape, a day behind you.
>
> The rule, as shipped. An anchored sequence moves a verifier's floor only
> when all of the following hold: the calldata parses and names this grant;
> the key segment equals the grant's accountant; the anchored content was
> actually retrieved; the anchor digest recomputes over the retrieved head;
> the head's signature verifies; and the head itself names this grant, this
> accountant, and this sequence. Anything short of that is ignored with a
> warning naming the reason, and a refusal can only lower the floor toward
> the tamper-evidence downgrade, never raise it. The old scan — the one that
> trusts the sequence printed in calldata — survives under an UNVERIFIED
> label for survey work, because a claimed sequence is still a lead worth
> following. It is no longer anything a verifier may refuse an honest head
> against.
>
> Shown failing before wired in, on your reasoning that a check which has
> only ever been green demonstrates nothing. Four controls:
>
> - The forged-key attack from my last message, run for real: a stranger's
> anchor claiming sequence 999999999 under the accountant's public key. The
> unverified scan reads 999999999 — poisoned exactly as I described it two
> days ago. The verified scan holds the floor at the genuine head and prints
> a warning naming the anchor it ignored.
>
> - An anchor whose retrieved head does not reproduce the anchored digest:
> no floor movement.
>
> - Calldata claiming one sequence over a head that states another: no floor
> movement.
>
> - No qualifying anchor at all: the floor is null, and null is not zero. "I
> could not derive a floor" is the labelled tamper-evidence downgrade, not a
> fact about sequence 0.
>
> What it caught immediately: my own anchor. The only anchor we have ever
> written to a chain — the Coston2 transaction at block 34259963 that I
> brought to this thread as evidence — is refused by the rule it motivated. I
> read its calldata back off the chain today: it parses, and it carries no
> key segment at all, because the grammar predates the hardening; the head it
> points to predates the accountant field. Two independent grounds, either
> one fatal to floor-eligibility. Your check's first catch was your own
> sentence; mine refused my own anchor. It stays in the record, labelled, as
> tamper evidence of the day the wiring first worked — which is exactly as
> much as the hardened rule permits it to be. The lesson I passed on last
> time, that we anchored a shape we were still editing, now has an
> enforcement mechanism instead of a caveat.
>
> Your 14-versus-15 split is in here too, arrived at separately. An anchor
> whose content cannot be fetched is ignored with "content not retrieved, so
> its sequence is unverified" — could-not-check never gets swallowed into
> failed, and never silently reads as verified. The null-rather-than-zero
> floor is the same rule one level up. I'd add both of ours to the pile Joel
> is counting: the weakest-rung rule keeps being reinvented at whatever layer
> someone is standing on, which is the strongest argument yet that it belongs
> in the substrate stated once.
>
> Two limits, stated now rather than found later.
>
> First, the verifier takes the retrieved heads as inputs; where they came
> from is outside the rule. Joel's placement rule — state where the evidence
> is obtained, not only where it is produced — lands on this with full force.
> A verifier handed heads by the executor it is auditing has re-derived at
> the second placement, and the result object should say so. Mine does not
> yet distinguish the placements; that is the next honest gap.
>
> Second, floor-eligibility depends on retrievability, so the floor's
> strength decays with content availability. An adversary who cannot forge a
> floor can still lower one by making anchored content unfetchable. That
> fails in the right direction — a downgrade rather than honest heads refused
> — but it is a downgrade an attacker can induce, and the record has to name
> it, or "no floor" reads as "nothing was ever anchored", which is a
> different claim.
>
> Walter
>
> On Thu, Aug 20, 2026 07:27 PM, e.dogru@conarium.dev wrote:
>
>> All,
>>
>> I said I would come back with the check and what it caught rather than
>> with a plan. It is written and merged; this is what it caught. Walter's
>> rung 3 question gets a measured answer at the end, because it turned out I
>> could check it rather than argue it.
>>
>> The problem is in -05 in my own words: four sentences in the
>> Implementation Status section of -04 were accurate the day they were
>> written and false within days, every one of them claiming less than the
>> code did, and nothing in the repository compared that section to the tool.
>> They were found by a reader holding the document beside the output.
>>
>> The check runs the section instead of reading it. Three declarations of
>> one fact exist: the code, the prose, and now a claim table between them,
>> because prose cannot be executed. Each behavioural sentence is bound to a
>> run of the shipped CLI over a named fixture, and two directions are
>> enforced. Every value a run produced must still appear in the sentence that
>> states it, and the number of sentences must equal the number of claims. A
>> sentence with no claim is one nothing measures. A claim with no sentence is
>> a measurement the document dropped.
>>
>> The revision it reads is derived from what is in the repository rather
>> than named in the check, because a hard-coded revision is the same class of
>> stale declaration the check exists to catch.
>>
>> I showed it failing three ways before wiring it in, since a check that
>> has only ever been green demonstrates nothing:
>>
>> a value edited out of a sentence
>> -> the sentence no longer contains observed-without-receipt
>> one bullet removed
>> -> states 4 observed behaviour(s), the claim table runs 5
>> a fixture changed so the tool's outcome moved, sentence untouched
>> -> item outcomes are ["excluded","indeterminate"],
>> the table says ["excluded","observed-without-receipt"]
>>
>> The third is the one that matters: the code moved and the sentence stood
>> still, which is what happened four times in -04.
>>
>> What it caught immediately is a sentence of mine. -05 says "The
>> implementation's test suite contains no check that compares this section
>> against what the code does." From the merge commit that is false, and a
>> posted draft cannot be edited, so it is the first correction -06 owes.
>>
>> Two limits, stated now rather than discovered later. It pins the
>> behaviour table and not the prose around it, so a paragraph can still go
>> stale in a way nothing runs. And a red has two honest fixes: change the
>> code back, or write the revision that says what the code now does. Editing
>> a posted draft is not one of them, which makes the cost of drift a document
>> rather than a diff.
>>
>> Walter, on rung 3. You put it as: an external anchor is not automatically
>> rung 3, an anchor is a claim by whoever paid for it, and a profile that
>> says "anchored" without saying "and re-derived" has described rung 2 with
>> extra steps. I went and looked rather than answering from memory, and the
>> implementation agrees with you, which I did not know for certain before you
>> wrote it.
>>
>> --anchor-check deserialises the timestamp proof, refuses a proof whose
>> digest is not the chain head, walks the attestations, and where the
>> attestation is Bitcoin it fetches that block and compares the digest
>> against the merkle root. So the re-derivation is in the path and not in the
>> prose.
>>
>> The part I would not have thought to defend, and which your framing is
>> the reason to mention: it separates two answers that a single exit code
>> would have merged. A proof that fails to verify exits 14. A proof that
>> could not be checked at all, where the block could not be fetched, exits
>> 15, and 15 is evaluated first, so "I could not check" never gets swallowed
>> by "it failed". Returning 0 there would have been the same error in the
>> other direction: the caller has to know the anchor was not verified, not
>> merely that it was not disproved. That is the weakest-rung rule applied to
>> our own tool, and it was written before anyone asked us for it, which is
>> the only reason I can say it now without it sounding convenient.
>>
>> Emek
>>
>>
>> On Fri, Aug 21, 2026 at 2:50 AM Walter Hawkins <wdhawkins46@gmail.com>
>> wrote:
>>
>> Henri, Joel, Emek, Pablo, Nenad, Todd,
>>
>> Two corrections of my own before the part I owe this thread, since that
>> appears
>> to be the going rate here and it's a good rate.
>>
>> **One.** I used the words "omission-proof" in my own notes and it was a
>> rung
>> short. Our spend log is chained and gap-free under a signed grant, and
>> the head
>> that seals it is counter-signed by a named accountant carrying a root and
>> a
>> running total. That much I had right — it's rung 2. What I had wrong was
>> the
>> floor. A verifier arriving for the first time, holding no prior state and
>> doing
>> no anchor lookup, accepts an old validly-signed head together with the
>> prefix
>> that matches it. Monotonic sequence, perfect arithmetic, short set.
>> Henri's
>> correction this morning is the same shape as mine, and I'd made mine a day
>> earlier without noticing what it cost.
>>
>> Fixed the way Emek's rule says it should be: the freshness floor is now a
>> required argument, and passing nothing is an explicit downgrade whose
>> verdict
>> is labelled tamper-evidence rather than completeness. The weakest-rung
>> rule
>> applied to an argument I had made optional.
>>
>> **Two, and this is the one I think is worth the thread's time, because
>> it's a
>> hole in rung 3 itself.**
>>
>> We put our heads on-chain: a zero-value transaction whose calldata
>> carries the
>> head digest, the grant it covers, the sequence, and the key that signed
>> it. A
>> first-contact verifier scans for those and takes the highest sequence as
>> its
>> floor. That is the external anchor the ladder asks for, and two days ago
>> I'd
>> have told you it put us at rung 3.
>>
>> Anchoring is permissionless. That property is why it resists censorship
>> and it
>> is also the attack. Anyone can write that calldata. The key field is
>> plaintext,
>> and the accountant's key is public because it's named in the grant. So a
>> stranger writes an anchor for my grant at sequence 999999999, my scanner
>> reads
>> it, and every honest head I produce afterwards is refused as a rollback.
>> The
>> floor is poisonable by anyone willing to pay for one transaction, and the
>> failure mode isn't a missed omission — it's an honest record permanently
>> refused. We had traded a false negative for a false positive against
>> ourselves,
>> which is the worse of the two trades.
>>
>> The fix is that an anchored sequence cannot be read as a fact. It is a
>> pointer.
>> A verifier deriving a floor has to fetch the anchored bytes, recompute the
>> digest over them, check the signature, and check that the head it just
>> verified
>> names this grant and this sequence — and only then let it move the floor.
>> An
>> anchor whose content can't be retrieved simply isn't floor-eligible,
>> which can
>> only lower the floor toward the tamper-evidence downgrade, never raise it.
>>
>> Stated honestly about where we are with it: that rule is written into our
>> spec
>> text and our scanner does not yet do it. What the scanner does today is
>> the
>> cheap half — it ignores anchors that don't name the grant's accountant,
>> which
>> closes the stranger-with-no-key case and leaves the forged-key case open,
>> because that field is unauthenticated. So take the paragraph above as a
>> claim
>> about what rung 3 requires, not a claim about what I've shipped. The
>> re-derivation is the next thing I write, and I'll come back with what it
>> caught.
>>
>> So for the statement I'd put it this way: an external anchor is not
>> automatically rung 3. An anchor is a claim by whoever paid for it. Rung 3
>> is
>> an anchor plus the verification that recovers what it points at, and a
>> profile
>> that says "anchored" without saying "and re-derived" has described rung 2
>> with
>> extra steps. Nenad, I think this bears directly on precedence — ordering a
>> revocation strictly against a settlement only means something if both
>> anchored
>> objects are re-derived first; otherwise you've ordered two assertions.
>>
>> **What I owe: the rail enumeration.**
>>
>> Every payment under a grant carries a binding in the one payer-chosen
>> slot the
>> rail's own signature covers — the EIP-3009 nonce, the Permit2 nonce, the
>> XRPL
>> InvoiceID. The value is a domain-separated digest over the grant digest
>> and the
>> payment identifier, so it's derivable by anyone holding those two and
>> recomputable from the settled transaction by anyone holding that.
>>
>> The receiver's use is the part this thread wants. A payee holding a
>> settled
>> transaction recovers which grant and which payment it commits to from the
>> artefact itself, without asking the executor which authorisation it would
>> like
>> to be judged under. And the enumeration runs off the rail rather than off
>> a
>> list: the payee walks settled transactions to their own address, not a
>> set the
>> executor hands them. A leg the executor never wrote down is still on the
>> rail
>> if the money moved, and the party holding it is the party who cannot be
>> made to
>> un-know it arrived.
>>
>> Testnet EVM rail, as promised: Coston2. A head anchored as a real
>> transaction at
>> block 34259963, the calldata read back off the chain and matched against
>> the
>> signed head, block timestamp doing the ordering.
>>
>> With a caveat that belongs in this thread more than most. That anchor
>> predates
>> the hardening I described above — its commitment has no accountant field
>> and its
>> calldata has no key segment, so it verifies against the code as of the
>> commit
>> that produced it and not against current HEAD. I keep it as evidence that
>> the
>> wiring worked on the day, and as a small lesson I'd pass on: we anchored
>> a shape
>> we were still editing, and permanence is the one property an anchor has
>> whether
>> or not you were finished. New heads use the hardened shape. I'll bring
>> both,
>> labelled.
>>
>> Two limits I'd rather state than have found.
>>
>> The rail has to enforce slot uniqueness for once-per-payment to come free.
>> EIP-3009 and Permit2 consume the nonce, so it does. XRPL does not enforce
>> InvoiceID uniqueness — two Payments carrying the same InvoiceID both
>> settle —
>> so anyone deriving a cumulative total from XRPL evidence has to
>> de-duplicate by
>> (grant, payment) and cannot lean on the rail for it.
>>
>> The second one I had wrong until a review pulled it apart yesterday, and
>> it
>> generalises past payments. The binding identifies the grant; it does not
>> bound
>> the amount. The slot commits to the grant and the payment identifier and
>> nothing else, so an agent holding the payer key can settle a million
>> against a
>> payment it reports as one. The cap only binds if the verifier reads
>> amount,
>> recipient and asset out of the settled transaction and compares them back
>> to
>> the grant. I had a document claiming one artefact proved both that money
>> moved
>> and that it moved within authority, and that sentence was doing work the
>> mechanism didn't do. Identifying which authorisation governed an act is
>> not
>> the same as bounding the act performed under it — Nenad, that's your item
>> 2
>> from the other end, and I think it's a second thing the substrate should
>> say
>> outright, because the failure is invisible and every incentive points at
>> it.
>>
>> The object model and its vectors are public as of today, as an open PR
>> rather
>> than anything settled: specs/extensions/authority.md and
>> authority-vectors.json in x402-foundation/x402 #3220. If the statement
>> cites
>> components normatively the way Henri suggests, that's the published home
>> for
>> the rail-binding piece, under the same one-authority rule Nenad named.
>>
>> **Nenad's fourth vantage.**
>>
>> I think it's right and I'd add a note from our side. We have no
>> revocation, and
>> that's deliberate rather than missing: our grants carry an expiry, and
>> authority
>> decays without anyone needing to deliver anything. A suppressed
>> revocation is
>> invisible to a receiver, as you say — but you can't suppress the passage
>> of
>> time. It's a real trade, not a free win: a short expiry means re-issuance
>> traffic and a live principal, and a long one means a wide window where
>> your
>> attack is exactly as bad as you describe. Where re-issuance is affordable,
>> expiry is revocation you don't have to deliver, and I'd rather the
>> substrate
>> name that as an option than have every profile carry a revocation channel
>> it
>> can't guarantee reaches anyone.
>>
>> **Joel's fourth item.**
>>
>> Same answer as Henri, Pablo and Nenad: we don't have it. Nothing in our
>> records
>> pins the publisher-assigned version of any corpus a decision was computed
>> against. Four for four is a better argument for lifting it into the
>> substrate
>> than any of us adding a field and then citing ourselves.
>>
>> **Willing, and here's what I bring.**
>>
>> The rail-enumeration binding above, the Coston2 fixtures, and the rung-3
>> refinement — anchor as pointer, not fact. Henri, the completeness
>> component and
>> its rungs stay yours to state; Todd, the fraud side is what keeps this
>> attached
>> to a real failure; and if Emek's ladder is going to carry the structure
>> of the
>> document, it should carry his name in it.
>>
>> Walter
>>
>> On Thu, Aug 20, 2026 04:53 PM, e.dogru@conarium.dev wrote:
>>
>>> Joel, Henri, Pablo, Walter, Todd, Nenad,
>>>
>>> Joel, you withdrew two claims in public before making your argument. I
>>> will put something of the same kind on the table before making mine.
>>>
>>> The corollary you name is the half I have not done, and I can show you
>>> exactly where the line falls.
>>>
>>> You wrote that the vocabulary has to reach the output, and that both our
>>> implementations skip it. In mine the /2 result carries a bounds object: for
>>> each bound the comparison relied on, its source is protocol-defined,
>>> measured, operator-declared, or undeclared, and a result whose bounds are
>>> operator-declared states an outcome of that standing and no stronger.
>>> Measured against the published package, one fixture, a receipt three
>>> seconds outside a two-hour window:
>>>
>>> - no profile: all three bounds undeclared, both items indeterminate
>>> - a profile with no clocks member: multiplicity and exclusion
>>> operator-declared, skew still undeclared
>>> - clocks.skew declared wider than the offset: operator-declared, and the
>>> item stays indeterminate. A declaration does not manufacture a match.
>>> - clocks.skew declared narrower than the offset: the same item becomes
>>> observed-without-receipt
>>>
>>> So the vocabulary reaches that object. It does not reach the exit codes,
>>> which predate it and are still not a mapping of it. Your sentence covers
>>> precisely the half I have not done, and I would rather that stayed in the
>>> record than get answered with a plan.
>>>
>>> The three clocks fields you asked for are in -05 with their encoding:
>>> clocks.observation, clocks.receipt, clocks.skew, a duration grammar, a rule
>>> that an unknown key in clocks is rejected by name, and a rule that the same
>>> bound declared twice must parse to the same milliseconds or the run fails
>>> rather than picking one. Written to be borrowed, not restated.
>>>
>>> On placement. The distinction is in -05 as a security consideration
>>> (sec-completeness), written against my own implementation rather than about
>>> two of them: a truncated receipt set verifies, detecting the removal needs
>>> a quantity from outside it, and whether that quantity reached the verifier
>>> independently of the Issuer is not visible in a digest. I support lifting
>>> it to the substrate, with one caveat about how it is worded there. The
>>> property that matters is not "in-record beats argument". It is that a
>>> Consumer cannot tell the two apart from the artefact, so the result has to
>>> say which one it relied on. Stated as a ranking it invites an
>>> implementation to claim the stronger form without carrying it; stated as a
>>> disclosure obligation it does not.
>>>
>>> On the weakest-rung rule. Four documents arriving at it separately is a
>>> better argument for the substrate than any one of us restating it, and your
>>> verdict-side version is the one I would take as the general form, because
>>> it survives translation to outputs that are not bounds at all. One caution
>>> from the side that has already shipped it: the rule is cheap to write and
>>> expensive to keep. indeterminate is only load-bearing while nobody is
>>> allowed to average it, and the pressure to fold it into a coverage
>>> percentage arrives from outside engineering, after the vocabulary is public
>>> and looks like a scoring system.
>>>
>>> One more thing worth putting in the record. Your sentence from two days
>>> ago, that a gaps section going stale understating an implementation fails
>>> the same way as one that overstates, turned out to apply to mine four times
>>> over. Each statement was accurate when written and false within days,
>>> always in the direction of claiming less than the code did, and nothing in
>>> my test suite compares that section against the code. So the corollary
>>> reaches further than either of us put it: the problem is not that our
>>> implementations skip the vocabulary. It is that neither of us has a check
>>> that would notice if they did. That is the gap I would most like the
>>> substrate to make expensive to leave open, and it is the next thing I write
>>> on my own side. I will come back with the check and what it caught, rather
>>> than with a plan.
>>>
>>> Emek Can Dogru
>>> Conarium
>>>
>>>
>>> On Fri, Aug 21, 2026 at 12:06 AM Joel Hillier <jhillier=
>>> 40certisyn.com@dmarc.ietf.org> wrote:
>>>
>>> Hi Henri, Pablo, Walter, Todd,
>>>
>>> Emek, I'm adding you to this because the rung ladder is yours and it's
>>> been travelling without your name on it. Henri's message this morning
>>> credits you with cloning at befdced and running the three cases, which
>>> is true, but you're also the one who wrote the ladder and the rule that
>>> goes with it. That rule is the most useful thing in this thread, and the
>>> first thing I did with it was turn it on my own document.It cost me two
>>> claims I made to you all yesterday. Both are below, before the rest.
>>>
>>> *Correction one. I told you ARP's sweep chain makes a withdrawn
>>> Statement detectable. That's true for the middle and false for the tail.*
>>>
>>> Each Evaluation Sweep Statement carries the previous Statement's
>>> notarisation, so I had the chain doing a job it can't do. Your 0.2.4
>>> shipped under "a hash chain is blind to a shorter tail" and it applies to
>>> my series exactly as it applies to yours: a truncatedtail of Sweep
>>> Statements verifies precisely as it did before.
>>>
>>> What actually puts ARP at rung 3 is a different mechanism, which I'd
>>> been crediting to the chain. Each Statement is due inside a bounded
>>> interval measured from its trigger, and where the trigger is a public event
>>> with a public timestamp, a sanctions list publishinga delta starts a clock
>>> the operator doesn't own. An operator with no Statement inside the interval
>>> hasn't evaluated in time, and anyone can check that holding none of the
>>> set. The chain covers the middle. The deadline covers the tail. Two
>>> mechanisms, and I'dhad them credited as one.
>>>
>>> *Correction two, and this one is worse, because it's the sentence where
>>> I congratulated myself on not rounding up.*
>>>
>>> I said the falsifiability argument holds for three of ARP's four
>>> triggers, "stated rather than rounded up." Only one of those three rested
>>> on anything outside the reconciliation server. The other two rested on a
>>> transition list published by the server, underthe server's own key, in a
>>> document with no publication time, no notarisation and no chain. A
>>> transition simply left out of that array started no clock, and no party
>>> could date the document well enough to show that it had been. On that
>>> footing the count wasone in four, and the sentence claiming otherwise was
>>> the one place the document rounded up.
>>>
>>> It's three in four now because I fixed the mechanism rather than the
>>> sentence: the document carries a publication timestamp, is notarised on the
>>> ledger-head interval, and has to be republished on that interval whether or
>>> not anything in it changed. That lastpart is the one that matters, and it's
>>> your placement argument doing the work. Without it, an operator that
>>> removed a witness entry and an operator that changed nothing publish the
>>> same thing, and the notarised series has no entry to be missing.
>>>
>>> The claim is now conditional and the document says so: a deployment
>>> whose policy parameters aren't anchored that way has one falsifiable
>>> trigger, not three. Which is your rule about naming the ground, applied to
>>> a count I'd already published.
>>>
>>> *One more, since it's the mechanism I described to Walter yesterday.* I
>>> said ARP's witness quorum counts entries under common control once, because
>>> two instances of one observer aren't two observers. The distinctness test
>>> was written over the operating-partyidentifier alone and said nothing about
>>> the keys. Two entries declaring the same key under two different party
>>> identifiers satisfied a quorum of two with a single signature. Both entries
>>> verify, the identifiers are distinct, and a relying party doing exactlywhat
>>> the document said counted one signer twice. Now fixed by requiring the
>>> verification method references to be pairwise distinct as well.
>>>
>>> Worth separating that from the limit I did state correctly. Whether two
>>> named parties are genuinely independent can't be tested by a verifier and
>>> the document concedes it. Whether two entries name one key can always be
>>> tested, and wasn't.
>>>
>>> *Now the thing I owe this thread.*
>>>
>>> Henri and Pablo have both said they'd need to add the fourth item before
>>> they could point at one, and Henri's suggested the substrate cite
>>> components normatively rather than restate them. Together that means the
>>> fourth item gets lifted out of ARP. So let mehand it over along with what
>>> was wrong with it, rather than after it's in shared text.
>>>
>>> A Partial Attestation carries a Source-Data Version Identifier Set: one
>>> identifier per source consulted in evaluating the predicate. Each is a
>>> tuple of the list name as declared in the bilateral agreement, and the
>>> state identifier the list publisher assignsto that state, not a value the
>>> answering party made up. It rides in the protected header so it's covered
>>> by the register's signature, and it's carried into the output so it's
>>> covered by the sealing signature.
>>>
>>> The reason it's the publisher's identifier and not the register's is the
>>> part worth carrying into any shared text, because it isn't obvious and it's
>>> the whole point. A register-chosen opaque string would be an
>>> arbitrary-bandwidth channel from register to relyingparty, travelling under
>>> signature into a sealed and ledgered artefact, and the accompanying rule
>>> that differing identifiers mustn't be read as disagreement would normalise
>>> it. Taking the identifier from the publisher's own state sequence is what
>>> makes it evidenceinstead of a side channel.
>>>
>>> Three things were wrong with the encoding, all found yesterday, all now
>>> repaired:
>>>
>>> 1. The Set had no ordering rule. Five other collections in ARP carry an
>>> explicit bytewise sort. This one was called a Set in its own section
>>> heading, was carried into two signatures, and was never sorted, so a
>>> register consulting three lists had six conformingencodings of one
>>> attestation. Now sorted.
>>>
>>> 2. The state identifier was disjunctive with no discriminator. "A
>>> published version token, or a digest of the published corpus where the
>>> publisher assigns none." Text in one branch, digest bytes in the other,
>>> same position, no algorithm named, and no statementof what the corpus is as
>>> a byte sequence. It now carries an explicit form discriminator, and the
>>> digest branch is taken over the octets the publisher serves, before any
>>> decompression. That second part is the one I'd have got wrong: a list
>>> published as a compressedarchive has at least two byte sequences with an
>>> equal claim to being the corpus, and two registers choosing differently
>>> produce two identifiers for one state, which a retroactive sweep then reads
>>> as a version change that never happened.
>>>
>>> 3. It was carried twice with no equality rule. ARP has exactly the right
>>> sentence for this, that a value carried twice with no equality rule is a
>>> value an implementation may read either way, and applied it to the two
>>> other duplicated headers and not to thisone.
>>>
>>> So the shape and the reason are worth lifting. The encoding is worth
>>> lifting as of this morning and wasn't yesterday.
>>>
>>> *On placement, Emek, your distinction deserves to be a named property of
>>> the substrate rather than a remark about two implementations.*
>>>
>>> You put it as: Henri's seal is a record inside the stream, signed and
>>> carried with the evidence, readable by whoever holds the set; Conarium's
>>> pin is an argument to the verifier, so a third party handed only the
>>> receipt set can't tell it's short unless someonestates what it should have
>>> been, and the obvious someone is the issuer, the party the audit is about.
>>>
>>> That's sharper than the rung number alone, because two mechanisms can
>>> sit on the same rung and differ entirely in who has to be asked. I'd state
>>> it as three placements:
>>>
>>> Inside the evidence. Travels with the set, needs nobody. Henri's seal.
>>>
>>> Supplied by the audited party. The verifier has to be told the expected
>>> count or terminal hash, and the party best placed to tell it is the one
>>> under audit. Conarium's pin, and you said so yourself rather than letting
>>> the symmetry flatter you.
>>>
>>> Supplied by a party with no stake. ARP's deadline is this one: the clock
>>> is started by a list publisher who's never heard of the reconciliation
>>> server.
>>>
>>> The third is strongest and least available, because it only exists where
>>> the obligation happens to be triggered by something public. It isn't a
>>> design choice you can make freely. Where it exists it should be used, and
>>> where it doesn't the statement should saywhich of the other two you're on.
>>>
>>> The sweep found a fourth case that the three-way split predicts and I
>>> hadn't looked for: a mechanism sitting at the first placement whose
>>> evidence is only obtainable from the second. ARP's head consistency
>>> statements are signed by independent witnesses, whichis the whole point of
>>> them, and the document specified the artefact, the quorum arithmetic and
>>> the freshness window and specified no channel by which a relying party
>>> obtains one. The obvious implementation is that the responding service
>>> hands them over withits response. Every check passes. The witness signature
>>> stops the service forging a statement and does nothing to stop it choosing
>>> which ones to pass on, so under exactly the fork the mechanism exists to
>>> detect, each branch's reader gets the witnesses thatbranch was fed. Now
>>> fixed by requiring witnesses to publish on their own origins and forbidding
>>> acceptance of one obtained from the responding service.
>>>
>>> Worth adding to the substrate as a rule in its own right: *state where
>>> the evidence is obtained, not only where it is produced.* An artefact
>>> produced at the third placement and delivered by the second is at the
>>> second.
>>>
>>> *Your weakest-rung rule already exists in ARP under another name, which
>>> I think argues it's the right general rule rather than a local convention.*
>>>
>>> You wrote that a result which doesn't say which rung it stands on has to
>>> be read at the weakest one, and that it's Walter's completeness ladder one
>>> level up.
>>>
>>> ARP arrived at the same rule from the verdict side and calls it verdict
>>> re-typing. An answer over a register whose attestation can't be verified
>>> doesn't produce a weaker match, it produces indeterminate. Where a
>>> re-notification can't say whether a change came from policy or from the
>>> underlying corpus, it has to carry attribution-indeterminate and name
>>> which causes were examined and why neither could be excluded, because a
>>> bare qualifier is a discretionary escape signed by the party that benefits
>>> from it. And conformance run records carry a does_not_establish field
>>> so the things a run didn't prove are enumerated rather than inferred from
>>> silence.
>>>
>>> Four instances, four documents, arrived at separately: bounds in yours,
>>> populations in Walter's, verdicts and conformance claims in mine. Worth
>>> stating once in the substrate, roughly as: *every result names the
>>> ground it stands on, and a result that names none is read at the weakest
>>> ground available to it.*
>>>
>>> The corollary is the one implementations skip, including both of ours.
>>> The vocabulary has to reach the output. You say your exit codes predate the
>>> vocabulary and still aren't a mapping of it. I found two of the same shape
>>> this week: a verifier told to refusea proof with no defined way to say
>>> which of four refusals it made, and one reason code carrying five distinct
>>> causes because four other sections pointed at it for conditions its own
>>> definition never named. Both now split. Defining the distinction and
>>> givingthe implementation no way to express it is a document agreeing with
>>> itself.
>>>
>>> *On your open question about the boundary declaration.*
>>>
>>> You framed it well: a boundary inside the record has nothing to drift
>>> from and is the stronger guarantee, but it's asserted by one party at issue
>>> time; a profile is the weaker binding and the only one that can be agreed
>>> between parties before a run and pointedat afterwards by both.
>>>
>>> ARP's answer is to make the profile a signed bilateral instrument with a
>>> hash. The agreement is settled before any run, both parties compute an
>>> agreement hash over its declared items independently and have to get the
>>> same value, and every reconciliation commitsto that hash. Drift suspends
>>> reconciliation rather than producing a weaker answer. So it keeps the
>>> agreed-not-asserted property of a profile and gains the
>>> nothing-to-drift-from property of an in-record boundary.
>>>
>>> The cost is that it only works bilaterally. It doesn't reach a
>>> population of parties who have never negotiated, which is most of Walter's
>>> setting. Worth having in the statement as one of the answers rather than
>>> the answer.
>>>
>>> *Pablo, your precedence point generalises, and it flips direction
>>> depending on what's being anchored.* Yours is anchoring_precedence: the
>>> anchor has to be strictly earlier than the outcome it covers, because an
>>> anchor arriving after the fact proves nothing about what was true when the
>>> outcome was recorded. ARP's is the mirror image. The Statement has tofall
>>> later than the trigger and inside a bounded interval, because what's being
>>> proved is that a duty was discharged on time, not that a fact was true
>>> beforehand.
>>>
>>> Same underlying requirement, that an anchor with no ordering constraint
>>> against the thing it covers is decorative. Two opposite orderings, because
>>> one anchors the proof and the other anchors the duty. I'd say it that way
>>> in the shared text: every external anchordeclares its required ordering
>>> relative to what it covers, and which of the two it is.
>>>
>>> Agreed on normative citation over restatement, with one addition. Where
>>> a component is cited rather than restated, the citing statement should also
>>> name the rung the component reaches. Henri's correction this morning is the
>>> case in point, and so are both ofmine above. None of the three components
>>> changed. The claim made about each was one rung short. A substrate that
>>> cites a component and repeats an over-broad claim about it has moved the
>>> error rather than removed it.
>>>
>>> Joel
>>>
>>>
>