@reclaimprotocol/agent 0.1.23 → 0.1.24

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 (152) hide show
  1. package/README.md +55 -20
  2. package/lib/authoring/backend.d.ts +5 -4
  3. package/lib/authoring/tools.js +30 -30
  4. package/lib/cdp/browser-proxy.d.ts +78 -0
  5. package/lib/cdp/browser-proxy.js +186 -0
  6. package/lib/cdp/capture-engine.js +8 -7
  7. package/lib/cdp/client.d.ts +10 -0
  8. package/lib/cdp/geo.d.ts +10 -0
  9. package/lib/cdp/geo.js +36 -0
  10. package/lib/cdp/index.d.ts +1 -0
  11. package/lib/cdp/index.js +1 -0
  12. package/lib/live/cloudflared.d.ts +15 -0
  13. package/lib/live/cloudflared.js +268 -0
  14. package/lib/live/page.d.ts +20 -0
  15. package/lib/live/page.js +134 -0
  16. package/lib/live/tunnel.d.ts +23 -0
  17. package/lib/live/tunnel.js +120 -0
  18. package/lib/live/viewer.d.ts +28 -0
  19. package/lib/live/viewer.js +232 -0
  20. package/lib/mcp/api/operations.gen.js +34 -26
  21. package/lib/mcp/api/to-zod.d.ts +1 -1
  22. package/lib/mcp/api/to-zod.js +1 -1
  23. package/lib/mcp/api/tools.js +1 -1
  24. package/lib/mcp/instructions.d.ts +15 -2
  25. package/lib/mcp/instructions.js +101 -38
  26. package/lib/mcp/start.js +4 -3
  27. package/lib/mcp/tools/agent/analyze.js +5 -1
  28. package/lib/mcp/tools/agent/attach.d.ts +8 -1
  29. package/lib/mcp/tools/agent/attach.js +166 -22
  30. package/lib/mcp/tools/agent/build.d.ts +13 -8
  31. package/lib/mcp/tools/agent/build.js +22 -11
  32. package/lib/mcp/tools/agent/capture.js +5 -1
  33. package/lib/mcp/tools/agent/eval.js +3 -1
  34. package/lib/mcp/tools/agent/find.js +6 -5
  35. package/lib/mcp/tools/agent/how-it-works.d.ts +15 -7
  36. package/lib/mcp/tools/agent/how-it-works.js +57 -17
  37. package/lib/mcp/tools/agent/live-view.d.ts +21 -0
  38. package/lib/mcp/tools/agent/live-view.js +153 -0
  39. package/lib/mcp/tools/agent/pod-proof.d.ts +8 -0
  40. package/lib/mcp/tools/agent/pod-proof.js +64 -0
  41. package/lib/mcp/tools/agent/propose.js +4 -2
  42. package/lib/mcp/tools/agent/proxy.d.ts +3 -0
  43. package/lib/mcp/tools/agent/proxy.js +199 -0
  44. package/lib/mcp/tools/agent/publish.js +47 -77
  45. package/lib/mcp/tools/agent/session.d.ts +2 -1
  46. package/lib/mcp/tools/agent/session.js +26 -7
  47. package/lib/mcp/tools/authenticate/authenticate.js +26 -13
  48. package/lib/mcp/tools/mode/mode.d.ts +1 -1
  49. package/lib/mcp/tools/mode/mode.js +10 -22
  50. package/lib/old/client.d.ts +1 -1
  51. package/lib/old/client.js +1 -1
  52. package/lib/old/instructions.d.ts +9 -1
  53. package/lib/old/instructions.js +4 -6
  54. package/lib/old/login-cdp.d.ts +2 -2
  55. package/lib/old/login-cdp.js +46 -6
  56. package/lib/old/to-register.d.ts +3 -2
  57. package/lib/old/to-register.js +1 -1
  58. package/lib/old/tools/authenticate.js +9 -1
  59. package/lib/old/tools/get-provider-info.js +6 -5
  60. package/lib/old/tools/issue-app-credentials.js +3 -2
  61. package/lib/old/tools/publish.d.ts +1 -1
  62. package/lib/old/tools/publish.js +90 -131
  63. package/lib/old/tools/update-provider-metadata.d.ts +1 -1
  64. package/lib/old/tools/update-provider-metadata.js +1 -1
  65. package/lib/old/tools/verify-sdk-setup.js +1 -1
  66. package/lib/proof/attestor.d.ts +3 -3
  67. package/lib/proof/debug-response-dump.js +4 -4
  68. package/lib/proof/index.d.ts +1 -0
  69. package/lib/proof/index.js +1 -0
  70. package/lib/proof/page-replay.d.ts +1 -1
  71. package/lib/proof/page-replay.js +1 -1
  72. package/lib/proof/pod-prover.d.ts +47 -0
  73. package/lib/proof/pod-prover.js +155 -0
  74. package/lib/provider/dom-xpath.js +11 -9
  75. package/lib/provider/draft.d.ts +10 -7
  76. package/lib/provider/draft.js +11 -9
  77. package/lib/provider/injection-doc.d.ts +23 -1
  78. package/lib/provider/injection-doc.js +34 -21
  79. package/lib/provider/match-redaction-warning.d.ts +2 -2
  80. package/lib/provider/match-redaction-warning.js +3 -3
  81. package/lib/provider/matcher.js +13 -12
  82. package/lib/provider/secrets.d.ts +3 -3
  83. package/lib/provider/secrets.js +3 -3
  84. package/lib/provider/to-version.d.ts +7 -6
  85. package/lib/provider/to-version.js +3 -3
  86. package/lib/tools/registry.d.ts +5 -5
  87. package/lib/tools/registry.js +5 -5
  88. package/package.json +3 -3
  89. package/samples/demo-server/README.md +7 -6
  90. package/skill/SKILL.builder.md +356 -189
  91. package/skill/SKILL.common.md +323 -97
  92. package/skill/SKILL.old.md +340 -155
  93. package/skill/builder-mode.md +58 -17
  94. package/skill/hash-validation.md +170 -0
  95. package/skill/interception.md +235 -0
  96. package/skill/js-sdk-integration.md +13 -7
  97. package/skill/old-mode.md +60 -21
  98. package/skill/user-script.md +61 -0
  99. package/src/authoring/backend.ts +5 -4
  100. package/src/authoring/tools.ts +31 -30
  101. package/src/cdp/capture-engine.ts +8 -7
  102. package/src/cdp/client.ts +8 -0
  103. package/src/cdp/geo.ts +52 -0
  104. package/src/cdp/index.ts +1 -0
  105. package/src/consts.ts +1 -0
  106. package/src/live/cloudflared.ts +348 -0
  107. package/src/live/page.ts +145 -0
  108. package/src/live/tunnel.ts +157 -0
  109. package/src/live/viewer.ts +293 -0
  110. package/src/mcp/api/operations.gen.ts +34 -26
  111. package/src/mcp/api/to-zod.ts +1 -1
  112. package/src/mcp/api/tools.ts +1 -1
  113. package/src/mcp/instructions.ts +121 -48
  114. package/src/mcp/start.ts +4 -3
  115. package/src/mcp/tools/agent/analyze.ts +5 -1
  116. package/src/mcp/tools/agent/attach.ts +194 -22
  117. package/src/mcp/tools/agent/build.ts +26 -15
  118. package/src/mcp/tools/agent/capture.ts +5 -1
  119. package/src/mcp/tools/agent/eval.ts +4 -1
  120. package/src/mcp/tools/agent/find.ts +6 -5
  121. package/src/mcp/tools/agent/how-it-works.ts +62 -21
  122. package/src/mcp/tools/agent/live-view.ts +198 -0
  123. package/src/mcp/tools/agent/pod-proof.ts +90 -0
  124. package/src/mcp/tools/agent/propose.ts +4 -2
  125. package/src/mcp/tools/agent/publish.ts +52 -77
  126. package/src/mcp/tools/agent/session.ts +26 -5
  127. package/src/mcp/tools/authenticate/authenticate.ts +28 -13
  128. package/src/mcp/tools/mode/mode.ts +10 -22
  129. package/src/old/client.ts +2 -2
  130. package/src/old/instructions.ts +4 -10
  131. package/src/old/login-cdp.ts +50 -6
  132. package/src/old/to-register.ts +3 -2
  133. package/src/old/tools/authenticate.ts +9 -1
  134. package/src/old/tools/get-provider-info.ts +6 -5
  135. package/src/old/tools/issue-app-credentials.ts +3 -2
  136. package/src/old/tools/publish.ts +95 -131
  137. package/src/old/tools/update-provider-metadata.ts +1 -1
  138. package/src/old/tools/verify-sdk-setup.ts +1 -1
  139. package/src/proof/attestor.ts +3 -3
  140. package/src/proof/debug-response-dump.ts +4 -4
  141. package/src/proof/index.ts +8 -0
  142. package/src/proof/page-replay.ts +1 -1
  143. package/src/proof/pod-prover.ts +231 -0
  144. package/src/provider/dom-xpath.ts +11 -9
  145. package/src/provider/draft.ts +13 -10
  146. package/src/provider/injection-doc.ts +37 -24
  147. package/src/provider/match-redaction-warning.ts +3 -3
  148. package/src/provider/matcher.ts +13 -12
  149. package/src/provider/secrets.ts +3 -3
  150. package/src/provider/to-version.ts +11 -9
  151. package/src/tools/registry.ts +5 -5
  152. package/skill/injection-api.md +0 -364
