@integraledger/lcp-placement-x402 0.10.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,111 @@
1
1
  # @integraledger/lcp-placement-x402
2
2
 
3
+ ## 0.12.0
4
+
5
+ ### Patch Changes
6
+
7
+ - @integraledger/lcp-binding-core@0.12.0
8
+
9
+ ## 0.11.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 822190a: Give the terms URL the write path the published set never had, and certify the composition that broke
14
+ without it (integra-protocol#8).
15
+
16
+ A third party assembling a seller from published parts emitted a 402 the published buyer refuses: every
17
+ published reader demanded `legalContextUrl` and no published writer placed it, and the schema
18
+ `placement-x402` inlined onto the wire (`required: ["type","value"]`, closed) contradicted the authority
19
+ document integraledger.com serves (`required: ["type","value","legalContextUrl"]`, closed) — two
20
+ definitions of one `info`, no document valid against both, each package self-consistent. Three structural
21
+ gaps let it ship: the manifest's `termsUrlField` was singular and read-only (declared, hygiene-checked,
22
+ never written — and x402's wire carries the URL in two slots, so one path could not even name the shape),
23
+ nothing compared the inlined schema to the authority document, and the corpus certified `place` and
24
+ `extract` separately but never fed one to the other.
25
+
26
+ `binding-core` — the placement seam now moves an ADVERTISEMENT, not a bare reference. `place` takes
27
+ `{ ref, termsUrl? }` and writes the URL at every slot the manifest's new `termsUrlFields` (plural,
28
+ replacing `termsUrlField`) declares; it REFUSES an integrity-bearing advertisement with no URL where slots
29
+ are declared (a hash no counterparty can resolve is unverifiable by construction), a URL where no slot
30
+ exists (silent dropping is fail-open), and a non-https URL on either side of the seam. `extract` returns
31
+ `{ ref, termsUrl }` with absence as a typed value — `no-field-declared` is a fact about the protocol,
32
+ `declared-fields-empty` a fact about the document, and the gate decides what an absence means — while two
33
+ slots that disagree, or a malformed value in either, refuse. The object-path writer learned to descend
34
+ into an EXISTING array element (never minting one, never extending a list, refusing an index segment it
35
+ would have to create), which is what lets x402's `accepts[0].extra` mirrors land.
36
+
37
+ `placement-x402` — the inlined wire schema now IS the authority document minus `$id` and `$defs`
38
+ (Bazaar forbids both on the wire), drift-gated in `lcp-conformance` where the two packages meet.
39
+ `termsUrlFields` declares both slots the wire carries; the bare-hash alias is written (`extra` stopped
40
+ being wholly scheme-private when x402 §6.1 reserved names inside it, and LCP v1.38 §C.4's own Tier A
41
+ illustration carries the pair there); the `url` carrier admission is withdrawn (`carrierTypes` is
42
+ `sha256` alone — the schema on the wire is `const: "sha256"`, and no shipped reader ever accepted a url
43
+ in this slot). The `place` override shrinks to composition: the kit performs the whole placement and the
44
+ override adds only the `{info, schema}` wrapper.
45
+
46
+ `placement-mpp` / `placement-acp` — the singular member becomes the one-entry `termsUrlFields`; the kit
47
+ now writes the slot their buyer parsers always demanded and refuses first at the seller.
48
+
49
+ `lcp-conformance` — the corpus grows 812 → 844: a `roundtrip` op (place then extract in one case, the
50
+ composition certification whose absence let two separately-conformant halves ship jointly broken),
51
+ advertisement-rule refusals for every manifest, and the authority↔wire drift gate. Extract expectations
52
+ across every placement area become the extracted advertisement.
53
+
54
+ `lcp-verify` — `referencePlacementStep` reads the advertisement (`extracted.ref.value`) and deliberately
55
+ ignores `termsUrl`: where the terms live is the gate's fetch concern, not a fact the record can
56
+ contradict.
57
+
58
+ `lcp-discovery` — the x402 authority document restates the atrHash pattern inline in both definitions
59
+ (no `$defs` indirection the wire copy would have to rewrite) and moves the two-definitions rationale into
60
+ `$defs.receipt`, so the challenge-time root is byte-derivable for the wire.
61
+
62
+ ### Patch Changes
63
+
64
+ - b2ffecc: Report the class a record actually supports, place a terms URL on UCP, and stop refusing a conformant UCP
65
+ profile — the remediation of the 2026-08-19 conformance re-audit.
66
+
67
+ **`verify` now computes `supportedClass` instead of echoing the claim.** It was `anyFailed ? "TC-0" :
68
+ claimedClass`, so a record proving nothing — no settlement, no acceptance, no authority chain — reported
69
+ whatever class the caller named, while the field's own published docblock promised "what the record
70
+ honestly supports, not what the caller asked for". It is now the highest class every one of whose required
71
+ steps is `proved`, `TC-0` on any failure, computed from the steps alone: neither capped by the claim (rungs
72
+ that reach TC-3 read TC-3 where the caller claimed TC-2) nor lifted by it. White paper #4 §5 defines the
73
+ class of a transaction as "the highest class whose criteria it fully meets", and this is that.
74
+
75
+ The claim is not discarded — the report gains **`claimedClass`**, a required member, because `verified`
76
+ answers "did the record reach the class it claimed?" and cannot be read without it. The two fields are the
77
+ report's two halves: an input echoed, and a finding computed. Where they differ, the record did not reach
78
+ its own shape. An out-of-taxonomy claim now lands only in the echo and can no longer masquerade as a
79
+ finding.
80
+
81
+ **UCP can advertise a terms URL.** Its policy object declares `url` — "Optional link to the full policy
82
+ document", `format: uri` — on the very entry this placement writes, and §C.3's illustration carries `url`
83
+ and `atrHash` side by side there. The manifest previously said the protocol had no slot, citing `links[]`,
84
+ which §C.3 separates as "a standing page, not a per-transaction record". The obstacle was mechanical:
85
+ `termsUrlFields` addresses document paths, and a tagged-array entry's index is chosen at write time. The
86
+ `tagged-array` container therefore gains `termsUrlField`, written onto the same entry in the same write, and
87
+ read back through the same first-match rule. UCP was the last shipped protocol that refused an
88
+ advertisement carrying its own locator (integra-protocol#8).
89
+
90
+ **`readUcpProfile` no longer refuses a conformant business profile.** `requireHttps` mapped an ABSENT `spec`
91
+ to the same branch as a malformed one, and the live host requires `spec` only of a platform declaration —
92
+ as this repository's own README already said. It is now `requireHttpsIfDeclared`: absence is absence, and a
93
+ declared value is still held to the host's https MUST.
94
+
95
+ Also: `requireWritten` replaces an unchecked cast in the x402 override, so a broken postcondition throws
96
+ instead of returning a success carrying no document; six x402 citations move to the revision that actually
97
+ touches the file they name, and a new gate refuses any `owner/repo@sha` in source that `spec-pins.json` does
98
+ not record; four spec citations move from line anchors to section anchors; the escrow binding states why it
99
+ declares no §8.3.1 off-canonical variant, and asserts it; and §C.3's `policies[]` illustration is recorded as
100
+ invalid against the live UCP schema, which shows `description` as a bare string where the host requires an
101
+ object — owed upstream, not a defect here.
102
+
103
+ Corpus 844 → 847, root `ec4ad1b02a81538b…`.
104
+
105
+ - Updated dependencies [b2ffecc]
106
+ - Updated dependencies [822190a]
107
+ - @integraledger/lcp-binding-core@0.11.0
108
+
3
109
  ## 0.10.1
4
110
 
5
111
  **0.10.0 was staged and withdrawn before approval; this is that release, re-cut.** The conformance corpus
package/README.md CHANGED
@@ -19,10 +19,10 @@ npm install @integraledger/lcp-placement-x402
19
19
  | **Chain** | none here — settlement is `binding-evm-x402`'s, on whichever EVM rail the scheme selects |
20
20
  | **Pattern** | `http-advisory` (LCP §8.3.7, Tier A) |
21
21
  | **Field** | `extensions.legalContext.info` — the top-level extensions map, the carrier x402 protects |
22
- | **Read also** | `accepts.0.extra.atrHash` — a **bare** hash, its own encoding (integrity) |
23
- | **Terms URL** | `extensions.legalContext.info.legalContextUrl` — declared, never written by `place` |
24
- | **Carrier types** | `sha256`, `url` |
25
- | **Spec** | x402 v2 (`x402-foundation/x402@1fec3aa04e41`, `specs/x402-specification-v2.md`), gate discharged **2026-07-30** |
22
+ | **Read also** | `accepts.0.extra.atrHash` — a **bare** hash, its own encoding (integrity), **written** by `place` |
23
+ | **Terms URL** | `extensions.legalContext.info.legalContextUrl` and `accepts.0.extra.legalContextUrl` both written by `place` |
24
+ | **Carrier types** | `sha256` |
25
+ | **Spec** | x402 v2 (`x402-foundation/x402@db5da2e65952`, `specs/x402-specification-v2.md`, read **2026-08-11**), gate discharged **2026-07-30** |
26
26
 
27
27
  ## Use
28
28
 
@@ -31,7 +31,9 @@ import { X402_PLACEMENT, x402Placement } from "@integraledger/lcp-placement-x402
31
31
 
32
32
  declare const challenge: unknown; // the x402 402 challenge document, as received
33
33
 
34
- const placed = x402Placement.place({ type: "sha256", value: "0x…" }, challenge);
34
+ const placed = x402Placement.place(
35
+ { ref: { type: "sha256", value: "0x…" }, termsUrl: "https://seller.example/.well-known/legal-context.json" },
36
+ challenge);
35
37
  const ref = x402Placement.extract(challenge); // reads either carrier, canonical first
36
38
  ```
37
39
 
@@ -55,8 +57,9 @@ and the PaymentRequirements table), four facts were confirmed and each one decid
55
57
  cannot delete or overwrite existing info." This is the carrier the **protocol itself protects**, which is
56
58
  why it is canonical here rather than the per-requirement object.
57
59
  4. **`extra` is "Scheme-specific additional information"** on a `PaymentRequirements` entry — the payment
58
- scheme's object, whose contents that scheme defines. So `place` **never writes there**; the alias is
59
- read-only, and that is a decision about whose namespace it is, not an omission.
60
+ scheme's object, whose contents that scheme defines. `place` writes the §C.4 mirror and the terms URL
61
+ there because §6.1 reserves those names for exactly this use, and writing only the extensions map left
62
+ a challenge the published buyer parsers refuse (integra-protocol#8).
60
63
 
61
64
  Extension identifiers are implementation-defined strings — no registry, no reverse-domain rule — so the
62
65
  `legalContext` key is available today and this placement is Tier A on the wire.
@@ -150,55 +153,48 @@ canonical non-negative integer segment only, so `length` and every other array p
150
153
  That relaxation lives in the kit rather than in a private loop here, because a read rule nobody can find is
151
154
  the same defect as a carrier nobody can find.
152
155
 
153
- ## Known limitations
154
-
155
- - **`place` writes the hash, never the terms URL.** `place(ref, doc)` holds one reference; the terms URL is a
156
- second datum. `termsUrlField` is declared so a parser can find the URL our seller does emit, the same
157
- division `placement-acp` draws with `metadata.legal_context_url`.
158
- - **The shipped carrier repeats the URL at `accepts[0].extra.legalContextUrl`.** A single `termsUrlField`
159
- cannot express two spellings, so that one is recorded here rather than half-declared in the manifest.
160
- - **A `url` carrier is placeable here and no shipped x402 reader accepts one.** `carrierTypes` permits
161
- `sha256` and `url` the §8.1 integrity form and the §8.1 discovery form, the same pair `placement-acp` and
162
- `placement-ucp` permit while a strict buyer gate refuses any `info.type` but `sha256`. The permission is not
163
- narrowed away here because narrowing the reference field to one type is a statement about what the slot may
164
- hold across the whole set, not a fact about x402. **Related and not ours to fix in this package:**
165
- `readDeclaredPaths` labels a canonical-field hit `carrierClass: "integrity"` unconditionally, so a `url`
166
- read from `extensions.legalContext.info` is labelled `integrity` and passes `requireIntegrity()` (from
167
- [`@integraledger/lcp-binding-core`](../binding-core#readme), like `makePlacement`). That is
168
- `binding-core` behaviour that `placement-acp` and `placement-ucp` already share on `main`; this manifest
169
- only adds a third reference field with the same property, and `requireIntegrity` has no production caller
170
- yet.
171
- - **Our own `legalContext` entry is replaced whole, not merged — and the key that loses is `legalContextUrl`,
172
- the one `termsUrlField` points at.** `place` writes `{info, schema}`, so nothing previously inside our entry
173
- survives, and on the document sellers actually emit that is concretely the terms-URL half: measured on
174
- the vector case for the long-standing shipped carrier, `termsUrlField` reads
175
- `https://seller.example/.well-known/legal-context.json` before `place` and `undefined` after. This is the
176
- kit's leaf-write semantics, not an override defect `makePlacement(X402_PLACEMENT).place` drops it
177
- identically — and it is why the previous bullet says `place` holds one reference: a caller that needs both
178
- halves on the wire writes the URL itself.
179
- **Consequence for a buyer.** Do not build a `PaymentPayload` echo with `place`. x402's rule is that the
180
- client "cannot delete or overwrite existing `info`", and re-placing over a server-sent entry deletes the
181
- URL the server put there. Echo the received entry verbatim and place only where no entry exists yet; the
182
- reconciled read/echo path belongs to a universal buyer parser, not this package.
183
- x402 defines only `info` and `schema` for an entry and the `legalContext` key is ours, so owning the entry is
184
- the point for the *seller* direction; entries *beside* ours in the `extensions` map are preserved
185
- unconditionally.
186
- - **`LEGAL_CONTEXT_SCHEMA` is INLINED, not a reference.** It was a `$ref` at
187
- `https://legalcontextprotocol.org/schemas/lcp-extension.json`, which returns 404 — re-measured
188
- 2026-08-11 — and x402 makes `schema` a REQUIRED member of every extension entry, so that shipped a
189
- required member no counterparty could resolve. All nine extensions published in the x402 repository inline
190
- a complete JSON Schema, and one of them, Bazaar, forbids an external `$ref` outright. The exported value is
191
- now a frozen literal that is byte-identical to what the placement emits. Changing it is a **wire change**:
192
- it appears in every challenge.
156
+ ## Limitations, and the three this version resolved
157
+
158
+ The predecessor recorded four limitations here. Three were one defect wearing different clothes
159
+ integra-protocol#8, a seller built on this package emitting a challenge the published buyer refuses and
160
+ are resolved rather than recorded:
161
+
162
+ - **`place` now writes the whole advertisement.** `place({ ref, termsUrl }, doc)` puts the reference and
163
+ the terms URL on the wire together, at every slot `termsUrlFields` declares the `info` member the
164
+ authority schema requires and the `accepts[0].extra` mirror §C.4's illustration carries and REFUSES an
165
+ integrity-bearing advertisement with no URL, because a hash no counterparty can resolve is unverifiable
166
+ by construction. The predecessor's "place writes the hash, never the terms URL" division put the URL in
167
+ no published writer at all while every published reader demanded it.
168
+ - **Both URL spellings are declared, and both are written.** `termsUrlFields` is plural; the singular
169
+ member could not express the second spelling and the shipped carrier's `extra`-side URL lived only in
170
+ this README.
171
+ - **Our own entry is still replaced whole — and no longer costs the URL.** `place` rebuilds
172
+ `{info, schema}` wholesale (junk a counterparty parked inside our entry does not ride our wire), and the
173
+ URL survives because the rebuild happens after the kit has written it into `info`. The predecessor
174
+ measured the opposite: the leaf-write dropped `legalContextUrl` on the very document sellers emit.
175
+
176
+ One limitation is withdrawn rather than resolved: **the `url` carrier admission is gone.**
177
+ `carrierTypes` is `sha256` alone. A `url` here was well-formed against the manifest and refused by every
178
+ shipped reader a permission no reader accepts is a claim about the ecosystem, not a description of it —
179
+ and the schema this package itself puts on the wire (the authority document's shape, drift-gated in
180
+ `@integraledger/lcp-conformance`) is `const: "sha256"`, so a `url` reference would emit a challenge that
181
+ violates its own adjacent schema.
182
+
183
+ What remains, and is a fact rather than a defect: **`readDeclaredPaths` labels a canonical-field hit
184
+ `carrierClass: "integrity"` unconditionally.** That is `binding-core` behaviour shared by every placement,
185
+ and `requireIntegrity()` (from
186
+ [`@integraledger/lcp-binding-core`](../binding-core#readme), like `makePlacement`) is where a caller that
187
+ needs a content-addressed value says so it checks the decoded type as well as the slot's label.
193
188
 
194
189
  ## Provenance
195
190
 
196
- Cut against x402 v2 (`x402-foundation/x402@1fec3aa04e41`, `specs/x402-specification-v2.md`, read 2026-07-30) and reconciled
197
- against LCP v1.37 §C.4 the same day, and re-read against **v1.38 §C.4** on 2026-08-12 which adopted the
198
- drift items below and added §6.1's reserved keys. The **paths and shapes** are matched field-for-field against the
199
- shipped seller carrier and the buyer parsers that read it; the **resolution semantics diverge from a strict
200
- buyer gate in three recorded ways**, and *Drift from a strict buyer-side reader*
201
- above states each one and who closes it. Both drift sections are the disclosure, not a to-do list — a
191
+ Cut against x402 v2 and reconciled against LCP v1.37 §C.4 on 2026-07-30, and re-read against **v1.38 §C.4**
192
+ on 2026-08-12 which adopted the drift items below and added §6.1's reserved keys. The host revision is
193
+ `x402-foundation/x402@db5da2e65952` (`specs/x402-specification-v2.md`), which is what `spec-pins.json`
194
+ records this tree read on 2026-08-11 and what `pnpm spec-drift` watches. The **paths and shapes** are
195
+ matched field-for-field against the shipped seller carrier and the buyer parsers that read it; the
196
+ **resolution semantics diverge from a strict buyer gate in three recorded ways**, and *Drift from a strict
197
+ buyer-side reader* above states each one and who closes it. Both drift sections are the disclosure, not a to-do list — a
202
198
  divergence written down is governed; the same divergence unwritten is the undisclosed-drift defect wearing
203
199
  a new name.
204
200
 
@@ -1,40 +1,44 @@
1
1
  import type { PlacementManifest } from "@integraledger/lcp-binding-core";
2
2
  /**
3
- * The carrier's JSON Schema, INLINED rather than referenced.
4
- *
5
- * x402 makes `schema` a REQUIRED member of an extension entry "JSON Schema defining the expected
6
- * structure of info" so whatever goes here is on the wire of every challenge. A `$ref` to a URL nobody
7
- * serves would be a required member no counterparty can resolve, and "x402 never fetches it" is a reason
8
- * that does not break, not a reason to ship it. (`https://legalcontextprotocol.org/schemas/lcp-extension.json`
9
- * returns **404**, measured 2026-08-08 the schema is inlined instead.)
10
- *
11
- * **All nine extensions published in the x402 repository inline a complete JSON Schema** rather than
12
- * referencing an external document — `bazaar`, `builder-code`, `eip2612GasSponsoring`,
13
- * `erc20ApprovalGasSponsoring`, `auth-hints`, `offer-receipt`, `http-message-signatures`,
14
- * `payment-identifier`, `sign-in-with-x`, read at `x402-foundation/x402` HEAD 2026-08-11. Note that two of
15
- * the nine are camelCase, so this package's `legalContext` key follows an established minority spelling
16
- * rather than diverging from a unanimous one. LCP v1.38 §C.4 says to do one or the other — "Because
17
- * `schema` is a REQUIRED member, publish a resolvable schema or inline it" and adds that inlining is the
18
- * safer of the two. Inlining also removes a hosting
19
- * dependency the deployment does not currently meet the same dependency the UCP capability still owes.
20
- *
21
- * **One of the nine makes it a rule, which settles the question.** The Bazaar extension requires a
22
- * `schema`'s `$ref`/`$id` values to be "same-document JSON Pointer fragments (starting with `#`); external
23
- * references (`http(s)://`, `file://`, or any other absolute/relative URI) are not allowed", and says a
24
- * facilitator "must not resolve external `$ref`/`$id` values when validating an untrusted `schema`". So a
25
- * `$ref` here would not merely be unresolvable to a counterparty — it would be rejected outright by any
26
- * facilitator cataloguing this extension.
27
- *
28
- * The shape is the §8.1 reference object this placement writes into `info`, and nothing more: it describes
29
- * the carrier, not the terms behind it.
3
+ * The carrier's JSON Schema, INLINED rather than referenced — and equal, member for member, to the
4
+ * AUTHORITY document at `https://integraledger.com/lcp/x402/legal-context/v1.schema.json` minus its `$id`
5
+ * and `$defs`. That equality is load-bearing and drift-gated (the conformance suite compares this literal
6
+ * to `@integraledger/lcp-discovery`'s shipped copy of the authority file), because its absence was a
7
+ * published defect: from 0.10.1's release until this version, this schema said `required: ["type",
8
+ * "value"]` while the authority document said `required: ["type", "value", "legalContextUrl"]` — two
9
+ * definitions of the same `info` in the published ecosystem, self-consistent halves, no document valid
10
+ * against both (integra-protocol#8). Both being conformant to the schema each carried is exactly why no
11
+ * package's own tests could catch it; only comparing the two could, and now something does.
12
+ *
13
+ * WHY INLINE AT ALL: x402 makes `schema` a REQUIRED member of an extension entry — "JSON Schema defining
14
+ * the expected structure of info" so whatever goes here is on the wire of every challenge. **All nine
15
+ * extensions published in the x402 repository inline a complete JSON Schema** rather than referencing an
16
+ * external document (`x402-foundation/x402` HEAD, read 2026-08-11), and one of the nine makes it a rule:
17
+ * the Bazaar extension requires a `schema`'s `$ref`/`$id` values to be "same-document JSON Pointer
18
+ * fragments (starting with `#`); external references are not allowed", and says a facilitator "must not
19
+ * resolve external `$ref`/`$id` values when validating an untrusted `schema`". So an external `$ref`
20
+ * here would not merely be unresolvable to a counterparty that declines to fetch — wherever Bazaar
21
+ * governs, it is rejected outright. LCP v1.38 §C.4 draws the same conclusion ("publish a resolvable
22
+ * schema or inline it and inlining is the safer of the two"). Dropping `$id` and `$defs` from the
23
+ * inlined form is that rule applied: the authority document's `$id` is an absolute URL, and its `$defs`
24
+ * carries the RECEIPT-time definition, which is not this challenge-time `info` and would bloat every 402.
25
+ *
26
+ * The shape is the §8.1 reference object PLUS the locator the reference is verified through:
27
+ * `legalContextUrl` is REQUIRED here because `value` is a digest — a buyer verifies the terms by fetching
28
+ * the document and hashing it, so a challenge advertising the hash without the locator advertises
29
+ * something no counterparty who lacks the document can check. Every shipped buyer parser already refuses
30
+ * such a challenge; the schema now says on the wire what the readers always demanded. It describes the
31
+ * carrier, not the terms behind it, and asserts nothing about any agreement's lawfulness — the
32
+ * description says so in as many words because the schema travels alone.
30
33
  *
31
34
  * Changing this is a WIRE change — it appears in every challenge — so it is a frozen literal rather than a
32
- * value assembled at call time.
35
+ * value assembled at call time, and the drift gate is what keeps the frozen copy honest.
33
36
  */
34
37
  export declare const LEGAL_CONTEXT_SCHEMA: Readonly<Record<string, unknown>>;
35
38
  /**
36
39
  * x402 reference placement — the HTTP-layer carrier, cut against the live x402 v2 specification
37
- * (`x402-foundation/x402@1fec3aa04e41`, `specs/x402-specification-v2.md`; gate discharged in the README).
40
+ * (`x402-foundation/x402@db5da2e65952`, `specs/x402-specification-v2.md`, read 2026-08-11; gate discharged
41
+ * in the README).
38
42
  *
39
43
  * **THE CANONICAL REPOSITORY IS THE FOUNDATION'S, and the citation moved on 2026-08-08.** Every
40
44
  * reference here named `coinbase/x402`, which the GitHub API reports as `"fork": true`;
@@ -77,39 +81,47 @@ export declare const LEGAL_CONTEXT_SCHEMA: Readonly<Record<string, unknown>>;
77
81
  * would emit something no x402 counterparty parses. This is the reason an alias declares its own `encoding`
78
82
  * at all.
79
83
  *
80
- * **The alias declares no `write`, and the live spec is the reason.** `extra` is "Scheme-specific additional
81
- * information" the payment scheme's object, whose contents that scheme defines. An `atrHash` is READ there
82
- * because sellers put one there; writing into another party's namespace is not the same act. The
83
- * `extensions` map is the protocol's own declared extension point and is where `place` writes.
84
+ * **The alias IS WRITTEN, and this reverses a recorded stance deliberately, on three grounds.** The
85
+ * predecessor declared no `write` on the reasoning that `extra` is "Scheme-specific additional information"
86
+ * and writing into another party's namespace is not our act. That reasoning has been overtaken. First, the
87
+ * host itself no longer treats `extra` as wholly scheme-private: §6.1 reserves `assetTransferMethod` and
88
+ * `paymentFlow` inside it as protocol-governed names, so `extra` is a host-managed extension surface with
89
+ * scheme-specific residue, not a foreign namespace. Second, LCP v1.38 §C.4's own Tier A illustration puts
90
+ * `atrHash` AND `legalContextUrl` in `accepts[].extra` — a third-party reader built from the spec's example
91
+ * reads `extra` first, and a challenge that leaves it empty is invisible to that reader. Third, the shipped
92
+ * buyer parser reads BOTH carriers and reconciles, refusing disagreement — so the mirror cannot drift
93
+ * silently: two slots either agree or the document refuses at the counterparty. The write lands only in
94
+ * `accepts[0]`, the requirement buyer parsers read (see the index-0 rule below), and never touches the
95
+ * reserved names.
84
96
  *
85
97
  * **The alias is index 0 only.** A locator names one path. `accepts[0]` is what buyer parsers read, and the
86
98
  * reason is substantive: the reference must bind to the requirement actually being paid, and searching every
87
99
  * requirement would let a seller park a second set of terms on an alternative it never expects to be chosen.
88
100
  *
89
- * **`termsUrlField` is DECLARED this is the protocol whose wire carries both halves.** `binding-core`'s own
90
- * contract cites x402 for exactly that: a buyer-side parser may demand the URL because x402 carries it, and
91
- * emitters put `legalContextUrl` inside `info` beside `type`/`value`. Declaring the path
92
- * makes that half machine-readable instead of a second private convention; `place` never writes it, because
93
- * `place(ref, doc)` holds one reference and the terms URL is a different datum (the same division ACP draws
94
- * with `metadata.legal_context_url`). The shipped carrier repeats the URL at `accepts[0].extra.legalContextUrl`
95
- * too, which a single `termsUrlField` cannot express recorded in the README as a known limitation rather
96
- * than half-declared here.
97
- *
98
- * **`carrierTypes` permits `sha256` and `url`, and the two are admitted on DIFFERENT grounds** one reason
99
- * cannot cover both. `sha256` is the integrity carrier: §C.4's illustration carries one, emitters carry one,
100
- * and the bare alias is fixed to it. `url` is the §8.1 discovery form, admitted because the canonical
101
- * slot is a general reference-object slot and the kit puts the integrity-versus-discovery decision at the
102
- * READER `carrierClass` plus `requireIntegrity` not in the permission list; ACP and UCP permit it for the
103
- * same reason. `ipfs`/`ar` are excluded on a ground that does NOT apply to `url`: they are ALTERNATIVE
104
- * integrity carriers, so admitting one adds no capability `sha256` does not already discharge while
105
- * advertising a content-addressed transport no x402 counterparty resolves a claim about the ecosystem
106
- * rather than a description of it.
107
- *
108
- * The `url` permission is nonetheless WIDER than any shipped x402 reader: a buyer parser that requires an
109
- * integrity carrier refuses `info.type !== "sha256"` outright, so a `url` placed in this slot is well-formed
110
- * against this manifest and would still be rejected at read time. Recorded in the README as a limitation
111
- * rather than narrowed away here, because narrowing the reference field to one type is a change to what the
112
- * SLOT may hold across the set, not a fact about x402.
101
+ * **`termsUrlFields` declares BOTH slots the wire carries, and both are written.** The predecessor member
102
+ * (`termsUrlField`, singular) named only the `extensions` slot, and it was read-only in every published
103
+ * package the write path did not exist anywhere, so a seller assembling from published parts emitted a
104
+ * challenge advertising a hash with no locator, which the published buyer refuses
105
+ * (integra-protocol#8). Declaring both slots makes the manifest state what actually lands on the wire:
106
+ * `place` writes the URL beside the reference in `info` (where the authority schema requires it) and
107
+ * mirrors it at `accepts[0].extra.legalContextUrl` (where §C.4's illustration carries it), and `extract`
108
+ * reconciles the two, refusing disagreement. The kit REQUIRES the URL of any integrity-bearing
109
+ * advertisement on this manifest — a hash no counterparty can resolve is unverifiable by construction,
110
+ * which is the defect the readers always guarded against and the emitters never did.
111
+ *
112
+ * **`carrierTypes` is `sha256` alone, and the `url` admission is WITHDRAWN a defect resolved, not a
113
+ * preference.** The predecessor admitted `url` as the §8.1 discovery form and recorded, in the same
114
+ * docblock, that the permission was "WIDER than any shipped x402 reader": a buyer parser that requires an
115
+ * integrity carrier refuses `info.type !== "sha256"` outright, so a `url` placed in this slot was
116
+ * well-formed against the manifest and rejected at read time a permission no reader accepts, which is a
117
+ * claim about the ecosystem rather than a description of it. The withdrawal ground is now structural: the
118
+ * `schema` member this package puts on the wire is the AUTHORITY document's shape, whose `type` is
119
+ * `const: "sha256"`, so a `url` reference would emit a challenge that violates its own adjacent schema.
120
+ * The predecessor declined to narrow because "narrowing the reference field to one type is a change to
121
+ * what the SLOT may hold across the set" that set-wide decision has since been made, by the authority
122
+ * document. `ipfs`/`ar` remain excluded on the original ground: alternative integrity carriers add no
123
+ * capability `sha256` does not already discharge while advertising a content-addressed transport no x402
124
+ * counterparty resolves.
113
125
  */
114
126
  export declare const X402_PLACEMENT: PlacementManifest;
115
127
  //# sourceMappingURL=manifest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuB/D,CAAC;AAEL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8EG;AACH,eAAO,MAAM,cAAc,EAAE,iBAkB5B,CAAC"}
1
+ {"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,oBAAoB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA6B/D,CAAC;AAEL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuFG;AACH,eAAO,MAAM,cAAc,EAAE,iBAsB5B,CAAC"}