@integraledger/lcp-placement-x402 0.9.0 → 0.11.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 +119 -0
- package/README.md +53 -52
- package/dist/manifest.d.ts +66 -42
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +85 -52
- package/dist/manifest.js.map +1 -1
- package/dist/placement.d.ts +23 -10
- package/dist/placement.d.ts.map +1 -1
- package/dist/placement.js +41 -81
- package/dist/placement.js.map +1 -1
- package/package.json +2 -2
- package/src/manifest.ts +87 -53
- package/src/placement.ts +50 -98
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,124 @@
|
|
|
1
1
|
# @integraledger/lcp-placement-x402
|
|
2
2
|
|
|
3
|
+
## 0.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 822190a: Give the terms URL the write path the published set never had, and certify the composition that broke
|
|
8
|
+
without it (integra-protocol#8).
|
|
9
|
+
|
|
10
|
+
A third party assembling a seller from published parts emitted a 402 the published buyer refuses: every
|
|
11
|
+
published reader demanded `legalContextUrl` and no published writer placed it, and the schema
|
|
12
|
+
`placement-x402` inlined onto the wire (`required: ["type","value"]`, closed) contradicted the authority
|
|
13
|
+
document integraledger.com serves (`required: ["type","value","legalContextUrl"]`, closed) — two
|
|
14
|
+
definitions of one `info`, no document valid against both, each package self-consistent. Three structural
|
|
15
|
+
gaps let it ship: the manifest's `termsUrlField` was singular and read-only (declared, hygiene-checked,
|
|
16
|
+
never written — and x402's wire carries the URL in two slots, so one path could not even name the shape),
|
|
17
|
+
nothing compared the inlined schema to the authority document, and the corpus certified `place` and
|
|
18
|
+
`extract` separately but never fed one to the other.
|
|
19
|
+
|
|
20
|
+
`binding-core` — the placement seam now moves an ADVERTISEMENT, not a bare reference. `place` takes
|
|
21
|
+
`{ ref, termsUrl? }` and writes the URL at every slot the manifest's new `termsUrlFields` (plural,
|
|
22
|
+
replacing `termsUrlField`) declares; it REFUSES an integrity-bearing advertisement with no URL where slots
|
|
23
|
+
are declared (a hash no counterparty can resolve is unverifiable by construction), a URL where no slot
|
|
24
|
+
exists (silent dropping is fail-open), and a non-https URL on either side of the seam. `extract` returns
|
|
25
|
+
`{ ref, termsUrl }` with absence as a typed value — `no-field-declared` is a fact about the protocol,
|
|
26
|
+
`declared-fields-empty` a fact about the document, and the gate decides what an absence means — while two
|
|
27
|
+
slots that disagree, or a malformed value in either, refuse. The object-path writer learned to descend
|
|
28
|
+
into an EXISTING array element (never minting one, never extending a list, refusing an index segment it
|
|
29
|
+
would have to create), which is what lets x402's `accepts[0].extra` mirrors land.
|
|
30
|
+
|
|
31
|
+
`placement-x402` — the inlined wire schema now IS the authority document minus `$id` and `$defs`
|
|
32
|
+
(Bazaar forbids both on the wire), drift-gated in `lcp-conformance` where the two packages meet.
|
|
33
|
+
`termsUrlFields` declares both slots the wire carries; the bare-hash alias is written (`extra` stopped
|
|
34
|
+
being wholly scheme-private when x402 §6.1 reserved names inside it, and LCP v1.38 §C.4's own Tier A
|
|
35
|
+
illustration carries the pair there); the `url` carrier admission is withdrawn (`carrierTypes` is
|
|
36
|
+
`sha256` alone — the schema on the wire is `const: "sha256"`, and no shipped reader ever accepted a url
|
|
37
|
+
in this slot). The `place` override shrinks to composition: the kit performs the whole placement and the
|
|
38
|
+
override adds only the `{info, schema}` wrapper.
|
|
39
|
+
|
|
40
|
+
`placement-mpp` / `placement-acp` — the singular member becomes the one-entry `termsUrlFields`; the kit
|
|
41
|
+
now writes the slot their buyer parsers always demanded and refuses first at the seller.
|
|
42
|
+
|
|
43
|
+
`lcp-conformance` — the corpus grows 812 → 844: a `roundtrip` op (place then extract in one case, the
|
|
44
|
+
composition certification whose absence let two separately-conformant halves ship jointly broken),
|
|
45
|
+
advertisement-rule refusals for every manifest, and the authority↔wire drift gate. Extract expectations
|
|
46
|
+
across every placement area become the extracted advertisement.
|
|
47
|
+
|
|
48
|
+
`lcp-verify` — `referencePlacementStep` reads the advertisement (`extracted.ref.value`) and deliberately
|
|
49
|
+
ignores `termsUrl`: where the terms live is the gate's fetch concern, not a fact the record can
|
|
50
|
+
contradict.
|
|
51
|
+
|
|
52
|
+
`lcp-discovery` — the x402 authority document restates the atrHash pattern inline in both definitions
|
|
53
|
+
(no `$defs` indirection the wire copy would have to rewrite) and moves the two-definitions rationale into
|
|
54
|
+
`$defs.receipt`, so the challenge-time root is byte-derivable for the wire.
|
|
55
|
+
|
|
56
|
+
### Patch Changes
|
|
57
|
+
|
|
58
|
+
- b2ffecc: Report the class a record actually supports, place a terms URL on UCP, and stop refusing a conformant UCP
|
|
59
|
+
profile — the remediation of the 2026-08-19 conformance re-audit.
|
|
60
|
+
|
|
61
|
+
**`verify` now computes `supportedClass` instead of echoing the claim.** It was `anyFailed ? "TC-0" :
|
|
62
|
+
claimedClass`, so a record proving nothing — no settlement, no acceptance, no authority chain — reported
|
|
63
|
+
whatever class the caller named, while the field's own published docblock promised "what the record
|
|
64
|
+
honestly supports, not what the caller asked for". It is now the highest class every one of whose required
|
|
65
|
+
steps is `proved`, `TC-0` on any failure, computed from the steps alone: neither capped by the claim (rungs
|
|
66
|
+
that reach TC-3 read TC-3 where the caller claimed TC-2) nor lifted by it. White paper #4 §5 defines the
|
|
67
|
+
class of a transaction as "the highest class whose criteria it fully meets", and this is that.
|
|
68
|
+
|
|
69
|
+
The claim is not discarded — the report gains **`claimedClass`**, a required member, because `verified`
|
|
70
|
+
answers "did the record reach the class it claimed?" and cannot be read without it. The two fields are the
|
|
71
|
+
report's two halves: an input echoed, and a finding computed. Where they differ, the record did not reach
|
|
72
|
+
its own shape. An out-of-taxonomy claim now lands only in the echo and can no longer masquerade as a
|
|
73
|
+
finding.
|
|
74
|
+
|
|
75
|
+
**UCP can advertise a terms URL.** Its policy object declares `url` — "Optional link to the full policy
|
|
76
|
+
document", `format: uri` — on the very entry this placement writes, and §C.3's illustration carries `url`
|
|
77
|
+
and `atrHash` side by side there. The manifest previously said the protocol had no slot, citing `links[]`,
|
|
78
|
+
which §C.3 separates as "a standing page, not a per-transaction record". The obstacle was mechanical:
|
|
79
|
+
`termsUrlFields` addresses document paths, and a tagged-array entry's index is chosen at write time. The
|
|
80
|
+
`tagged-array` container therefore gains `termsUrlField`, written onto the same entry in the same write, and
|
|
81
|
+
read back through the same first-match rule. UCP was the last shipped protocol that refused an
|
|
82
|
+
advertisement carrying its own locator (integra-protocol#8).
|
|
83
|
+
|
|
84
|
+
**`readUcpProfile` no longer refuses a conformant business profile.** `requireHttps` mapped an ABSENT `spec`
|
|
85
|
+
to the same branch as a malformed one, and the live host requires `spec` only of a platform declaration —
|
|
86
|
+
as this repository's own README already said. It is now `requireHttpsIfDeclared`: absence is absence, and a
|
|
87
|
+
declared value is still held to the host's https MUST.
|
|
88
|
+
|
|
89
|
+
Also: `requireWritten` replaces an unchecked cast in the x402 override, so a broken postcondition throws
|
|
90
|
+
instead of returning a success carrying no document; six x402 citations move to the revision that actually
|
|
91
|
+
touches the file they name, and a new gate refuses any `owner/repo@sha` in source that `spec-pins.json` does
|
|
92
|
+
not record; four spec citations move from line anchors to section anchors; the escrow binding states why it
|
|
93
|
+
declares no §8.3.1 off-canonical variant, and asserts it; and §C.3's `policies[]` illustration is recorded as
|
|
94
|
+
invalid against the live UCP schema, which shows `description` as a bare string where the host requires an
|
|
95
|
+
object — owed upstream, not a defect here.
|
|
96
|
+
|
|
97
|
+
Corpus 844 → 847, root `ec4ad1b02a81538b…`.
|
|
98
|
+
|
|
99
|
+
- Updated dependencies [b2ffecc]
|
|
100
|
+
- Updated dependencies [822190a]
|
|
101
|
+
- @integraledger/lcp-binding-core@0.11.0
|
|
102
|
+
|
|
103
|
+
## 0.10.1
|
|
104
|
+
|
|
105
|
+
**0.10.0 was staged and withdrawn before approval; this is that release, re-cut.** The conformance corpus
|
|
106
|
+
was re-sealed after 0.10.0 was staged — its root moved `32fa90a6…` → `28bbf4ef…` when the vector tree was
|
|
107
|
+
brought inside the prose gates — so the staged `lcp-conformance` tarball carried a seal that no longer
|
|
108
|
+
matched the repository. The seal is what proves corpus authenticity to an independent implementer, and a
|
|
109
|
+
published version cannot be replaced, so the whole set was rejected and re-cut rather than shipping one
|
|
110
|
+
package that disagreed with its own source. No version 0.10.0 exists on the registry.
|
|
111
|
+
|
|
112
|
+
Released as part of a flat `0.10.1` across the whole suite. One number describes the set that was built,
|
|
113
|
+
tested and sealed together, so a consumer never has to work out which combination of versions was verified.
|
|
114
|
+
|
|
115
|
+
This package has no source change of its own in this release. What moved across the suite: three exported
|
|
116
|
+
names were corrected before anyone depends on them — `USDC_DECIMALS` became rail-qualified after it was
|
|
117
|
+
found to mean 6 on three rails and 7 on Stellar under one name, `VISA_TAP_PLACEMENT_TIER_A` lost the tier
|
|
118
|
+
it had baked into an identifier, and the memo codecs were named for the carrier they encode. Two wire
|
|
119
|
+
identities were respelled to match the vocabulary they are written into. Five gates were added or widened,
|
|
120
|
+
and a large number of documentation claims were corrected against the host specifications at HEAD.
|
|
121
|
+
|
|
3
122
|
## 0.9.0
|
|
4
123
|
|
|
5
124
|
First public release.
|
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` —
|
|
24
|
-
| **Carrier types** | `sha256
|
|
25
|
-
| **Spec** | x402 v2 (`x402-foundation/x402@
|
|
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(
|
|
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,18 +57,21 @@ 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.
|
|
59
|
-
|
|
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.
|
|
63
66
|
|
|
64
|
-
### Drift from LCP v1.37 §C.4
|
|
67
|
+
### Drift from LCP v1.37 §C.4 — recorded then adopted by v1.38
|
|
65
68
|
|
|
66
69
|
The host governs: its live specification is binding and LCP's Appendix C is an illustration.
|
|
67
70
|
Two differences, neither of which changes the design:
|
|
68
71
|
|
|
69
|
-
- v1.37
|
|
72
|
+
- v1.37 rendered the echo rule in RFC-2119 capitals ("MAY append to but **MUST NOT** delete or overwrite")
|
|
73
|
+
where the host states it in lower case and about the data rather than the map. **v1.38 §C.4 now says
|
|
74
|
+
exactly that**, so this item is discharged; it is kept because it is why the package reads the way it does
|
|
70
75
|
and states it about the extensions map. The live spec states it in lower case and about the `info` payload.
|
|
71
76
|
Same rule, weaker modality, one level lower.
|
|
72
77
|
- The live spec also carries `extra` on `PaymentPayload.accepted`, which §C.4 does not mention.
|
|
@@ -148,52 +153,48 @@ canonical non-negative integer segment only, so `length` and every other array p
|
|
|
148
153
|
That relaxation lives in the kit rather than in a private loop here, because a read rule nobody can find is
|
|
149
154
|
the same defect as a carrier nobody can find.
|
|
150
155
|
|
|
151
|
-
##
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
unconditionally.
|
|
184
|
-
- **`LEGAL_CONTEXT_SCHEMA_REF` points at a document the LCP TSC has not published yet**
|
|
185
|
-
(`https://legalcontextprotocol.org/schemas/lcp-extension.json`). It is byte-identical to what emitters
|
|
186
|
-
emits, and it is never fetched by anything in x402. Publishing it — or repointing it — is a **wire change
|
|
187
|
-
and must be made in both repos at once**; repointing it here alone would recreate the drift this package
|
|
188
|
-
exists to close.
|
|
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.
|
|
189
188
|
|
|
190
189
|
## Provenance
|
|
191
190
|
|
|
192
|
-
Cut against x402 v2
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
|
197
198
|
divergence written down is governed; the same divergence unwritten is the undisclosed-drift defect wearing
|
|
198
199
|
a new name.
|
|
199
200
|
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,28 +1,44 @@
|
|
|
1
1
|
import type { PlacementManifest } from "@integraledger/lcp-binding-core";
|
|
2
2
|
/**
|
|
3
|
-
* The carrier's JSON Schema, INLINED rather than referenced
|
|
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.
|
|
4
12
|
*
|
|
5
|
-
* x402 makes `schema` a REQUIRED member of an extension entry — "JSON Schema defining
|
|
6
|
-
* structure of info" — so whatever goes here is on the wire of every challenge.
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
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.
|
|
10
25
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
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.
|
|
18
33
|
*
|
|
19
34
|
* Changing this is a WIRE change — it appears in every challenge — so it is a frozen literal rather than a
|
|
20
|
-
* value assembled at call time.
|
|
35
|
+
* value assembled at call time, and the drift gate is what keeps the frozen copy honest.
|
|
21
36
|
*/
|
|
22
37
|
export declare const LEGAL_CONTEXT_SCHEMA: Readonly<Record<string, unknown>>;
|
|
23
38
|
/**
|
|
24
39
|
* x402 reference placement — the HTTP-layer carrier, cut against the live x402 v2 specification
|
|
25
|
-
* (`x402-foundation/x402@
|
|
40
|
+
* (`x402-foundation/x402@db5da2e65952`, `specs/x402-specification-v2.md`, read 2026-08-11; gate discharged
|
|
41
|
+
* in the README).
|
|
26
42
|
*
|
|
27
43
|
* **THE CANONICAL REPOSITORY IS THE FOUNDATION'S, and the citation moved on 2026-08-08.** Every
|
|
28
44
|
* reference here named `coinbase/x402`, which the GitHub API reports as `"fork": true`;
|
|
@@ -65,39 +81,47 @@ export declare const LEGAL_CONTEXT_SCHEMA: Readonly<Record<string, unknown>>;
|
|
|
65
81
|
* would emit something no x402 counterparty parses. This is the reason an alias declares its own `encoding`
|
|
66
82
|
* at all.
|
|
67
83
|
*
|
|
68
|
-
* **The alias
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
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.
|
|
72
96
|
*
|
|
73
97
|
* **The alias is index 0 only.** A locator names one path. `accepts[0]` is what buyer parsers read, and the
|
|
74
98
|
* reason is substantive: the reference must bind to the requirement actually being paid, and searching every
|
|
75
99
|
* requirement would let a seller park a second set of terms on an alternative it never expects to be chosen.
|
|
76
100
|
*
|
|
77
|
-
* **`
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* cannot cover both. `sha256` is the integrity carrier: §C.4's illustration carries one, emitters carry one,
|
|
88
|
-
* and the bare alias is fixed to it. `url` is the §8.1 discovery form, admitted because the canonical
|
|
89
|
-
* slot is a general reference-object slot and the kit puts the integrity-versus-discovery decision at the
|
|
90
|
-
* READER — `carrierClass` plus `requireIntegrity` — not in the permission list; ACP and UCP permit it for the
|
|
91
|
-
* same reason. `ipfs`/`ar` are excluded on a ground that does NOT apply to `url`: they are ALTERNATIVE
|
|
92
|
-
* integrity carriers, so admitting one adds no capability `sha256` does not already discharge while
|
|
93
|
-
* advertising a content-addressed transport no x402 counterparty resolves — a claim about the ecosystem
|
|
94
|
-
* rather than a description of it.
|
|
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.
|
|
95
111
|
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
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.
|
|
101
125
|
*/
|
|
102
126
|
export declare const X402_PLACEMENT: PlacementManifest;
|
|
103
127
|
//# sourceMappingURL=manifest.d.ts.map
|
package/dist/manifest.d.ts.map
CHANGED
|
@@ -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
|
|
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"}
|