@supercuts-walrus/backend 0.0.0-stage → 0.1.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/LICENSE +201 -0
- package/README.md +249 -2
- package/dist/audit.d.ts +52 -0
- package/dist/audit.js +157 -0
- package/dist/backend.d.ts +27 -0
- package/dist/backend.js +62 -0
- package/dist/buyer-setup.d.ts +6 -0
- package/dist/buyer-setup.js +241 -0
- package/dist/capability.d.ts +32 -0
- package/dist/capability.js +87 -0
- package/dist/claude.d.ts +44 -0
- package/dist/claude.js +98 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +29 -0
- package/dist/config.d.ts +20 -0
- package/dist/config.js +58 -0
- package/dist/dashboard.d.ts +5 -0
- package/dist/dashboard.js +21 -0
- package/dist/deal-adapter.d.ts +3 -0
- package/dist/deal-adapter.js +39 -0
- package/dist/deal-engine.d.ts +62 -0
- package/dist/deal-engine.js +211 -0
- package/dist/deal-wire.d.ts +16 -0
- package/dist/deal-wire.js +65 -0
- package/dist/deal.d.ts +79 -0
- package/dist/deal.js +228 -0
- package/dist/demo.d.ts +2 -0
- package/dist/demo.js +449 -0
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +13 -0
- package/dist/events.d.ts +27 -0
- package/dist/events.js +68 -0
- package/dist/facilitator.d.ts +21 -0
- package/dist/facilitator.js +173 -0
- package/dist/finality.d.ts +2 -0
- package/dist/finality.js +27 -0
- package/dist/hosted-exit-cli.d.ts +2 -0
- package/dist/hosted-exit-cli.js +37 -0
- package/dist/hosted-exit.d.ts +24 -0
- package/dist/hosted-exit.js +70 -0
- package/dist/hosted-seller-import.d.ts +2 -0
- package/dist/hosted-seller-import.js +70 -0
- package/dist/hosted-seller-repository.d.ts +31 -0
- package/dist/hosted-seller-repository.js +214 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +37 -0
- package/dist/indexer.d.ts +63 -0
- package/dist/indexer.js +60 -0
- package/dist/live-deal-ports.d.ts +41 -0
- package/dist/live-deal-ports.js +212 -0
- package/dist/live-index.d.ts +28 -0
- package/dist/live-index.js +149 -0
- package/dist/live-runtime.d.ts +82 -0
- package/dist/live-runtime.js +570 -0
- package/dist/live-seller-transport.d.ts +30 -0
- package/dist/live-seller-transport.js +169 -0
- package/dist/live-seller-verifier.d.ts +11 -0
- package/dist/live-seller-verifier.js +65 -0
- package/dist/mandate.d.ts +34 -0
- package/dist/mandate.js +178 -0
- package/dist/mcp-bootstrap.d.ts +2 -0
- package/dist/mcp-bootstrap.js +39 -0
- package/dist/mcp.d.ts +2 -0
- package/dist/mcp.js +187 -0
- package/dist/memory-worker.d.ts +2 -0
- package/dist/memory-worker.js +38 -0
- package/dist/memory.d.ts +15 -0
- package/dist/memory.js +17 -0
- package/dist/memwal.d.ts +35 -0
- package/dist/memwal.js +83 -0
- package/dist/negotiation.d.ts +23 -0
- package/dist/negotiation.js +58 -0
- package/dist/notes.d.ts +11 -0
- package/dist/notes.js +26 -0
- package/dist/onboarding-doctor.d.ts +1 -0
- package/dist/onboarding-doctor.js +76 -0
- package/dist/phase3.d.ts +100 -0
- package/dist/phase3.js +1144 -0
- package/dist/procurement.d.ts +8 -0
- package/dist/procurement.js +19 -0
- package/dist/protocol-engine.d.ts +121 -0
- package/dist/protocol-engine.js +149 -0
- package/dist/prover.d.ts +28 -0
- package/dist/prover.js +70 -0
- package/dist/relayer.d.ts +32 -0
- package/dist/relayer.js +43 -0
- package/dist/remote-buyer.d.ts +67 -0
- package/dist/remote-buyer.js +231 -0
- package/dist/resource-server.d.ts +2 -0
- package/dist/resource-server.js +24 -0
- package/dist/resource.d.ts +22 -0
- package/dist/resource.js +55 -0
- package/dist/seam.d.ts +2 -0
- package/dist/seam.js +117 -0
- package/dist/seller-catalog-generate.d.ts +2 -0
- package/dist/seller-catalog-generate.js +68 -0
- package/dist/seller-database.d.ts +41 -0
- package/dist/seller-database.js +80 -0
- package/dist/seller-directory-cli.d.ts +2 -0
- package/dist/seller-directory-cli.js +24 -0
- package/dist/seller-directory.d.ts +5 -0
- package/dist/seller-directory.js +50 -0
- package/dist/seller-live-cli.d.ts +2 -0
- package/dist/seller-live-cli.js +53 -0
- package/dist/seller-provision.d.ts +2 -0
- package/dist/seller-provision.js +84 -0
- package/dist/seller-register-cli.d.ts +2 -0
- package/dist/seller-register-cli.js +40 -0
- package/dist/seller-runtime.d.ts +60 -0
- package/dist/seller-runtime.js +96 -0
- package/dist/seller-service.d.ts +34 -0
- package/dist/seller-service.js +416 -0
- package/dist/seller-state.d.ts +19 -0
- package/dist/seller-state.js +60 -0
- package/dist/seller-stdio.d.ts +6 -0
- package/dist/seller-stdio.js +29 -0
- package/dist/seller-v2-provision.d.ts +2 -0
- package/dist/seller-v2-provision.js +71 -0
- package/dist/seller-watch.d.ts +2 -0
- package/dist/seller-watch.js +45 -0
- package/dist/settlement.d.ts +95 -0
- package/dist/settlement.js +141 -0
- package/dist/store.d.ts +36 -0
- package/dist/store.js +193 -0
- package/dist/sui.d.ts +49 -0
- package/dist/sui.js +77 -0
- package/dist/tatum.d.ts +17 -0
- package/dist/tatum.js +83 -0
- package/dist/transcript.d.ts +31 -0
- package/dist/transcript.js +52 -0
- package/dist/v2-exit-relayer.d.ts +34 -0
- package/dist/v2-exit-relayer.js +102 -0
- package/dist/v2-exit-worker.d.ts +2 -0
- package/dist/v2-exit-worker.js +97 -0
- package/dist/v2-exits.d.ts +30 -0
- package/dist/v2-exits.js +78 -0
- package/dist/v2-prefund.d.ts +19 -0
- package/dist/v2-prefund.js +43 -0
- package/dist/v2-relayer-cli.d.ts +2 -0
- package/dist/v2-relayer-cli.js +39 -0
- package/dist/wallet.d.ts +32 -0
- package/dist/wallet.js +90 -0
- package/dist/workflow-memory.d.ts +22 -0
- package/dist/workflow-memory.js +49 -0
- package/dist/x402-resource-service.d.ts +34 -0
- package/dist/x402-resource-service.js +154 -0
- package/dist/x402.d.ts +46 -0
- package/dist/x402.js +136 -0
- package/package.json +60 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,3 +1,250 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Supercuts backend, CLI, and MCP
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The backend implements Supercuts' private negotiation and settlement layer for
|
|
4
|
+
agents. Signed encrypted offers bind an agreement to a public x402 or shielded
|
|
5
|
+
payment. Walrus Memory carries typed owner rules and verified outcomes across
|
|
6
|
+
runs, so remembered context can guide a later deal. The role-separated CLI,
|
|
7
|
+
MCP, and SDK adapters share this runtime; settlement and policy decisions stay
|
|
8
|
+
here rather than being reimplemented in tool handlers.
|
|
9
|
+
|
|
10
|
+
Each buyer, seller, auditor, resource server, and relayer uses a separate
|
|
11
|
+
SQLite state path and a separate owner-only 32-byte state-key file. Secret
|
|
12
|
+
rows are encrypted before reaching SQLite. Do not reuse a role's key file.
|
|
13
|
+
|
|
14
|
+
## Development
|
|
15
|
+
|
|
16
|
+
For npm tarball verification and publishing handoff, see [the release guide](../docs/npm-release.md).
|
|
17
|
+
|
|
18
|
+
```sh
|
|
19
|
+
pnpm install
|
|
20
|
+
pnpm check
|
|
21
|
+
pnpm test
|
|
22
|
+
pnpm build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The package deliberately does not load `.env` itself. Process adapters validate
|
|
26
|
+
and inject configuration, keeping credentials out of logs and returned objects.
|
|
27
|
+
|
|
28
|
+
The `supercuts` binary emits one versioned JSON response. The `supercuts-mcp`
|
|
29
|
+
binary implements MCP JSON-RPC over newline-delimited stdio and exposes only the
|
|
30
|
+
tools allowed by the initialized role. Use separate `SUPERCUTS_HOME` directories
|
|
31
|
+
for buyer, seller, and auditor processes. Development homes initialized with
|
|
32
|
+
`--mock` always label receipts `deterministic-mock-not-funded`.
|
|
33
|
+
|
|
34
|
+
For a reusable live buyer, run `supercuts setup buyer --home <buyer-home>` and
|
|
35
|
+
follow [the self-service setup guide](../docs/self-service-setup.md). It creates
|
|
36
|
+
a standing v2 policy, signed seller sources, and a Codex MCP configuration.
|
|
37
|
+
For independent seller registration and buyer approval through the shared
|
|
38
|
+
Testnet directory, follow [the onboarding guide](../docs/testnet-onboarding.md).
|
|
39
|
+
|
|
40
|
+
MCP mutations are authorized by the owner-only `mandate.json` created at
|
|
41
|
+
initialization. Tool schemas reject unknown arguments (including the deprecated
|
|
42
|
+
`authorize` flag), require an operation ID for every mutation, and enforce the
|
|
43
|
+
mandate's command list, counterparties, assets, per-deal ceiling, total run
|
|
44
|
+
budget, privacy requirement, and expiry before creating an internal exact-intent
|
|
45
|
+
approval. Approval tokens are never returned to the agent.
|
|
46
|
+
|
|
47
|
+
Buyer mandates may additionally set `requireSettlementPlan` and narrow allowed
|
|
48
|
+
capability IDs, capability classes, and rails. `settlement.plan` deterministically
|
|
49
|
+
ranks validated manifests, applies only trusted typed memories, clamps the opening
|
|
50
|
+
and maximum amounts, and persists a five-minute canonical plan. Pass its
|
|
51
|
+
`planHash` to `pay.x402` or `pay.shielded`; the runtime binds the exact operation,
|
|
52
|
+
manifest, seller, endpoint/session, asset, network, rail, request, and amount
|
|
53
|
+
before signing or proving. `supercuts settlement approve-review` is an owner-only
|
|
54
|
+
CLI path and is intentionally absent from MCP and the agent SDK.
|
|
55
|
+
|
|
56
|
+
`@supercuts-walrus/sdk` is built from `packages/sdk`. It resolves the matching
|
|
57
|
+
`supercuts-seam` binary from this package and exchanges one protocol-v1 request
|
|
58
|
+
per subprocess. SDK mutations use exact-intent approval tokens instead of the
|
|
59
|
+
legacy CLI `--authorize` boolean.
|
|
60
|
+
|
|
61
|
+
Create a live Testnet profile from the repository root. Credential files must
|
|
62
|
+
be owner-only and contain the Sui private key and Tatum API key respectively:
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
chmod 600 .secrets/sui.key .secrets/tatum.key
|
|
66
|
+
pnpm --dir backend exec supercuts init --role buyer --home .state/live-buyer \
|
|
67
|
+
--deployment-config config/phase1.testnet.json \
|
|
68
|
+
--sui-key-file .secrets/sui.key --tatum-key-file .secrets/tatum.key \
|
|
69
|
+
--write-grpc https://fullnode.testnet.sui.io:443 \
|
|
70
|
+
--tatum-grpc https://sui-testnet.gateway.tatum.io:443 \
|
|
71
|
+
--rapidsnark circuits/build/tools/rapidsnark-src/build_prover/src/prover
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Live mutations require an `--operation-id`. Withdrawals additionally require a
|
|
75
|
+
32-byte `--recipient`. A failed or ambiguous broadcast retains the exact signed
|
|
76
|
+
bytes under that operation ID; retrying another payload with the same ID fails.
|
|
77
|
+
|
|
78
|
+
## Procurement demo
|
|
79
|
+
|
|
80
|
+
For the Codex buyer with three remote Claude-backed sellers, Supabase-backed seller state, and the separate Render facilitator, follow [the remote demo guide](../docs/remote-codex-demo.md). The remote path is live-only and gives Codex a buyer role MCP server; the three seller panes use the safe `supercuts-seller-watch` event stream.
|
|
81
|
+
|
|
82
|
+
`supercuts-demo` is the operator entry point:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
# Four isolated development roles and their MCP configuration.
|
|
86
|
+
supercuts-demo setup --state-dir .state/demo --mock
|
|
87
|
+
supercuts-demo doctor --state-dir .state/demo
|
|
88
|
+
supercuts-demo check --state-dir .state/demo --events evidence/run.jsonl
|
|
89
|
+
|
|
90
|
+
# Never submits a transaction; every event is relabeled recorded-funded-run.
|
|
91
|
+
supercuts-demo replay --state-dir .state/demo --events evidence/run.jsonl
|
|
92
|
+
supercuts-demo replay --events evidence/run.jsonl --serve --port 4173
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Omit `--mock` and provide the pinned deployment, Tatum, Sui gRPC, and optional
|
|
96
|
+
owner-only Mainnet memory configuration arguments to create live profiles. The
|
|
97
|
+
setup output prints only the buyer address/assets that need funding and the
|
|
98
|
+
public seller `payTo` address. `live --workflow workflow.json` refuses mock
|
|
99
|
+
profiles. Setup also requires `--workflow workflow.json` so the generated buyer
|
|
100
|
+
mandate can be narrowed to its exact capability IDs/classes and rails. The live
|
|
101
|
+
run creates a baseline plan, negotiates the public exact amount, executes the
|
|
102
|
+
seller-owned x402 resource, writes ten typed Mainnet memories plus an owner
|
|
103
|
+
privacy policy, restarts the buyer process, recalls citations without plaintext,
|
|
104
|
+
creates the shielded plan, deposits a buyer note, signs and accepts a
|
|
105
|
+
confidential agreement with the confidential seller, and settles it. The live
|
|
106
|
+
workflow must set `publicOperationId` to the operation ID registered by the
|
|
107
|
+
seller resource for `publicResourceUrl`. It must also set
|
|
108
|
+
`buyerDepositAmount` above the confidential capability's minimum price so the
|
|
109
|
+
private transfer can create a positive change note. Use a fresh operation ID
|
|
110
|
+
and resource registration for each recording. The JSONL stream describes x402 amount/payee as
|
|
111
|
+
public. Shielded settlement avoids a direct public payment revealing the deal
|
|
112
|
+
amount or counterparty; funding, deposits, withdrawals, gas, and pool activity
|
|
113
|
+
remain visible.
|
|
114
|
+
|
|
115
|
+
For a Claude-agent recording, export `CLAUDE_API_KEY` or provide
|
|
116
|
+
`live --claude-key-file <owner-only-file>`; a repository `.env` file is not
|
|
117
|
+
loaded automatically. Validate the credential against Anthropic before starting
|
|
118
|
+
a funded take.
|
|
119
|
+
The live path calls Claude for bounded buyer and seller decisions and records
|
|
120
|
+
only the model, action, and token counts. The backend still enforces capability,
|
|
121
|
+
amount, rail, and agreement bindings. Validate that recording with
|
|
122
|
+
`supercuts-demo check --require-claude`; an earlier funded run without these
|
|
123
|
+
agent-decision events does not satisfy that gate.
|
|
124
|
+
|
|
125
|
+
The optional `--memory-config-file` is imported from a mode-0600 JSON file and
|
|
126
|
+
stored only as encrypted role state. It has the `ManualMemoryConfig` fields
|
|
127
|
+
documented by `src/memwal.ts`; its Sui and embedding credentials never enter
|
|
128
|
+
profiles, prompts, MCP calls, events, or browser state. Live profiles reject
|
|
129
|
+
non-loopback embedding endpoints; use the repository's 1536-dimension adapter
|
|
130
|
+
for both writes and recalls.
|
|
131
|
+
|
|
132
|
+
## Current boundaries
|
|
133
|
+
|
|
134
|
+
- `TatumLedger` uses native HTTP/2 gRPC with an API key for evidence reads.
|
|
135
|
+
- `buildPrivateTransfer` builds the pinned pool's `transfer_private` PTB using
|
|
136
|
+
the Sui TypeScript gRPC stack.
|
|
137
|
+
- `LiveRuntime` also builds deposit and withdrawal PTBs, reconstructs the
|
|
138
|
+
20-level Poseidon tree from pinned pool events, creates dynamic circuit
|
|
139
|
+
witnesses, verifies artifact hashes, and uses RapidSnark with SnarkJS fallback.
|
|
140
|
+
- `RelayAdmission` verifies a single-use, Sui-signed relay challenge before
|
|
141
|
+
a pinned proof/public-statement submission can be broadcast.
|
|
142
|
+
- `ManualMemory` uses `MemWalManual`, so memory plaintext is embedded and
|
|
143
|
+
encrypted on the client before the Walrus Memory relayer receives it.
|
|
144
|
+
- `DurableTranscriptTransport` persists exact ciphertext before Walrus upload
|
|
145
|
+
and authenticates the complete transcript after restart.
|
|
146
|
+
- `PrivateWallet` reconciles only finalized, pinned pool evidence and recovers
|
|
147
|
+
payment/change notes from matching openings.
|
|
148
|
+
- `ExactResourceServer` adds a signed request binding and atomic payment-use
|
|
149
|
+
ledger around the standard x402 exact rail. The seller-owned resource URL is
|
|
150
|
+
supplied to the live workflow; the facilitator only validates and relays the
|
|
151
|
+
buyer-signed transaction.
|
|
152
|
+
- `SealClientDisclosureTransport` encrypts auditor-scoped disclosure packages;
|
|
153
|
+
`scripts/seal-live-e2e.ts` supplies the deployed account policy approval PTB,
|
|
154
|
+
verifies finalized chain evidence, and gates the designated auditor decrypt.
|
|
155
|
+
|
|
156
|
+
The selected x402 facilitator URL remains public deployment configuration. Its
|
|
157
|
+
`FACILITATOR_SHARED_KEY` is seller-only configuration: it is sent only by the
|
|
158
|
+
resource service as `x-supercuts-facilitator-key`, and must never appear in a
|
|
159
|
+
buyer profile, manifest, event log, or dashboard. The Testnet
|
|
160
|
+
Seal policy is pinned in `config/seal-policy.testnet.json`. No test-only pool is represented as production-safe. Run the
|
|
161
|
+
repository-level `scripts/phase2-check.sh` and see `phase-2-report.md` for the
|
|
162
|
+
live test evidence and explicit production boundary.
|
|
163
|
+
|
|
164
|
+
## Protocol memory worker
|
|
165
|
+
|
|
166
|
+
The long-running buyer MCP process drains pending outcome writes every 30
|
|
167
|
+
seconds. For a live role home used without MCP, run
|
|
168
|
+
`supercuts-memory-worker <role-home>` as a separate process. It drains pending
|
|
169
|
+
verified outcome writes and synchronizes the role's Walrus Memory metadata
|
|
170
|
+
using `listMemories()`. `memory.inventory` performs one metadata sync on demand.
|
|
171
|
+
An uncertain write remains `unresolved` for operator reconciliation; the
|
|
172
|
+
worker does not submit it again automatically. `deal.reconcile` compares
|
|
173
|
+
client-decrypted recall results with the exact saved outcome; a missing search
|
|
174
|
+
hit leaves the job unresolved.
|
|
175
|
+
|
|
176
|
+
## Composite deal adapters
|
|
177
|
+
|
|
178
|
+
The buyer SDK exposes `executeDeal`, `getDeal`, `resumeDeal`,
|
|
179
|
+
`getDealEvidence`, and `getDealDelivery`. The built-in live adapter loads
|
|
180
|
+
owner-only `deal-sellers.json` from the buyer role home. It is a JSON array of
|
|
181
|
+
seller origins, for example `["https://seller.example"]`, mode `0600`. It
|
|
182
|
+
discovers signed capabilities at `/v2/catalog`, fetches the signed manifest
|
|
183
|
+
from Walrus, selects the mandate-approved rail, and uses the buyer role's own
|
|
184
|
+
signer, pool notes, Walrus and Walrus Memory configuration. A missing file
|
|
185
|
+
returns `DEAL_SELLERS_NOT_CONFIGURED`. An owner-only `deal-ports.mjs` exporting
|
|
186
|
+
`createDealPorts(app, mandate)` remains available as an advanced override.
|
|
187
|
+
|
|
188
|
+
The seller service is `supercuts-seller-live <seller-home> <config.json>`.
|
|
189
|
+
Register each capability first with
|
|
190
|
+
`supercuts-seller-register <seller-home> <spec.json> <registration.json>`.
|
|
191
|
+
The spec contains `capabilityId`, `capability`, `description`, `inputSchema`,
|
|
192
|
+
`outputSchema`, `supportedRails`, `priceBand`, `asset`, `endpoint`, and
|
|
193
|
+
`expiresAt`; Supercuts inserts the seller signer address, validates the schema,
|
|
194
|
+
publishes the manifest to Walrus, and signs its manifest hash and transport
|
|
195
|
+
key. The registration file is created mode `0600`. The endpoint is the
|
|
196
|
+
seller-owned resource prefix, such as
|
|
197
|
+
`https://seller.example/v2/resource/report`. The current wire contract uses
|
|
198
|
+
the signed price band's `minimum` as its exact price.
|
|
199
|
+
|
|
200
|
+
The seller's owner-only config has this shape:
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"origin": "https://seller.example",
|
|
205
|
+
"listenHost": "127.0.0.1",
|
|
206
|
+
"listenPort": 8788,
|
|
207
|
+
"facilitatorUrl": "https://facilitator.example",
|
|
208
|
+
"facilitatorKey": "OWNER_ONLY_FACILITATOR_SECRET",
|
|
209
|
+
"allowedBuyers": ["0xBUYER_ADDRESS"],
|
|
210
|
+
"capabilities": [
|
|
211
|
+
{ "registrationFile": "/owner/path/registration.json", "handler": "/owner/path/handler.mjs" }
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The handler module exports `default async function handle(request)` and
|
|
217
|
+
returns `{ artifact: Uint8Array, contentType: string, metadata: object }`.
|
|
218
|
+
Its `request.input` is decrypted inside the seller role, and `metadata` must
|
|
219
|
+
match the signed output schema. Keep config and handler mode `0600`. Put a TLS
|
|
220
|
+
reverse proxy in front of the local listener when `origin` is public HTTPS.
|
|
221
|
+
The facilitator key stays in the seller config. The x402 resource delivers a
|
|
222
|
+
paid acknowledgment; the buyer then requests the encrypted artifact. For a
|
|
223
|
+
shielded deal, the seller verifies the pinned pool transfer event and recovers
|
|
224
|
+
its note before the handler runs. Both rails require matching finalized Sui and
|
|
225
|
+
Tatum evidence. The service publishes encrypted offer, agreement reply, and
|
|
226
|
+
delivery blobs to Walrus and checks readback.
|
|
227
|
+
|
|
228
|
+
Issue a buyer `supercuts.mandate.v2` allowing `deal.execute` and the underlying
|
|
229
|
+
planning, agreement, payment, and memory commands. Shielded is the default;
|
|
230
|
+
public x402 additionally requires `allowPublicX402: true`, `allowedRails`
|
|
231
|
+
containing `x402`, and no shielded-only privacy rule. The buyer must have
|
|
232
|
+
Testnet SUI for gas and payment, a v2 pool deposit for shielded settlement,
|
|
233
|
+
proof artifacts, and an owner-only Mainnet Walrus Memory configuration.
|
|
234
|
+
`executeDeal` returns `outcome_pending` after finalized payment and verified
|
|
235
|
+
delivery, with the artifact available through `getDealDelivery`. Walrus Memory
|
|
236
|
+
indexing proceeds in the worker; `getDeal` later returns `complete` when the
|
|
237
|
+
outcome blob appears in inventory. `reconcileDealOutcome` handles an ambiguous
|
|
238
|
+
manual Memory write without repaying or resubmitting it. Mock profiles are
|
|
239
|
+
refused.
|
|
240
|
+
|
|
241
|
+
The generic live wire has been tested with local Walrus and signed HTTP
|
|
242
|
+
negotiation on both rails, plus simulated Sui and Tatum seller verification.
|
|
243
|
+
On 2026-11-03, separate funded Sui Testnet composite operations reached
|
|
244
|
+
`complete`: shielded digest
|
|
245
|
+
`HxyeTY9ThQoAtz4a1FLcgJhj1vtsSGSB3zyz6A9fyoeq` and public x402 digest
|
|
246
|
+
`CAWTGRYSr5aTzMmToxZbZQmeX4DN2KiZz7ANoZxNsGeM`. Both buyer journals
|
|
247
|
+
show finalized payment, verified Walrus delivery readback, and indexed Walrus
|
|
248
|
+
Memory outcome. The shielded seller recovered its payment note. These are
|
|
249
|
+
live Testnet observations, separate from the recorded `supercuts-demo` event
|
|
250
|
+
stream. The v2 pool remains experimental and Testnet-only.
|
package/dist/audit.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { SealClient, SessionKey } from "@mysten/seal";
|
|
2
|
+
export interface DisclosurePackage {
|
|
3
|
+
version: 1;
|
|
4
|
+
dealId: string;
|
|
5
|
+
auditor: string;
|
|
6
|
+
agreement: Record<string, unknown>;
|
|
7
|
+
agreementOpening: string;
|
|
8
|
+
transcriptRoot: string;
|
|
9
|
+
authorizations: Array<{
|
|
10
|
+
author: string;
|
|
11
|
+
signature: string;
|
|
12
|
+
}>;
|
|
13
|
+
paymentDigest: string;
|
|
14
|
+
dealNullifier: string;
|
|
15
|
+
}
|
|
16
|
+
export interface AuditChainEvidence {
|
|
17
|
+
finalized: boolean;
|
|
18
|
+
paymentDigest: string;
|
|
19
|
+
dealNullifier: string;
|
|
20
|
+
agreementCommitment: string;
|
|
21
|
+
transcriptRoot: string;
|
|
22
|
+
}
|
|
23
|
+
export type AuditEvidenceReader = (digest: string) => Promise<AuditChainEvidence | undefined>;
|
|
24
|
+
export type AuditAuthorizationVerifier = (author: string, bytes: Uint8Array, signature: string) => Promise<boolean>;
|
|
25
|
+
export type AgreementCommitmentDeriver = (agreement: Record<string, unknown>, opening: string) => Promise<string>;
|
|
26
|
+
export declare function agreementCommitment(agreement: Record<string, unknown>, opening: string): string;
|
|
27
|
+
/** Recomputes the exact protocol-v1 deal commitment opened by an audit package. */
|
|
28
|
+
export declare function protocolAgreementCommitment(agreement: Record<string, unknown>, opening: string): Promise<string>;
|
|
29
|
+
export declare function disclosureAuthorizationBytes(pkg: Omit<DisclosurePackage, "authorizations">): Uint8Array;
|
|
30
|
+
export declare function validateDisclosureShape(pkg: DisclosurePackage): void;
|
|
31
|
+
export declare function verifyDisclosure(pkg: DisclosurePackage, auditor: string, expectedDealId: string, read: AuditEvidenceReader, verifyAuthorization: AuditAuthorizationVerifier, expectedAuthors: ReadonlySet<string>, deriveCommitment: AgreementCommitmentDeriver): Promise<void>;
|
|
32
|
+
/** Adapter boundary for @mysten/seal encryption in the live process. */
|
|
33
|
+
export interface SealDisclosureTransport {
|
|
34
|
+
encrypt(auditor: string, plaintext: Uint8Array): Promise<Uint8Array>;
|
|
35
|
+
decrypt(auditor: string, ciphertext: Uint8Array): Promise<Uint8Array>;
|
|
36
|
+
}
|
|
37
|
+
export interface SealAuthorization {
|
|
38
|
+
sessionKey: SessionKey;
|
|
39
|
+
txBytes: Uint8Array;
|
|
40
|
+
}
|
|
41
|
+
/** Live Seal adapter; the authorization transaction must approve only the designated auditor. */
|
|
42
|
+
export declare class SealClientDisclosureTransport implements SealDisclosureTransport {
|
|
43
|
+
private readonly client;
|
|
44
|
+
private readonly packageId;
|
|
45
|
+
private readonly threshold;
|
|
46
|
+
private readonly authorize;
|
|
47
|
+
constructor(client: SealClient, packageId: string, threshold: number, authorize: (auditor: string, ciphertext: Uint8Array) => Promise<SealAuthorization>);
|
|
48
|
+
encrypt(auditor: string, plaintext: Uint8Array): Promise<Uint8Array>;
|
|
49
|
+
decrypt(auditor: string, ciphertext: Uint8Array): Promise<Uint8Array>;
|
|
50
|
+
}
|
|
51
|
+
export declare function sealDisclosure(pkg: DisclosurePackage, seal: SealDisclosureTransport): Promise<Uint8Array>;
|
|
52
|
+
export declare function openDisclosure(ciphertext: Uint8Array, auditor: string, seal: SealDisclosureTransport): Promise<DisclosurePackage>;
|
package/dist/audit.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { buildPoseidon } from "circomlibjs";
|
|
3
|
+
import { fail } from "./errors.js";
|
|
4
|
+
function canonical(value) {
|
|
5
|
+
if (Array.isArray(value))
|
|
6
|
+
return `[${value.map(canonical).join(",")}]`;
|
|
7
|
+
if (value && typeof value === "object")
|
|
8
|
+
return `{${Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`).join(",")}}`;
|
|
9
|
+
const encoded = JSON.stringify(value);
|
|
10
|
+
if (encoded === undefined)
|
|
11
|
+
fail("AUDIT_PACKAGE_INVALID", "disclosure contains a non-JSON value");
|
|
12
|
+
return encoded;
|
|
13
|
+
}
|
|
14
|
+
export function agreementCommitment(agreement, opening) {
|
|
15
|
+
const terms = Buffer.from(canonical(agreement));
|
|
16
|
+
const secret = Buffer.from(opening);
|
|
17
|
+
const lengths = Buffer.alloc(8);
|
|
18
|
+
lengths.writeUInt32BE(terms.length);
|
|
19
|
+
lengths.writeUInt32BE(secret.length, 4);
|
|
20
|
+
return createHash("sha256").update("supercuts-disclosure-agreement-v1\0").update(lengths).update(terms).update(secret).digest("hex");
|
|
21
|
+
}
|
|
22
|
+
const FIELD_MODULUS = 21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
23
|
+
const U64_MAX = (1n << 64n) - 1n;
|
|
24
|
+
let poseidonPromise;
|
|
25
|
+
function decimal(name, value, maximum = FIELD_MODULUS - 1n) {
|
|
26
|
+
if (typeof value === "string" && /^(?:0|[1-9][0-9]*)$/.test(value)) {
|
|
27
|
+
const parsed = BigInt(value);
|
|
28
|
+
if (parsed >= 0n && parsed <= maximum)
|
|
29
|
+
return parsed;
|
|
30
|
+
}
|
|
31
|
+
return fail("AUDIT_PACKAGE_INVALID", `${name} must be a canonical decimal string inside its protocol range`);
|
|
32
|
+
}
|
|
33
|
+
/** Recomputes the exact protocol-v1 deal commitment opened by an audit package. */
|
|
34
|
+
export async function protocolAgreementCommitment(agreement, opening) {
|
|
35
|
+
let parsedOpening;
|
|
36
|
+
try {
|
|
37
|
+
parsedOpening = JSON.parse(opening);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return fail("AUDIT_PACKAGE_INVALID", "agreement opening is not valid JSON", error);
|
|
41
|
+
}
|
|
42
|
+
if (!parsedOpening || typeof parsedOpening !== "object" || Array.isArray(parsedOpening))
|
|
43
|
+
fail("AUDIT_PACKAGE_INVALID", "agreement opening has an invalid shape");
|
|
44
|
+
const secret = parsedOpening;
|
|
45
|
+
if (Object.keys(secret).length !== 1 || !("blinding" in secret))
|
|
46
|
+
fail("AUDIT_PACKAGE_INVALID", "agreement opening must contain only the deal blinding");
|
|
47
|
+
const fields = {
|
|
48
|
+
deployment: decimal("deployment", agreement.deployment),
|
|
49
|
+
dealId: decimal("dealId", agreement.dealId),
|
|
50
|
+
revision: decimal("revision", agreement.revision, U64_MAX),
|
|
51
|
+
transcriptRootLo: decimal("transcriptRootLo", agreement.transcriptRootLo),
|
|
52
|
+
transcriptRootHi: decimal("transcriptRootHi", agreement.transcriptRootHi),
|
|
53
|
+
serviceDigestLo: decimal("serviceDigestLo", agreement.serviceDigestLo),
|
|
54
|
+
serviceDigestHi: decimal("serviceDigestHi", agreement.serviceDigestHi),
|
|
55
|
+
settlementNonceLo: decimal("settlementNonceLo", agreement.settlementNonceLo),
|
|
56
|
+
settlementNonceHi: decimal("settlementNonceHi", agreement.settlementNonceHi),
|
|
57
|
+
paymentAmount: decimal("paymentAmount", agreement.paymentAmount, U64_MAX),
|
|
58
|
+
expiry: decimal("expiry", agreement.expiry, U64_MAX),
|
|
59
|
+
buyerAx: decimal("buyerAx", agreement.buyerAx), buyerAy: decimal("buyerAy", agreement.buyerAy),
|
|
60
|
+
sellerAx: decimal("sellerAx", agreement.sellerAx), sellerAy: decimal("sellerAy", agreement.sellerAy),
|
|
61
|
+
recipientSpendTag: decimal("recipientSpendTag", agreement.recipientSpendTag),
|
|
62
|
+
blinding: decimal("blinding", secret.blinding),
|
|
63
|
+
};
|
|
64
|
+
if (Object.keys(agreement).length !== 16)
|
|
65
|
+
fail("AUDIT_PACKAGE_INVALID", "agreement contains unexpected fields");
|
|
66
|
+
const poseidon = await (poseidonPromise ??= buildPoseidon());
|
|
67
|
+
const hash = (values) => poseidon.F.toObject(poseidon(values));
|
|
68
|
+
const paymentSalt = hash([2009n, fields.deployment, fields.settlementNonceLo, fields.settlementNonceHi]);
|
|
69
|
+
const termsA = hash([2001n, fields.deployment, fields.dealId, fields.revision, fields.transcriptRootLo, fields.transcriptRootHi]);
|
|
70
|
+
const termsB = hash([fields.serviceDigestLo, fields.serviceDigestHi, fields.settlementNonceLo, fields.settlementNonceHi, fields.paymentAmount, fields.expiry]);
|
|
71
|
+
const termsC = hash([fields.buyerAx, fields.buyerAy, fields.sellerAx, fields.sellerAy, fields.recipientSpendTag, paymentSalt]);
|
|
72
|
+
return hash([2002n, termsA, termsB, termsC, fields.blinding, fields.expiry]).toString(16).padStart(64, "0");
|
|
73
|
+
}
|
|
74
|
+
export function disclosureAuthorizationBytes(pkg) {
|
|
75
|
+
return Buffer.from(canonical({ domain: "supercuts-audit-v1", version: pkg.version, dealId: pkg.dealId, auditor: pkg.auditor, agreement: pkg.agreement, agreementOpening: pkg.agreementOpening, transcriptRoot: pkg.transcriptRoot, paymentDigest: pkg.paymentDigest, dealNullifier: pkg.dealNullifier }));
|
|
76
|
+
}
|
|
77
|
+
export function validateDisclosureShape(pkg) {
|
|
78
|
+
if (!pkg || typeof pkg !== "object" || pkg.version !== 1 || !pkg.agreement || typeof pkg.agreement !== "object" || Array.isArray(pkg.agreement)
|
|
79
|
+
|| typeof pkg.agreementOpening !== "string" || !Array.isArray(pkg.authorizations))
|
|
80
|
+
fail("AUDIT_PACKAGE_INVALID", "disclosure has an invalid shape or version");
|
|
81
|
+
const serialized = JSON.stringify(pkg);
|
|
82
|
+
if (/spend(?:ing)?[_ -]?(?:key|secret)|parent[_ -]?session[_ -]?key|note[_ -]?opening/i.test(serialized)) {
|
|
83
|
+
fail("AUDIT_SECRET_LEAK", "disclosure contains a wallet or parent-session secret");
|
|
84
|
+
}
|
|
85
|
+
if (![pkg.dealId, pkg.auditor, pkg.transcriptRoot, pkg.paymentDigest, pkg.dealNullifier].every((value) => typeof value === "string" && value.length > 0)
|
|
86
|
+
|| !pkg.authorizations.length || pkg.authorizations.some((item) => !item || typeof item.author !== "string" || !item.author || typeof item.signature !== "string" || !item.signature))
|
|
87
|
+
fail("AUDIT_PACKAGE_INVALID", "disclosure is incomplete");
|
|
88
|
+
}
|
|
89
|
+
export async function verifyDisclosure(pkg, auditor, expectedDealId, read, verifyAuthorization, expectedAuthors, deriveCommitment) {
|
|
90
|
+
validateDisclosureShape(pkg);
|
|
91
|
+
if (pkg.auditor !== auditor)
|
|
92
|
+
fail("AUDITOR_IDENTITY_MISMATCH", "disclosure was encrypted for another auditor");
|
|
93
|
+
if (pkg.dealId !== expectedDealId)
|
|
94
|
+
fail("AUDIT_DEAL_MISMATCH", "disclosure describes another deal");
|
|
95
|
+
const evidence = await read(pkg.paymentDigest);
|
|
96
|
+
if (!evidence)
|
|
97
|
+
return fail("AUDIT_PAYMENT_MISMATCH", "disclosed payment was not found");
|
|
98
|
+
if (!evidence.finalized)
|
|
99
|
+
fail("AUDIT_PAYMENT_MISMATCH", "disclosed payment is not finalized");
|
|
100
|
+
if (evidence.paymentDigest !== pkg.paymentDigest || evidence.dealNullifier !== pkg.dealNullifier)
|
|
101
|
+
fail("AUDIT_PAYMENT_MISMATCH", "disclosed payment does not match finalized chain evidence");
|
|
102
|
+
if (evidence.transcriptRoot !== pkg.transcriptRoot)
|
|
103
|
+
fail("AUDIT_TRANSCRIPT_MISMATCH", "transcript root does not match chain-bound evidence");
|
|
104
|
+
if (evidence.agreementCommitment !== await deriveCommitment(pkg.agreement, pkg.agreementOpening))
|
|
105
|
+
fail("AUDIT_TERMS_MISMATCH", "agreement opening does not match chain evidence");
|
|
106
|
+
const { authorizations: _authorizations, ...unsigned } = pkg;
|
|
107
|
+
const bytes = disclosureAuthorizationBytes(unsigned);
|
|
108
|
+
const authors = new Set();
|
|
109
|
+
for (const authorization of pkg.authorizations) {
|
|
110
|
+
if (authors.has(authorization.author) || !(await verifyAuthorization(authorization.author, bytes, authorization.signature)))
|
|
111
|
+
fail("AUDIT_AUTHORIZATION_INVALID", "disclosure authorization is invalid or duplicated");
|
|
112
|
+
authors.add(authorization.author);
|
|
113
|
+
}
|
|
114
|
+
if (authors.size !== expectedAuthors.size || [...expectedAuthors].some((author) => !authors.has(author)))
|
|
115
|
+
fail("AUDIT_AUTHORIZATION_INVALID", "disclosure does not contain the exact required authorizers");
|
|
116
|
+
}
|
|
117
|
+
/** Live Seal adapter; the authorization transaction must approve only the designated auditor. */
|
|
118
|
+
export class SealClientDisclosureTransport {
|
|
119
|
+
client;
|
|
120
|
+
packageId;
|
|
121
|
+
threshold;
|
|
122
|
+
authorize;
|
|
123
|
+
constructor(client, packageId, threshold, authorize) {
|
|
124
|
+
this.client = client;
|
|
125
|
+
this.packageId = packageId;
|
|
126
|
+
this.threshold = threshold;
|
|
127
|
+
this.authorize = authorize;
|
|
128
|
+
}
|
|
129
|
+
async encrypt(auditor, plaintext) {
|
|
130
|
+
if (!/^0x[0-9a-f]{64}$/i.test(auditor) || !/^0x[0-9a-f]{64}$/i.test(this.packageId) || !Number.isSafeInteger(this.threshold) || this.threshold < 1)
|
|
131
|
+
fail("SEAL_CONFIG_INVALID", "Seal auditor, package, or threshold is invalid");
|
|
132
|
+
const result = await this.client.encrypt({ threshold: this.threshold, packageId: this.packageId, id: auditor.replace(/^0x/, ""), data: plaintext });
|
|
133
|
+
return result.encryptedObject;
|
|
134
|
+
}
|
|
135
|
+
async decrypt(auditor, ciphertext) {
|
|
136
|
+
const authorization = await this.authorize(auditor, ciphertext);
|
|
137
|
+
return this.client.decrypt({ data: ciphertext, sessionKey: authorization.sessionKey, txBytes: authorization.txBytes });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export async function sealDisclosure(pkg, seal) {
|
|
141
|
+
validateDisclosureShape(pkg);
|
|
142
|
+
return seal.encrypt(pkg.auditor, Buffer.from(JSON.stringify(pkg)));
|
|
143
|
+
}
|
|
144
|
+
export async function openDisclosure(ciphertext, auditor, seal) {
|
|
145
|
+
const plaintext = await seal.decrypt(auditor, ciphertext);
|
|
146
|
+
let pkg;
|
|
147
|
+
try {
|
|
148
|
+
pkg = JSON.parse(Buffer.from(plaintext).toString());
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
return fail("AUDIT_PACKAGE_INVALID", "disclosure is not valid JSON", error);
|
|
152
|
+
}
|
|
153
|
+
validateDisclosureShape(pkg);
|
|
154
|
+
if (pkg.auditor !== auditor)
|
|
155
|
+
fail("AUDITOR_IDENTITY_MISMATCH", "disclosure was encrypted for another auditor");
|
|
156
|
+
return pkg;
|
|
157
|
+
}
|