@ashlar-blue/x402-trust 0.3.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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +198 -0
  3. package/dist/attested-directory.d.ts +78 -0
  4. package/dist/attested-directory.d.ts.map +1 -0
  5. package/dist/attested-directory.js +163 -0
  6. package/dist/attested-directory.js.map +1 -0
  7. package/dist/badges.d.ts +131 -0
  8. package/dist/badges.d.ts.map +1 -0
  9. package/dist/badges.js +213 -0
  10. package/dist/badges.js.map +1 -0
  11. package/dist/catalog-mcp.d.ts +145 -0
  12. package/dist/catalog-mcp.d.ts.map +1 -0
  13. package/dist/catalog-mcp.js +409 -0
  14. package/dist/catalog-mcp.js.map +1 -0
  15. package/dist/census-reconcile.d.ts +69 -0
  16. package/dist/census-reconcile.d.ts.map +1 -0
  17. package/dist/census-reconcile.js +162 -0
  18. package/dist/census-reconcile.js.map +1 -0
  19. package/dist/directory-classify.d.ts +47 -0
  20. package/dist/directory-classify.d.ts.map +1 -0
  21. package/dist/directory-classify.js +54 -0
  22. package/dist/directory-classify.js.map +1 -0
  23. package/dist/discovery.d.ts +304 -0
  24. package/dist/discovery.d.ts.map +1 -0
  25. package/dist/discovery.js +926 -0
  26. package/dist/discovery.js.map +1 -0
  27. package/dist/evidence-ref.d.ts +96 -0
  28. package/dist/evidence-ref.d.ts.map +1 -0
  29. package/dist/evidence-ref.js +179 -0
  30. package/dist/evidence-ref.js.map +1 -0
  31. package/dist/facilitator-config.d.ts +83 -0
  32. package/dist/facilitator-config.d.ts.map +1 -0
  33. package/dist/facilitator-config.js +94 -0
  34. package/dist/facilitator-config.js.map +1 -0
  35. package/dist/index.d.ts +15 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +18 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/manifest-sig.d.ts +131 -0
  40. package/dist/manifest-sig.d.ts.map +1 -0
  41. package/dist/manifest-sig.js +262 -0
  42. package/dist/manifest-sig.js.map +1 -0
  43. package/dist/merkle-transcript.d.ts +34 -0
  44. package/dist/merkle-transcript.d.ts.map +1 -0
  45. package/dist/merkle-transcript.js +162 -0
  46. package/dist/merkle-transcript.js.map +1 -0
  47. package/dist/open-catalog.d.ts +111 -0
  48. package/dist/open-catalog.d.ts.map +1 -0
  49. package/dist/open-catalog.js +307 -0
  50. package/dist/open-catalog.js.map +1 -0
  51. package/dist/reconcile.d.ts +150 -0
  52. package/dist/reconcile.d.ts.map +1 -0
  53. package/dist/reconcile.js +136 -0
  54. package/dist/reconcile.js.map +1 -0
  55. package/dist/trust-sdk.d.ts +127 -0
  56. package/dist/trust-sdk.d.ts.map +1 -0
  57. package/dist/trust-sdk.js +147 -0
  58. package/dist/trust-sdk.js.map +1 -0
  59. package/dist/types.d.ts +213 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/types.js +68 -0
  62. package/dist/types.js.map +1 -0
  63. package/package.json +52 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Walter D. Hawkins
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,198 @@
1
+ # @ashlar-blue/x402-trust
2
+
3
+ **Find an x402 facilitator, and decide whether to trust it before you pay.**
4
+
5
+ Reference implementation of two proposed x402 extensions:
6
+
7
+ - **`discovery`** ([x402-foundation/x402 #2979](https://github.com/x402-foundation/x402/pull/2979)) — find a facilitator from a domain name via DNS `TXT` + `/.well-known/x402`. No central directory, no list, no gatekeeper.
8
+ - **`attestations`** ([#3000](https://github.com/x402-foundation/x402/pull/3000)) — verify what a service claims about itself *before* paying it: TEE attestation blocks and soulbound, revocable, evidence-linked reputation records.
9
+
10
+ Discovery finds a service. Attestations tell you whether to trust it.
11
+
12
+ ## Three verbs
13
+
14
+ An agent that spends money has to do three things. This library does each in a
15
+ handful of lines, with **zero dependencies** and — for the two verification
16
+ verbs — **zero network and zero trust in us**.
17
+
18
+ ```ts
19
+ import { checkService, Transcript, verifyReceipt } from '@ashlar-blue/x402-trust';
20
+
21
+ // 1. BEFORE YOU PAY — resolve a service and get a plain decision + reasons.
22
+ const t = await checkService('facilitator.example');
23
+ if (!t.safeToPay) throw new Error(t.reasons.join('; ')); // e.g. off-domain resource, stale manifest
24
+
25
+ // 2. WHAT YOU PRODUCE — commit the work your agent did to a Merkle transcript.
26
+ const work = new Transcript()
27
+ .add('inv-002', 'GET /price?asset=XRP', '{"price":"2.99"}');
28
+ const root = work.root(); // sign or anchor this ONE value
29
+ const receipt = work.prove(0); // hand a counterparty a proof of just their line
30
+
31
+ // 3. WHAT YOU'RE HANDED — verify a receipt offline, trusting no one.
32
+ verifyReceipt(receipt); // true iff the work was in the committed set
33
+ ```
34
+
35
+ `checkService` composes the hardened resolver (same-origin enforcement,
36
+ SSRF/DoS guards, HTTPS-only redirects, live `/supported` cross-check) into one
37
+ call, so you do not re-implement the adversarial edge cases. The verification
38
+ functions are pure [RFC 6962](https://www.rfc-editor.org/rfc/rfc6962) Merkle
39
+ math: if a proof verifies, the claim is true no matter who produced it — an
40
+ enterprise does not have to trust FlareClaw to use FlareClaw's proofs.
41
+
42
+ Three more offline verifiers cover the artifacts the catalog and reconciler
43
+ emit — `verifyDirectoryEntry` (one host in a census), `verifyReconcileRow` (one
44
+ row of a two-observer reconciliation), and the signed-manifest / evidence
45
+ grammar below.
46
+
47
+ **Runnable tour:** [`examples/demo.mjs`](./examples/demo.mjs) — `npm run build && node examples/demo.mjs`. Produces work, verifies it, catches a tamper, and refuses a spoofed facilitator, all offline.
48
+
49
+ The rest of this document is the building blocks `checkService` sits on, for
50
+ callers who want the lower-level surface.
51
+
52
+ ## The Open Catalog, self-hostable (staged for 0.2.0)
53
+
54
+ Two further modules ship the catalog itself as a reference implementation —
55
+ the "anyone can rebuild this from public data" claim, made executable:
56
+
57
+ - **`open-catalog`** — the catalog engine: mirror the CDP Bazaar via its own
58
+ public API (with attribution), crawl `discovery` records breadth-first with
59
+ per-operator frontier-diversity bounds, and serve a provenance-tagged,
60
+ sha256-digested snapshot. Run it yourself and you are an independent
61
+ observer, not a mirror of ours.
62
+ - **`catalog-mcp`** — a transport-free MCP server core for that catalog:
63
+ stateless protocol revision 2026-07-28 primary, sessionless legacy
64
+ `initialize` shim, four read tools, a signable Server Card, and evidence
65
+ (snapshot digest + on-chain anchor) carried in BOTH `_meta` and readable
66
+ content. Wire it to any HTTP framework in ~20 lines; every dependency is
67
+ injected.
68
+
69
+ ## Zero dependencies
70
+
71
+ `npm ls` returns nothing. That is deliberate: a library whose job is *"decide whether to trust a stranger before sending them money"* cannot credibly arrive with a dependency tree you will never read. The entire trust path — DNS record parsing, manifest validation, redirect handling, the `eth_call`, the digest grammar — is auditable in one sitting.
72
+
73
+ The one consequence: we cannot compute `keccak256` at runtime, so the badge function selector and well-known `kind` hashes are compile-time constants. They are [asserted against `ethers` in the test suite](https://gitlab.com/flareclaw-saving-demo-group/flareclaw-verifier), so they are verified, not trusted. You can pass your own hasher for any other kind.
74
+
75
+ ## Install
76
+
77
+ ```bash
78
+ npm install @ashlar-blue/x402-trust
79
+ ```
80
+
81
+ ## Find a facilitator from a domain name
82
+
83
+ ```ts
84
+ import { resolveX402 } from '@ashlar-blue/x402-trust';
85
+
86
+ const r = await resolveX402('flareclaw.app');
87
+
88
+ r.via; // 'dns-txt' — found via _x402.flareclaw.app
89
+ r.manifest.facilitator.baseUrl;
90
+ r.liveKinds; // fetched from /supported — authoritative over the manifest
91
+ r.kindsMatchLive; // false = the operator's manifest is stale
92
+ r.attestationUsable // false = treat any TEE claim as absent
93
+ ```
94
+
95
+ Runs in Node out of the box. In a browser or a worker, inject a DNS resolver (DoH) and it works unchanged:
96
+
97
+ ```ts
98
+ await resolveX402('flareclaw.app', { resolveTxt: myDohResolver, fetchImpl: fetch });
99
+ ```
100
+
101
+ ### What it refuses to do
102
+
103
+ These are the extension's security rules, enforced rather than documented:
104
+
105
+ | Situation | Behaviour |
106
+ |---|---|
107
+ | `wk` in the TXT record points off-domain | hard error (spoofing signal) |
108
+ | Manifest redirects off-domain | hard error — the in-domain rule is re-applied to **every hop**, so the check runs on where the bytes came from, not where you asked |
109
+ | `facilitator.baseUrl` is off-domain | hard error — otherwise any domain could claim someone else's facilitator, and point every crawler at them |
110
+ | TEE block whose `verifier` is not a dereferenceable HTTPS URL | `attestationUsable: false` — an unverifiable claim must not read as a verified one |
111
+
112
+ ## Decide whether to trust it
113
+
114
+ The important part, and the part most callers get wrong:
115
+
116
+ ```ts
117
+ import { checkOperatorTrust } from '@ashlar-blue/x402-trust';
118
+
119
+ const trust = await checkOperatorTrust({
120
+ manifest: r.manifest,
121
+ rpcUrl: 'https://coston2-api.flare.network/ext/C/rpc',
122
+ registry: '0xb02f83e994830C4954c89C10482665A3963229c5', // PIN THIS
123
+ subject: r.manifest.badges.subject,
124
+ kind: 'x402-facilitator-attested',
125
+ });
126
+
127
+ trust.verified; // was this operator ever verified? (registry)
128
+ trust.liveAttestation; // is it in that mode right now? (manifest)
129
+ trust.attestedNow; // both — this is what you gate on
130
+ ```
131
+
132
+ ### Why these are three values and not one
133
+
134
+ A registry record is soulbound, therefore **durable**. TEE attestation is **transient**. Point the first at the second and you get a claim that is true when written and silently false later, with nothing in the record to reveal the drift.
135
+
136
+ We shipped exactly that bug. Our own `x402-facilitator-attested` record read `active` on-chain while our own manifest honestly reported `attestation: none`, because the enclave was down. The record was not lying about what it attested — a verification really did happen, and its evidence still hashes — it was being *read* as a liveness signal it cannot carry.
137
+
138
+ So:
139
+
140
+ | Question | Source of truth |
141
+ |---|---|
142
+ | *Was this operator verified, by whom, against what evidence?* | the registry record (durable, revocable) |
143
+ | *Is it operating that way right now?* | the operator's live manifest (self-degrading) |
144
+
145
+ `checkOperatorTrust` keeps them apart and only combines them explicitly. It **fails closed**: anything it cannot establish yields `attestedNow: false`.
146
+
147
+ `hasActiveBadge` likewise **throws rather than returning `false`** when it cannot reach the chain. "I could not check" and "I checked and it is not active" are different answers, and collapsing them is how an RPC outage becomes a silent trust upgrade.
148
+
149
+ ## Drop into the official SDK (`@x402/core`, Stripe Machine Payments)
150
+
151
+ Every integration built from the [Stripe Machine Payments sample](https://docs.stripe.com/payments/machine/x402) (and most others built on `@x402/core`) hardcodes its facilitator:
152
+
153
+ ```ts
154
+ import { facilitator } from "@coinbase/x402"; // ← a constant
155
+ const client = new HTTPFacilitatorClient(facilitator);
156
+ ```
157
+
158
+ `discoverFacilitator()` turns that constant into a resolved, verified choice — one line changes:
159
+
160
+ ```ts
161
+ import { HTTPFacilitatorClient } from "@x402/core/server";
162
+ import { discoverFacilitator } from "@ashlar-blue/x402-trust";
163
+
164
+ const { config, resolution } = await discoverFacilitator("facilitator.example.com", {
165
+ scheme: "exact",
166
+ network: "eip155:8453", // require support, judged against the LIVE /supported
167
+ });
168
+ const client = new HTTPFacilitatorClient(config); // drop-in
169
+
170
+ resolution.attestationUsable; // and you know whether its TEE claim is checkable
171
+ ```
172
+
173
+ No dependency on `@x402/core` is taken — the returned `config` is a plain `{ url, timeoutMs? }` matching core's `FacilitatorConfig` shape, so your SDK version is the only SDK version involved.
174
+
175
+ One honest constraint: `HTTPFacilitatorClient` calls the fixed paths `/verify`, `/settle`, `/supported`. A discovered manifest declaring different endpoint paths cannot be expressed as a `FacilitatorConfig`, so `discoverFacilitator()` **fails at resolve time** (`reason: "endpoints"`) instead of handing you a config that 404s at pay time.
176
+
177
+ ## Evidence references
178
+
179
+ Records point at the evidence that justified them. A bare URI has the trust model of brand reputation — it can be edited or taken down after the fact — so records carry a digest:
180
+
181
+ ```ts
182
+ import { encodeEvidenceRef, parseEvidenceRef, sameEvidence, canonicalJson } from '@ashlar-blue/x402-trust';
183
+
184
+ const ref = encodeEvidenceRef({
185
+ alg: 'sha256',
186
+ hex: '...',
187
+ ref: 'https://api.flareclaw.app/evidence/<digest>.json',
188
+ });
189
+ // "x402ev/1; digest=sha256:...; ref=https://..."
190
+ ```
191
+
192
+ Verify one by fetching the artifact, canonicalizing it ([RFC 8785 JCS](https://www.rfc-editor.org/rfc/rfc8785)), hashing, and comparing to the digest. A fetched artifact that **mismatches** its digest is evidence-invalid and must be a hard negative; an artifact that is merely **unreachable** should down-weight the record, not revoke it. Link rot and tampering are different failures.
193
+
194
+ ## Status
195
+
196
+ `0.2.0`. Both extensions are open pull requests, not ratified standards — the wire formats may change in response to review, and this package tracks them. It is published because a specification with an installable implementation is a proposal with users attached, and one without is a PDF. Requires Node ≥20 (the manifest signer validates UTF-16 well-formedness via `String.prototype.isWellFormed`).
197
+
198
+ MIT.
@@ -0,0 +1,78 @@
1
+ export declare const DIRECTORY_SCHEMA = "x402-attested-directory/1";
2
+ /**
3
+ * What a census can say about a host. Ordered strongest to weakest claim, and
4
+ * deliberately parallel to the fc-liveness vocabulary: `unreachable` is a
5
+ * statement about OUR vantage on this date, never about the host.
6
+ */
7
+ export type DirectoryVerdict = 'discoverable-verified' | 'discoverable' | 'one-edit-away' | 'not-discoverable' | 'unreachable';
8
+ export interface DirectoryEntry {
9
+ domain: string;
10
+ verdict: DirectoryVerdict;
11
+ /** One line a reader can check against the columns, never an accusation. */
12
+ detail: string;
13
+ /** The record as observed, verbatim, when there was one. */
14
+ record?: string;
15
+ }
16
+ export interface Census {
17
+ schema: typeof DIRECTORY_SCHEMA;
18
+ observedAt: string;
19
+ observer: string;
20
+ /** sha256 of the program that produced this. "Re-run it" needs a referent. */
21
+ methodDigest: string;
22
+ /** How the target set was chosen — a census over a hand-picked set is not a census. */
23
+ targetSource: string;
24
+ size: number;
25
+ inputRoot: string;
26
+ outputRoot: string;
27
+ tally: Record<DirectoryVerdict, number>;
28
+ /** Present ONLY when the run was genuinely attested. Absent means not. */
29
+ attestation?: {
30
+ type: string;
31
+ imageDigest?: string;
32
+ tokenDigest?: string;
33
+ };
34
+ }
35
+ export declare function buildCensus(entries: DirectoryEntry[], meta: {
36
+ observer: string;
37
+ methodDigest: string;
38
+ targetSource: string;
39
+ observedAt: string;
40
+ attestation?: Census['attestation'];
41
+ }): Census;
42
+ /** The digest a census is anchored under. */
43
+ export declare function censusDigest(c: Census): string;
44
+ /** Prove ONE host's verdict without disclosing the rest of the census. */
45
+ export declare function proveEntry(entries: DirectoryEntry[], index: number): {
46
+ entry: DirectoryEntry;
47
+ index: number;
48
+ size: number;
49
+ proof: string[];
50
+ };
51
+ /**
52
+ * Check one host's verdict against a published outputRoot, holding nothing
53
+ * else. This is what makes the census checkable retail rather than wholesale.
54
+ */
55
+ export declare function verifyEntry(entry: DirectoryEntry, index: number, size: number, proof: string[], outputRoot: string): boolean;
56
+ /**
57
+ * Re-derive both roots from a full target list and result set. A second
58
+ * observer runs this against their OWN sweep: matching roots mean identical
59
+ * observations, and differing roots localise the disagreement rather than
60
+ * hiding it. Disagreement is the signal.
61
+ */
62
+ export declare function recomputeRoots(entries: DirectoryEntry[]): {
63
+ inputRoot: string;
64
+ outputRoot: string;
65
+ };
66
+ /**
67
+ * Compare two censuses over the same targets. Returns the domains whose
68
+ * verdicts differ — the two-observer diff that makes a directory falsifiable.
69
+ */
70
+ export declare function diffCensus(a: DirectoryEntry[], b: DirectoryEntry[]): {
71
+ sameTargets: boolean;
72
+ disagreements: {
73
+ domain: string;
74
+ a: DirectoryVerdict;
75
+ b: DirectoryVerdict;
76
+ }[];
77
+ };
78
+ //# sourceMappingURL=attested-directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attested-directory.d.ts","sourceRoot":"","sources":["../src/attested-directory.ts"],"names":[],"mappings":"AA6CA,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAE5D;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GACtB,uBAAuB,GACvB,cAAc,GACd,eAAe,GACf,kBAAkB,GAClB,aAAa,CAAC;AAEpB,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,gBAAgB,CAAC;IAC1B,4EAA4E;IAC5E,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,MAAM;IACnB,MAAM,EAAE,OAAO,gBAAgB,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,YAAY,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC;IACxC,0EAA0E;IAC1E,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9E;AA2DD,wBAAgB,WAAW,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,IAAI,EAAE;IACzD,QAAQ,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;CACvC,GAAG,MAAM,CAgBT;AAED,6CAA6C;AAC7C,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAG9C;AAED,0EAA0E;AAC1E,wBAAgB,UAAU,CAAC,OAAO,EAAE,cAAc,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG;IAClE,KAAK,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,EAAE,CAAC;CACvE,CAIA;AAED;;;GAGG;AACH,wBAAgB,WAAW,CACvB,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GACxF,OAAO,CAET;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAKnG;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAAE,cAAc,EAAE,EAAE,CAAC,EAAE,cAAc,EAAE,GAAG;IAClE,WAAW,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,gBAAgB,CAAC;QAAC,CAAC,EAAE,gBAAgB,CAAA;KAAE,EAAE,CAAC;CACvG,CAWA"}
@@ -0,0 +1,163 @@
1
+ // GENERATED from src/x402/attested-directory.ts by scripts/sdk/sync-sdk.ts — do not edit here.
2
+ // The canonical module is the one FlareClaw runs in production; this is a copy so
3
+ // that what we publish and what we run cannot diverge. Edit the canonical file.
4
+ /**
5
+ * attested-directory — a census of x402 discovery records that a stranger can
6
+ * check without trusting the party who produced it.
7
+ *
8
+ * THE PROBLEM THIS EXISTS FOR. Every directory in this ecosystem asks you to
9
+ * trust an index. The measured consequence (our own sweep, 2026-08-18) is a
10
+ * catalogue where 78% of listings have exactly one payer ever, five listings
11
+ * carry 45% of all traffic, and listings age out for being unpopular. An index
12
+ * that deletes you for having no customers is not infrastructure, and "trust
13
+ * our index" is not a claim anyone should accept — including about ours.
14
+ *
15
+ * WHAT REPLACES THE TRUST. A census here commits to three things at once:
16
+ *
17
+ * methodDigest WHICH PROGRAM ran. Content-addressed, so "re-run it" is a
18
+ * well-defined instruction rather than an invitation to write
19
+ * your own and hope it matches.
20
+ * inputRoot WHICH TARGETS it ran over. Without this a publisher can
21
+ * quietly drop hosts whose verdicts it dislikes and no reader
22
+ * can tell — the omission problem, applied to directories.
23
+ * outputRoot WHAT IT FOUND, as an RFC 6962 tree rather than a flat hash.
24
+ *
25
+ * The tree is the part that makes it usable. A flat digest over 1,564 verdicts
26
+ * forces anyone checking ONE host to download all 1,564 and recompute. With a
27
+ * Merkle root, "host X had verdict Y in census C" is eleven hashes, and the
28
+ * reader never sees the other 1,563 — which matters because a directory that
29
+ * can only be checked wholesale will not be checked at all.
30
+ *
31
+ * WHAT THIS DOES NOT CLAIM. Being signed inside an enclave would prove the
32
+ * program was not tampered with between build and run. That is a stronger
33
+ * claim than a census signed anywhere else, and it is deliberately NOT made
34
+ * here: this module produces the artifact, and `attestation` is populated only
35
+ * when a caller supplies a real one. A census with no attestation says so.
36
+ *
37
+ * Discovery is not endorsement. A verdict describes what a program observed
38
+ * from one vantage at one time. It says nothing about whether the operator is
39
+ * honest, and the vocabulary below is chosen so it cannot be read that way.
40
+ */
41
+ import { createHash } from 'node:crypto';
42
+ import { jcsCanonical, assertJcsSafe } from './manifest-sig.js';
43
+ import { transcriptRoot, inclusionProof, verifyInclusion } from './merkle-transcript.js';
44
+ export const DIRECTORY_SCHEMA = 'x402-attested-directory/1';
45
+ /** Targets as tree leaves: the question asked, independent of the answer. */
46
+ const inputLeaf = (domain) => ({ drawId: domain, nonce: 'x402-directory-target', seed: '0x00' });
47
+ /**
48
+ * Verdict leaves. The domain is inside the leaf, so a proof cannot be moved
49
+ * from one host to another — without it, a verdict for a host that passed
50
+ * could be replayed as a verdict for one that did not.
51
+ */
52
+ const outputLeaf = (e) => {
53
+ // Commit the OBSERVED RECORD, not just the verdict and prose. Without it a
54
+ // proof bundle's `record` could be swapped for arbitrary content and still
55
+ // verify — a "verified" census entry carrying a fabricated record — and
56
+ // two sweeps differing only in the observed record would recompute
57
+ // identical roots, so recomputeRoots would miss a real observation
58
+ // difference. `record ?? null` keeps the leaf defined when a host has no
59
+ // record. (Crypto review D2, 2026-08-19.)
60
+ const core = { domain: e.domain, verdict: e.verdict, detail: e.detail, record: e.record ?? null };
61
+ assertJcsSafe(core);
62
+ return {
63
+ drawId: e.domain,
64
+ nonce: e.verdict,
65
+ seed: '0x' + createHash('sha256').update(jcsCanonical(core), 'utf8').digest('hex'),
66
+ };
67
+ };
68
+ /**
69
+ * Reject duplicate domains. A census with the same domain twice — one verdict
70
+ * at index i, a different one at index j — lets a publisher anchor ONE digest
71
+ * and then serve each retail verifier a valid inclusion proof for whichever
72
+ * verdict suits, both verifying against the same outputRoot. That is exactly
73
+ * the equivocation CT log operators are audited against, and it makes the
74
+ * retail claim "host X had verdict Y in census C" ill-defined. (Review D1.)
75
+ */
76
+ function assertCanonicalOrder(entries) {
77
+ // Unique AND ascending by domain, asserted rather than imposed. buildCensus
78
+ // must NOT silently re-sort: proveEntry/verifyEntry operate on the SAME
79
+ // array a caller holds, so if buildCensus reordered internally, a caller's
80
+ // index-based proof would not verify against the census's own outputRoot.
81
+ // classifySweep already yields this order; anything else is a caller bug.
82
+ const seen = new Set();
83
+ let prev = '';
84
+ for (const e of entries) {
85
+ if (seen.has(e.domain))
86
+ throw new Error(`[x402-directory] duplicate domain in census: ${e.domain}`);
87
+ if (prev && e.domain < prev) {
88
+ throw new Error(`[x402-directory] entries must be sorted by domain (${e.domain} after ${prev}) — use classifySweep`);
89
+ }
90
+ seen.add(e.domain);
91
+ prev = e.domain;
92
+ }
93
+ }
94
+ const EMPTY_TALLY = () => ({
95
+ 'discoverable-verified': 0, discoverable: 0, 'one-edit-away': 0,
96
+ 'not-discoverable': 0, unreachable: 0,
97
+ });
98
+ export function buildCensus(entries, meta) {
99
+ assertCanonicalOrder(entries);
100
+ const tally = EMPTY_TALLY();
101
+ for (const e of entries)
102
+ tally[e.verdict]++;
103
+ return {
104
+ schema: DIRECTORY_SCHEMA,
105
+ observedAt: meta.observedAt,
106
+ observer: meta.observer,
107
+ methodDigest: meta.methodDigest,
108
+ targetSource: meta.targetSource,
109
+ size: entries.length,
110
+ inputRoot: transcriptRoot(entries.map((e) => inputLeaf(e.domain))),
111
+ outputRoot: transcriptRoot(entries.map(outputLeaf)),
112
+ tally,
113
+ ...(meta.attestation ? { attestation: meta.attestation } : {}),
114
+ };
115
+ }
116
+ /** The digest a census is anchored under. */
117
+ export function censusDigest(c) {
118
+ assertJcsSafe(c);
119
+ return 'sha256:' + createHash('sha256').update(jcsCanonical(c), 'utf8').digest('hex');
120
+ }
121
+ /** Prove ONE host's verdict without disclosing the rest of the census. */
122
+ export function proveEntry(entries, index) {
123
+ const entry = entries[index];
124
+ if (!entry)
125
+ throw new Error(`index ${index} outside census of ${entries.length}`);
126
+ return { entry, index, size: entries.length, proof: inclusionProof(entries.map(outputLeaf), index) };
127
+ }
128
+ /**
129
+ * Check one host's verdict against a published outputRoot, holding nothing
130
+ * else. This is what makes the census checkable retail rather than wholesale.
131
+ */
132
+ export function verifyEntry(entry, index, size, proof, outputRoot) {
133
+ return verifyInclusion(outputLeaf(entry), index, size, proof, outputRoot);
134
+ }
135
+ /**
136
+ * Re-derive both roots from a full target list and result set. A second
137
+ * observer runs this against their OWN sweep: matching roots mean identical
138
+ * observations, and differing roots localise the disagreement rather than
139
+ * hiding it. Disagreement is the signal.
140
+ */
141
+ export function recomputeRoots(entries) {
142
+ return {
143
+ inputRoot: transcriptRoot(entries.map((e) => inputLeaf(e.domain))),
144
+ outputRoot: transcriptRoot(entries.map(outputLeaf)),
145
+ };
146
+ }
147
+ /**
148
+ * Compare two censuses over the same targets. Returns the domains whose
149
+ * verdicts differ — the two-observer diff that makes a directory falsifiable.
150
+ */
151
+ export function diffCensus(a, b) {
152
+ const byDomainB = new Map(b.map((e) => [e.domain, e]));
153
+ const sameTargets = a.length === b.length && a.every((e) => byDomainB.has(e.domain));
154
+ const disagreements = [];
155
+ for (const e of a) {
156
+ const other = byDomainB.get(e.domain);
157
+ if (other && other.verdict !== e.verdict) {
158
+ disagreements.push({ domain: e.domain, a: e.verdict, b: other.verdict });
159
+ }
160
+ }
161
+ return { sameTargets, disagreements };
162
+ }
163
+ //# sourceMappingURL=attested-directory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attested-directory.js","sourceRoot":"","sources":["../src/attested-directory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * keccak256(utf8(kind)) for the kinds this ecosystem has defined so far.
3
+ * Callers may also pass a pre-hashed 0x-prefixed bytes32 for any other
4
+ * kind, or supply their own keccak via `hashKind`.
5
+ */
6
+ export declare const KNOWN_BADGE_KINDS: Readonly<Record<string, string>>;
7
+ /** Resolve a kind name (or pass through a pre-hashed bytes32). */
8
+ export declare function resolveKind(kind: string, hashKind?: (s: string) => string): string;
9
+ export interface BadgeQuery {
10
+ /** JSON-RPC endpoint for the chain the registry lives on. */
11
+ rpcUrl: string;
12
+ /** Registry contract address. PIN THIS — see the note in hasActiveBadge. */
13
+ registry: string;
14
+ /** Address the record is about (facilitator settlement address, etc.). */
15
+ subject: string;
16
+ /** Kind name from KNOWN_BADGE_KINDS, or a pre-hashed bytes32. */
17
+ kind: string;
18
+ hashKind?: (s: string) => string;
19
+ fetchImpl?: typeof fetch;
20
+ timeoutMs?: number;
21
+ }
22
+ /**
23
+ * `hasActiveBadge(subject, kind)` on a conforming registry.
24
+ *
25
+ * ⚠️ Spec rule (Security considerations, "Registry substitution"): a
26
+ * malicious manifest can point at a lookalike registry, so a caller MUST
27
+ * pin or allowlist the registries it accepts. This function deliberately
28
+ * takes the registry as an explicit argument rather than reading it from
29
+ * a manifest — so that pinning is a decision you make, not one you
30
+ * forget to make.
31
+ *
32
+ * Throws on RPC failure rather than returning false: "I could not check"
33
+ * and "I checked and it is not active" are different answers, and
34
+ * collapsing them is how a chain outage turns into a silent trust
35
+ * upgrade. Callers decide their own failure policy.
36
+ */
37
+ export declare function hasActiveBadge(q: BadgeQuery): Promise<boolean>;
38
+ export interface BadgeQuorumQuery extends Omit<BadgeQuery, 'rpcUrl'> {
39
+ /** Independent RPC endpoints. Two must AGREE for an answer. */
40
+ rpcUrls: string[];
41
+ /** How many agreeing answers are required. Default 2 (or all, if fewer). */
42
+ quorum?: number;
43
+ }
44
+ export type BadgeQuorumResult = {
45
+ status: 'agreed';
46
+ active: boolean;
47
+ agreed: number;
48
+ queried: number;
49
+ }
50
+ /** Endpoints returned CONFLICTING answers — someone is lying or broken. */
51
+ | {
52
+ status: 'disputed';
53
+ answers: boolean[];
54
+ errors: string[];
55
+ }
56
+ /** Too few endpoints answered to reach quorum. Not a negative answer. */
57
+ | {
58
+ status: 'unavailable';
59
+ agreed: number;
60
+ quorum: number;
61
+ errors: string[];
62
+ };
63
+ /**
64
+ * `hasActiveBadge` across N independent endpoints, requiring agreement.
65
+ *
66
+ * ── WHY THIS EXISTS ──
67
+ * Badge status GATES PAYMENTS, and a single `eth_call` is one endpoint's
68
+ * word. Failover fixes liveness, not honesty: with one source, a hostile
69
+ * or buggy RPC can report `active` for a revoked venue (or `inactive` to
70
+ * censor a competitor) and the caller cannot tell. Any read whose answer
71
+ * we act on without a second source needs a second source.
72
+ *
73
+ * Three outcomes, deliberately distinct — collapsing them is how a trust
74
+ * check quietly stops being one:
75
+ * agreed — quorum endpoints returned the same answer. Act on it.
76
+ * disputed — endpoints DISAGREED. This is not "unknown", it is
77
+ * evidence that one of them is wrong about a
78
+ * payment-gating fact. Callers should refuse, not retry.
79
+ * unavailable — too few answered. "I could not check" ≠ "not active".
80
+ */
81
+ export declare function hasActiveBadgeQuorum(q: BadgeQuorumQuery): Promise<BadgeQuorumResult>;
82
+ export interface OperatorTrust {
83
+ /** The durable claim: a verification happened and has not been revoked. */
84
+ readonly verified: boolean;
85
+ /** The live claim, read from the operator's own manifest right now. */
86
+ readonly liveAttestation: 'tee' | 'none' | 'absent';
87
+ /**
88
+ * True only when the durable record AND the live state agree. This is
89
+ * the value to gate a state-dependent decision on.
90
+ */
91
+ readonly attestedNow: boolean;
92
+ readonly notes: readonly string[];
93
+ }
94
+ /**
95
+ * Evaluate an operator the way the spec requires for any decision that
96
+ * depends on RUNTIME state (e.g. "is settlement attested right now?").
97
+ *
98
+ * ── THE MISTAKE THIS FUNCTION EXISTS TO PREVENT ──
99
+ * A registry record is soulbound, therefore durable. TEE attestation is
100
+ * transient. Pointing the first at the second produces a claim that is
101
+ * true when written and silently false later, with nothing in the record
102
+ * to reveal the drift. We shipped exactly that: our own
103
+ * `x402-facilitator-attested` record read `active` on-chain while our own
104
+ * manifest honestly reported `attestation: none` because the enclave was
105
+ * down. The record was not lying about what it attested — a verification
106
+ * really did happen — it was being READ as a liveness signal it cannot
107
+ * carry.
108
+ *
109
+ * So the two answers are kept separate and only combined explicitly:
110
+ * verified — "was this operator verified?" (registry)
111
+ * liveAttestation — "is it in that mode right now?" (manifest)
112
+ * attestedNow — both, which is what you actually gate on.
113
+ *
114
+ * Fails CLOSED: any inability to establish the live state yields
115
+ * attestedNow === false.
116
+ */
117
+ export declare function checkOperatorTrust(opts: {
118
+ manifest: {
119
+ attestation?: {
120
+ type?: string;
121
+ verifier?: string;
122
+ };
123
+ };
124
+ rpcUrl: string;
125
+ registry: string;
126
+ subject: string;
127
+ kind: string;
128
+ hashKind?: (s: string) => string;
129
+ fetchImpl?: typeof fetch;
130
+ }): Promise<OperatorTrust>;
131
+ //# sourceMappingURL=badges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"badges.d.ts","sourceRoot":"","sources":["../src/badges.ts"],"names":[],"mappings":"AAyBA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAI7D,CAAC;AAKH,kEAAkE;AAClE,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,CAYlF;AAED,MAAM,WAAW,UAAU;IACvB,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,cAAc,CAAC,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CA6BpE;AAID,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC;IAChE,+DAA+D;IAC/D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,iBAAiB,GACvB;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AACxE,2EAA2E;GACzE;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,OAAO,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE;AAC9D,yEAAyE;GACvE;IAAE,MAAM,EAAE,aAAa,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAElF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,oBAAoB,CAAC,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA+B1F;AAID,MAAM,WAAW,aAAa;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,CAAC,eAAe,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IACpD;;;OAGG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC3C,QAAQ,EAAE;QAAE,WAAW,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC5B,GAAG,OAAO,CAAC,aAAa,CAAC,CAwCzB"}