@@ -1,364 +0,0 @@
1
- # Page injection: `jsUserScripts` / `customInjection`
2
-
3
- A provider can inject **plain JavaScript** that runs in the page before every
4
- page load. Builder stores it as `webSettings.jsUserScripts`; old-devtools stores
5
- it as `customInjection` (identical semantics). It is ordinary browser
6
- JavaScript — its job is to drive the page so the target API request(s) fire
7
- during a verification.
8
-
9
- ## What the script is for
10
-
11
- The verification client already **intercepts the page's network requests
12
- automatically** — the script does NOT necessarily ned to capture, forward, or "claim" them.
13
- Its primary job is to get the page into the state where those requests happen, using
14
- standard browser APIs:
15
-
16
- - **Navigate** with `window.location.href = '…'` / `location.assign(…)` / the
17
- history API.
18
- - **Interact**: select an element and `.click()` it, submit a form, dispatch
19
- events.
20
- - **Wait**: poll the DOM until login/mfa completes or content loads, then trigger
21
- the next step.
22
-
23
- Write it like any page script. It runs before EVERY page load, so keep it
24
- idempotent, cheap, and guarded against running at the wrong time.
25
-
26
- ## `window.Reclaim` — rarely needed, use sparingly
27
-
28
- `window.Reclaim` is an OPTIONAL bridge for a few edge cases only. Most providers
29
- never touch it. In particular it does **not** do navigation — use the standard
30
- browser APIs above for that, in plain `jsUserScripts` / `customInjection`.
31
-
32
- Do not reach for `window.Reclaim` by default. Only use it when an edge case
33
- genuinely requires it. Its full, authoritative API (every method, with exact
34
- signatures and semantics) is documented in
35
- `https://github.com/reclaimprotocol/provider-web-script-dev/blob/main/src/support/env.d.ts` — consult that file rather
36
- than guessing.
37
-
38
- > Both backends run the script: the Builder verification client installs
39
- > `jsUserScripts` to run before every page load (before navigating to the
40
- > provider's `initialUrl`), and old-devtools runs `customInjection` the same
41
- > way. (`window.Reclaim` itself is still only forwarded/available in the cases
42
- > noted above.)
43
-
44
- ## Interception mode: `injectionType` / `interceptorType`
45
-
46
- Separate from the script's CONTENT is HOW the client captures network traffic
47
- for it to consume — a per-version field alongside `customInjection`/
48
- `jsUserScripts`, not something you write into the script itself. Whichever
49
- mechanism is selected, the injected script consumes what it caught through
50
- the SAME API: `window.reclaimInterceptor.addResponseMiddleware(middleware,
51
- middlewareName?)`. That consumption API fires for any intercepted request
52
- under EVERY mechanism below, including `CDP` — it is not a HAWKEYE-specific
53
- thing.
54
-
55
- - **old-devtools** field: `injectionType`, one of `NONE | MSWJS | XHOOK | CDP |
56
- HAWKEYE` — confirmed the literal field name against a real snapshot of
57
- legacy-backend provider data (`packages/app/scripts/data/
58
- provider-configs.snapshot.json`, captured straight from the old-devtools
59
- Mongo `Provider`/`ProviderVersion.providerConfig`). The backend's own
60
- column default is `HAWKEYE`, not `NONE` — harmless for the standard flow
61
- (no interceptor script installed, so nothing calls
62
- `addResponseMiddleware`). `create_provider_version_from_capture`
63
- (old-devtools mode) now exposes `injectionType` as an author-settable
64
- input, with NO forced default — omit it and the backend's own `HAWKEYE`
65
- default applies, same as before this field was exposed at all; pass an
66
- explicit value (including `NONE`) if you want certainty either way. When
67
- passed, it's sent on both register and add-version; since it's unverified
68
- whether the register controller actually persists it (no source access to
69
- confirm), the tool's existing always-on add-version follow-up (see
70
- `disableRequestReplay` below) reapplies it regardless, so an explicit value
71
- is guaranteed correct by the time the follow-up version lands.
72
- - **builder** field: `webSettings.inapp.interceptorOptions.interceptorType`,
73
- one of `HAWKEYE | MSWJS | CDP` (no `NONE`/`XHOOK` in this schema — omitting
74
- `interceptorOptions` entirely is builder's NONE-equivalent).
75
- `create_provider_version_from_capture` (builder mode) exposes
76
- `interceptorOptions.interceptorType` as an author-settable input; passing
77
- it is how you opt INTO real interception. Omit it (the default) for the
78
- standard flow, and the tool correctly omits `inapp` — it does NOT default
79
- to `HAWKEYE` (that was a bug in an earlier version of this tool; fixed).
80
-
81
- **The mechanisms, and why you'd pick one over plain replay:**
82
- - **`CDP`** — interception at the browser/DevTools-protocol level, below the
83
- page's own JS. Works uniformly for every request the page makes, INCLUDING
84
- its own document/navigation load — no caveats, "works best."
85
- - **`HAWKEYE` / `MSWJS` / `XHOOK`** — interception done IN the page's JS
86
- context (different underlying libraries; same idea). These see the page's
87
- ordinary sub-resource/API requests fine, but the page's OWN document
88
- request is a special case — see `disableRequestReplay` below.
89
- - Reach for ANY of the above (over the plain replay flow, `injectionType:
90
- NONE` / no `inapp`) only when the data genuinely can't be reproduced by an
91
- independent replay — e.g. it depends on one-time server-side state (a
92
- nonce, a websocket push) that asking again wouldn't give you back. For
93
- everything else, the standard flow is simpler and is what `propose_provider`
94
- / `run_proof` are built for.
95
-
96
- ## `disableRequestReplay` / `isDocumentRequestReplayEnabled`
97
-
98
- **`create_provider_version_from_capture` always force-disables this now, in
99
- BOTH backends** — old-devtools always publishes `disableRequestReplay: true`
100
- (the tool auto-follows a fresh register with an add-version call to apply
101
- it, since the register endpoint hardcodes it `false` regardless of what's
102
- sent). Builder always publishes `isDocumentRequestReplayEnabled: false`
103
- under `webSettings.clientOptions.inapp.interceptorOptions` **whenever you
104
- pass `interceptorOptions` at all** — but if you don't (the standard,
105
- non-interception flow), the tool omits `inapp` entirely rather than forcing
106
- a default `interceptorType`, because builder's schema has no `NONE`
107
- interceptorType: omitting the whole object IS its no-interception signal,
108
- and defaulting it to (even inert) `HAWKEYE` would misrepresent the
109
- provider. There is no author-facing way to turn document replay back on
110
- through either tool once `interceptorOptions`/`injectionType` is in play.
111
- The rest of this section explains the underlying platform semantics for
112
- context (e.g. reading an existing provider someone else published outside
113
- these tools) — the replay sub-flag itself is no longer something you
114
- configure here.
115
-
116
- **This does NOT gate `addResponseMiddleware` in general** — that fires for
117
- any intercepted request regardless of this field. What it actually controls:
118
- whether the CURRENT PAGE'S OWN document/navigation request (the request that
119
- loads the page's HTML — not a sub-resource or API call) is sent for real
120
- over the network at all, for the non-CDP mechanisms above. And it only
121
- matters if the request you're proving *is* that document request — e.g. the
122
- target value is embedded straight in the page's own HTML rather than
123
- fetched via a separate API call.
124
-
125
- - `false` (old-devtools' schema default) — the document request IS sent for
126
- real (for `HAWKEYE`/`MSWJS`/`XHOOK`), so `addResponseMiddleware` sees it
127
- like any other intercepted request. This is what you need if you're
128
- proving data straight out of the current page's own HTML.
129
- - `true` — the document request is constructed/synthesized locally instead
130
- of actually being sent, so the interceptor never observes it (every OTHER
131
- request the page makes is still intercepted normally). This exists as a
132
- **compatibility escape hatch**: some websites misbehave when their own
133
- top-level navigation request gets pulled through a JS-level interceptor —
134
- flip this to `true` for those sites. It's not a privacy or architecture
135
- choice, it's a per-site workaround.
136
- - **`CDP` is exempt from all of this** — it intercepts at the browser level,
137
- so the document request is never a special case for it.
138
- `disableRequestReplay`/`isDocumentRequestReplayEnabled` is simply inert
139
- when `injectionType`/`interceptorType` is `CDP`.
140
-
141
- Field names, per backend:
142
- - **old-devtools**: `disableRequestReplay` (boolean, schema default `false`).
143
- The dashboard's own field hint (verbatim): *"When enabled (default), the
144
- main document HTTP request is actually sent to get the response. When
145
- disabled, the expected request and response are constructed locally
146
- without making the network call."* Note the double negative: the
147
- dashboard's checkbox is labeled "Enable Document Request Replay" and is
148
- CHECKED when `disableRequestReplay` is `false` — the checkbox and the field
149
- it writes are inverted from each other.
150
- - **builder**: `webSettings.inapp.interceptorOptions.isDocumentRequestReplayEnabled`
151
- (boolean, positive naming this time — `true` means the document request IS
152
- sent). Per its openapi description, the schema's own stated default is
153
- `false`. Don't assume the two backends' defaults line up just because the
154
- concept is the same — old effectively defaults to "sent", the builder
155
- schema's stated default is "not sent"; verify against whichever backend
156
- you're publishing to rather than carrying an assumption across.
157
-
158
- There is nothing to configure here through `create_provider_version_from_capture`
159
- any more (see the note at the top of this section) — both backends always
160
- publish with document replay disabled.
161
-
162
- ## `bodySniff` — unrelated to the above
163
-
164
- A per-request field (sits next to `url`/`method` on each `requestData` entry,
165
- not a version-level setting): `{ enabled: boolean, template: string }`.
166
-
167
- **`bodySniff.enabled` should be `true` whenever the request HAS a body at
168
- all** — this isn't a special case reserved for dynamic bodies. Only skip it
169
- (leave `enabled: false` / template empty) when the request genuinely sends
170
- no body (typically a GET). Both publish tools in this package already set
171
- `enabled`/`template` for you from `provider.body` for a normal
172
- capture-and-replay provider — you don't need to touch it directly there.
173
- Read the rest of this section if you're authoring a claim via live
174
- interception (`window.Reclaim.requestClaim`) whose body isn't simply the
175
- one literal string you captured once — most commonly because some SMALL
176
- part of it is dynamic per user/run (e.g. a user id, not necessarily the
177
- whole payload).
178
-
179
- **What actually gets hashed.** Confirmed directly from attestor-core
180
- (`hashProviderParams` in `@reclaimprotocol/attestor-core`): the claim/spec
181
- hash is `keccak256(canonicalStringify({ url, method, body, responseMatches,
182
- responseRedactions }))`, where `body` is whatever plain string sits in
183
- `params.body` — attestor-core does **no** template resolution, substitution,
184
- or `{{}}`-stripping on it; it hashes exactly the string it's given, verbatim.
185
- `bodySniff` as a concept (the `{enabled, template}` wrapper) doesn't exist
186
- inside attestor-core at all — it's translated to a plain `body: string`
187
- one layer up (old-devtools' `providerUtils.ts`: `body: bodySniff.enabled ?
188
- bodySniff.template : ''`), and THAT literal string — including any
189
- unresolved `{{paramName}}` text still in it — is what reaches
190
- `hashProviderParams`.
191
-
192
- **The consequence — and why this differs from `templateParams`.** A live
193
- claim's own `body` field (submitted via `requestClaim`) can ALSO be set to
194
- a literal template string containing `{{paramName}}`, rather than the real
195
- substituted content, with the real value supplied separately as a witness
196
- param. Since attestor-core hashes `body` as an opaque string with no
197
- resolution, a claim whose `body` is the literal template `T` and a spec
198
- whose `body`/`bodySniff.template` is that SAME literal template `T` hash
199
- identically — regardless of what real value the `{{paramName}}` hole stood
200
- for. This is the OPPOSITE of `templateParams`/`${var}` (used in
201
- `responseMatches`/`responseRedactions`), which — per the JS SDK's
202
- `generateSpecsFromRequestSpecTemplate` — genuinely gets substituted with a
203
- real value at verify time before hashing. Don't conflate the two: `${var}`
204
- in a match/redaction template IS resolved before hashing; `{{var}}` in a
205
- body template is NEVER resolved before hashing, on either side.
206
-
207
- **Practical use**: check exactly which part of the body actually changes
208
- across users/runs — usually it's one small field (an id, a token, a short
209
- list), not the whole payload. Template ONLY that minimal changing part;
210
- leave everything else as fixed literal text. For a body like
211
- `{"request":1,"type":"user_info","id":"12345"}` where only `id` varies, the
212
- template is `{"request":1,"type":"user_info","id":"{{REQ_BODY_USER_ID}}"}`
213
- — NOT e.g. `{{whole_body}}` wrapping the entire object, which would throw
214
- away the ability to structurally pin down everything else about the
215
- request. Then:
216
- 1. In the claim you build for `requestClaim`, set `bodySniff: { enabled:
217
- true, template: '{"request":1,"type":"user_info","id":"{{REQ_BODY_USER_ID}}"}' }`
218
- and add a matching witness/extracted param
219
- `REQ_BODY_USER_ID: theRealUserId` so the real value is still
220
- independently verifiable, just not part of the hash.
221
- 2. In the corresponding `allowedInjectedRequestData` entry, set `body` to
222
- that EXACT SAME literal template string (byte-for-byte, same
223
- `{{REQ_BODY_USER_ID}}` placeholder, same everything else) — NOT a
224
- `${var}` substitution, since `body` doesn't go through that substitution
225
- step.
226
- 3. Before publishing, sanity-check by hand that
227
- `template.replace('{{REQ_BODY_USER_ID}}', 'a real sample id')`
228
- reproduces the real captured/expected request body byte-for-byte — if it
229
- doesn't (extra whitespace, different key order, a field that turned out
230
- to ALSO vary but wasn't templated, etc.), the hashes won't match either,
231
- since the SPEC side uses the literal template as-is and the real claim's
232
- body must independently be built to match that same shape when the
233
- placeholder(s) are filled in. If MULTIPLE fields vary independently,
234
- template each one with its own `{{placeholder}}` and its own witness
235
- param — you're not limited to one hole per template.
236
-
237
- **Fallback — whole-body placeholder.** If precise per-field templating
238
- keeps causing hash mismatches (e.g. more fields turn out to vary than
239
- expected, key order isn't stable, whitespace differs run-to-run) and you
240
- can't pin down every variable field individually, fall back to templating
241
- the ENTIRE body as one placeholder instead: `{{REQ_BODY_GRD}}`. The `GRD`
242
- suffix matters — per the response-matching template engine
243
- (`convertTemplateToRegex`), a placeholder name ending in `GRD` is treated as
244
- a GREEDY match (`(.*)`, matching everything including embedded delimiters)
245
- rather than the default non-greedy `(.*?)` (which stops at the first
246
- occurrence of whatever comes after it in the template) — for a placeholder
247
- covering the entire remaining body with nothing fixed after it, greedy is
248
- what you want. This is strictly a fallback: it means NOTHING about the
249
- body's structure is pinned down by the hash anymore (any body at all would
250
- hash the same), so prefer the minimal-placeholder approach above whenever
251
- you can actually identify which fields vary.
252
-
253
- The same opaque-string hashing (confirmed directly from the attestor-core
254
- bundle shipped in this repo's `node_modules`) also applies to fields beyond
255
- `body`. The caller that invokes it against a live submitted claim to gate
256
- proof acceptance is `@reclaimprotocol/js-sdk`'s `verifyProof` — the next
257
- section covers how that gate works and how to configure a provider that
258
- satisfies it.
259
-
260
- ## Provider-hash validation (`verifyProof(proof, { providerId })`) — how it gates, and how to configure a provider that passes it
261
-
262
- The caller for the hashing described above is `@reclaimprotocol/js-sdk`'s
263
- `verifyProof`. When called with `{ providerId }` (the SDK's own
264
- integration guide treats this as the normal case), traced directly from
265
- that package's source (`src/utils/proofValidationUtils.ts`,
266
- `src/utils/providerUtils.ts`, `src/witness.ts`):
267
-
268
- 1. `assertValidateProof` → `fetchProviderHashRequirementsBy(providerId, …)`
269
- fetches this provider's CURRENT PUBLISHED CONFIG and builds a list of
270
- "expected hashes" — one per entry in `requestData` PLUS one per entry in
271
- `allowedInjectedRequestData` (old-devtools) / `allowedJsRequests`
272
- (builder) — same concept, different field name per backend; don't
273
- assume a doc or error naming one applies verbatim to the other.
274
- 2. Each expected hash is `hashRequestSpec` → `hashProofClaimParams({ url,
275
- method, body, responseMatches, responseRedactions })` — the SAME
276
- opaque-string hashing described above for `body`, applied to the WHOLE
277
- spec at once.
278
- 3. The REAL submitted proof's own `claimData.parameters` is hashed the
279
- SAME way, with `isOptional`-driven 2^N combinatorial expansion over
280
- `responseMatches`/`responseRedactions` PAIRS so an optional field can be
281
- either present or absent (`getProviderParamsAsCanonicalizedString`) —
282
- but this expansion ONLY touches match/redaction pairs, never `url`.
283
- 4. `assertValidProofsByHash` requires the real proof's hash to exactly
284
- equal ONE expected hash. No match → `verifyProof` rejects, even for a
285
- perfectly valid, correctly-signed proof whose `responseMatches` are
286
- 100% correct in shape.
287
-
288
- **Key constraint: `url` is hashed VERBATIM, with ZERO template/regex
289
- resolution — even when `urlType` is `REGEX` or `TEMPLATE`.** Confirmed
290
- directly from `generateSpecsFromRequestSpecTemplate` (the function that DOES
291
- resolve `${var}` placeholders in `responseMatches`/`responseRedactions`
292
- before hashing): it spreads every other field from the template unchanged,
293
- and never touches `url`. `urlType` is consumed by a DIFFERENT validation
294
- path entirely (matching an incoming live request against this spec at
295
- capture time) — never by this hash computation.
296
-
297
- **Check this before populating `allowedInjectedRequestData`/
298
- `allowedJsRequests` for a `verifyProof({ providerId })` use case:** if the
299
- real captured request URL contains ANY component that varies between two
300
- otherwise-identical sessions — a tracking nonce, a CSRF token, a
301
- cache-busting timestamp, a per-request trace id — the hash computed from
302
- your declared spec can NEVER equal the hash of any real future proof, no
303
- matter how correct `responseMatches`/`responseRedactions`/`body` are. This
304
- is common on JS-heavy sites: e.g. Slack regenerates `_x_id`, `_x_csid`, and
305
- `fp` on every single API call, so a captured `users.profile.getSections`
306
- URL is never identical twice, even for the same user seconds apart.
307
- Populating this field for such a provider is actively worse than leaving
308
- it empty — it converts "no provider-hash check was attempted" into
309
- "`verifyProof({ providerId })` rejects every real proof from this
310
- provider, unconditionally."
311
-
312
- **Verify before wiring this up**: capture the same endpoint twice, in two
313
- separate sessions (ideally two different users, or the same user on two
314
- different days), and diff the full request URL byte-for-byte.
315
- - Identical every time → safe to populate with `urlType: CONSTANT` and the
316
- literal URL.
317
- - Not identical → do not populate this field expecting it to gate
318
- `verifyProof({ providerId })`. There is no way to make a session-unique
319
- URL satisfy this specific mechanism, on either backend — this is a
320
- property of the hashing itself, not something fixable by editing
321
- `responseMatches`, `responseRedactions`, or `body` more carefully. See
322
- the fallback below.
323
-
324
- Also note, independent of the `url` constraint above: even with a stable URL,
325
- `responseMatches`/`responseRedactions` must be an EXACT structural match —
326
- not just similar in spirit. A provider whose extracted field SET varies
327
- per user (e.g. optional profile fields present for some users, absent for
328
- others) needs every such field marked `isOptional: true` on BOTH the match
329
- and its paired redaction, so the 2^N expansion produces a valid hash for
330
- the "field absent" case too. A field that's sometimes-present but never
331
- marked optional breaks the hash for any user missing it — the same failure
332
- mode as the `url` constraint above, just scoped to one field instead of the
333
- whole spec.
334
-
335
- **Fallback if provider-hash validation keeps causing problems.** Whether
336
- it's an unstable `url`, an un-marked optional field, or any other
337
- persistent hash mismatch you can't pin down — the way out on the verifying
338
- side is `verifyProof(proof, { dangerouslyDisableContentValidation: true })`.
339
- That skips step 1-4 above entirely and trusts only the attestor's
340
- cryptographic signature over the proof; it does NOT check that the proof's
341
- `url`/`method`/`body`/`responseMatches`/`responseRedactions` match anything
342
- you declared. A caller-supplied `{ hashes: [...] }` computed some other way
343
- is the alternative when you still want hash-equality enforced, just not
344
- derived from this provider's published config. Reach for
345
- `dangerouslyDisableContentValidation` deliberately, not as a silent
346
- default — it trades away the guarantee that the proof is shaped the way
347
- this provider's spec says it should be.
348
-
349
- ## Recognizing this on a provider you didn't author
350
-
351
- `get_provider_version_config` (old-devtools) and `get_provider_info` (public
352
- SDK backend) both return the raw `providerConfig`. If you see
353
- `injectionType`/`interceptorType` set to anything other than the inert
354
- default AND a `customInjection`/`jsUserScripts` that calls
355
- `reclaimInterceptor.addResponseMiddleware` (or references `MSWJS`/`XHook`),
356
- that provider is on the live-interception path, not the standard replay
357
- flow — read the script before assuming you can safely carry it forward or
358
- edit around it. If, on top of that, the request being proven IS the login/
359
- landing page itself (its `url` matches `loginUrl`, i.e. it's the document
360
- request, not a separate API call) and `injectionType`/`interceptorType`
361
- isn't `CDP`, also check `disableRequestReplay`/
362
- `isDocumentRequestReplayEnabled` — `true` there means that request was
363
- deliberately routed around live interception for site-compatibility
364
- reasons, not an oversight.