@haven_ai/signer 0.1.33-alpha.0 → 0.1.35-alpha.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/README.md +49 -14
- package/dist/cli.cjs +12 -13
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +12 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +8 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -11
- package/dist/index.d.ts +47 -11
- package/dist/index.js +9 -13
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -13,6 +13,17 @@ Contract: [`docs/architecture/06-hosted-mcp-connect-flow.md`](../../docs/archite
|
|
|
13
13
|
Requires **Node >= 22**; the signer refuses to start on anything older, before
|
|
14
14
|
it reads a key.
|
|
15
15
|
|
|
16
|
+
## Are you an AI agent whose user has no Haven account yet?
|
|
17
|
+
|
|
18
|
+
Read **`/for-agents.md`** on the Haven host your user gave you — or
|
|
19
|
+
[the copy in this repository](https://github.com/d-hinders/Haven-AI/blob/dev/packages/frontend/public/for-agents.md)
|
|
20
|
+
if you do not have that host yet.
|
|
21
|
+
|
|
22
|
+
Your user creates the account and the passkey: those are theirs, they need a
|
|
23
|
+
human, and you should never ask for their password. You can do everything else
|
|
24
|
+
— including running the connector command from the setup prompt they paste you,
|
|
25
|
+
and managing the account from the shell with `@haven_ai/cli`.
|
|
26
|
+
|
|
16
27
|
## Two ways to use it
|
|
17
28
|
|
|
18
29
|
**As a local MCP signer** (for Claude Desktop / Code / Cursor) — run it
|
|
@@ -21,22 +32,30 @@ the Haven dashboard hands out, which writes the MCP config and pins the
|
|
|
21
32
|
runtime:
|
|
22
33
|
|
|
23
34
|
```sh
|
|
24
|
-
npx @haven_ai/connect
|
|
35
|
+
npx @haven_ai/connect@<channel>
|
|
25
36
|
```
|
|
26
37
|
|
|
38
|
+
`<channel>` is a placeholder: production hands out `@alpha`, and it is the right
|
|
39
|
+
answer unless your dashboard hands you a different one — **run the command that dashboard shows
|
|
40
|
+
you**, which names the npm dist-tag that backend is paired with. This signer's
|
|
41
|
+
own messages do the same: since [#2423](https://github.com/d-hinders/Haven-AI/issues/2423)
|
|
42
|
+
every "rerun the connector" hint it prints names the channel THIS build was
|
|
43
|
+
published under, so a build installed from a non-production channel tells you
|
|
44
|
+
to reinstall from that same channel rather than sending you to production.
|
|
45
|
+
|
|
27
46
|
Rerunning it is also the documented fix for a signer that has fallen behind the
|
|
28
47
|
backend's expected-context version. To run the signer directly:
|
|
29
48
|
|
|
30
49
|
```sh
|
|
31
|
-
HAVEN_DELEGATE_KEY=0x... npx @haven_ai/signer
|
|
50
|
+
HAVEN_DELEGATE_KEY=0x... npx @haven_ai/signer@alpha
|
|
32
51
|
# or
|
|
33
|
-
npx @haven_ai/signer --credentials /path/to/haven-agent.json
|
|
52
|
+
npx @haven_ai/signer@alpha --credentials /path/to/haven-agent.json
|
|
34
53
|
```
|
|
35
54
|
|
|
36
55
|
On first launch, the signer prints the delegate address, any wallet/network
|
|
37
56
|
metadata found in the credential file, and the sign-only tool list. It refuses
|
|
38
57
|
to start until acknowledged with either `HAVEN_SIGNER_ACK=<hash>` or
|
|
39
|
-
`npx @haven_ai/signer --credentials /path/to/haven-agent.json --ack`.
|
|
58
|
+
`npx @haven_ai/signer@alpha --credentials /path/to/haven-agent.json --ack`.
|
|
40
59
|
|
|
41
60
|
It exposes four stdio MCP tools, all sign-only:
|
|
42
61
|
|
|
@@ -77,7 +96,7 @@ const { paymentHeader } = await signer.buildX402PaymentHeader(
|
|
|
77
96
|
The signer also exposes `signPaymentHash(hash)` (raw ECDSA over a legacy
|
|
78
97
|
AllowanceModule funding/transfer hash) and `signX402FundingHash(hash, expected)`
|
|
79
98
|
for v1 contexts, and `signSweepAuthorization(input)` for the gasless sweep. All
|
|
80
|
-
|
|
99
|
+
six are methods on the object `createEdgeSigner` returns, not standalone
|
|
81
100
|
exports.
|
|
82
101
|
|
|
83
102
|
## Orchestration
|
|
@@ -179,17 +198,33 @@ The delegate key is read from `HAVEN_DELEGATE_KEY` or a `--credentials` file's
|
|
|
179
198
|
`delegate_key` (with a permissive-file warning). It stays in this process, and
|
|
180
199
|
is never transmitted.
|
|
181
200
|
|
|
182
|
-
**The signer makes
|
|
183
|
-
[#1263](https://github.com/d-hinders/Haven-AI/issues/1263)
|
|
201
|
+
**The signer makes at most one kind of network call, on one path, and it is a
|
|
202
|
+
read.** Since [#1263](https://github.com/d-hinders/Haven-AI/issues/1263) the
|
|
203
|
+
`{ payment_id }` form of `haven_sign` and `haven_sign_x402` performs an
|
|
184
204
|
authenticated, read-only `GET /x402/:payment_id/sign-context` against Haven, so
|
|
185
205
|
that agents never have to relay multi-KB EIP-712 payloads through a model's
|
|
186
|
-
context window.
|
|
187
|
-
|
|
188
|
-
`
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
submits, or broadcasts.
|
|
206
|
+
context window. **Only the Bearer API key goes out; the delegate key is never
|
|
207
|
+
part of that request or its response.** Nothing else in the package reaches the
|
|
208
|
+
network: `haven_x402_sign_header` and `haven_sign_sweep_delegate` never fetch,
|
|
209
|
+
the library surface above (`createEdgeSigner` and its six signing methods, over
|
|
210
|
+
the network-free `src/core.ts`) never fetches, and passing the payload as
|
|
211
|
+
`typed_data_b64` instead of `payment_id` keeps even the two fetching tools
|
|
212
|
+
offline. It never relays, submits, or broadcasts.
|
|
213
|
+
|
|
214
|
+
The fetch needs `api_url` and `api_key` from an `identity.json` sitting **next
|
|
215
|
+
to the signer credential file** — the signer's own credential still needs no
|
|
216
|
+
`api_key`. So the network call is a property of how you start the signer, not
|
|
217
|
+
just of which tool you call: run it from `HAVEN_DELEGATE_KEY` alone and there is
|
|
218
|
+
no credential file, hence no directory to find an `identity.json` in, so the
|
|
219
|
+
`{ payment_id }` form refuses with a message naming the `typed_data_b64`
|
|
220
|
+
fallback rather than reaching out — and the process makes no network calls at
|
|
221
|
+
all. Egress is needed by `--credentials` / `HAVEN_CREDENTIALS` runs that use the
|
|
222
|
+
preferred `{ payment_id }` call, which is what the connector install above
|
|
223
|
+
sets up.
|
|
224
|
+
|
|
225
|
+
Fetched bytes are treated as untrusted input exactly like a tool argument: the
|
|
226
|
+
same digest re-derivation and Haven-binding verification apply, because what
|
|
227
|
+
makes them safe is the verification, not where they came from.
|
|
193
228
|
|
|
194
229
|
Connect Agent 2 may create the signer credential file locally during setup. In
|
|
195
230
|
that flow Haven receives the public signing address, proof, API-key hash/prefix,
|
package/dist/cli.cjs
CHANGED
|
@@ -7,10 +7,10 @@ var crypto = require('crypto');
|
|
|
7
7
|
var promises = require('fs/promises');
|
|
8
8
|
var os = require('os');
|
|
9
9
|
var path = require('path');
|
|
10
|
+
var sdk = require('@haven_ai/sdk');
|
|
10
11
|
var viem = require('viem');
|
|
11
12
|
var accounts = require('viem/accounts');
|
|
12
13
|
var schemes = require('x402/schemes');
|
|
13
|
-
var sdk = require('@haven_ai/sdk');
|
|
14
14
|
var v3 = require('zod/v3');
|
|
15
15
|
|
|
16
16
|
function defaultSigningAuditPath(credentialsPath) {
|
|
@@ -302,11 +302,9 @@ function createEdgeSigner(delegateKey, options = {}) {
|
|
|
302
302
|
}
|
|
303
303
|
try {
|
|
304
304
|
const payment = sdk.decodeBase64Json(header);
|
|
305
|
-
const wrapped = sdk.encodeBase64Json(
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
payload: payment.payload
|
|
309
|
-
});
|
|
305
|
+
const wrapped = sdk.encodeBase64Json(
|
|
306
|
+
sdk.x402V2PaymentEnvelope(paymentRequired, option, payment.payload)
|
|
307
|
+
);
|
|
310
308
|
return { paymentHeader: wrapped, accepted: option };
|
|
311
309
|
} finally {
|
|
312
310
|
x402Bindings.delete(x402Binding);
|
|
@@ -412,10 +410,10 @@ function assertSupportedBindingVersion(received, supported, context) {
|
|
|
412
410
|
const highest = Math.max(...supported);
|
|
413
411
|
const outOfDate = received > highest;
|
|
414
412
|
const code = context === "x402 expected context" ? sdk.SignerRefusalCode.UnsupportedExpectedContextVersion : sdk.SignerRefusalCode.UnsupportedSweepBindingVersion;
|
|
415
|
-
const ceiling = outOfDate ? `This signer is out of date: it supports ${context} versions up to ${highest}, and Haven sent version ${received}. Update @haven_ai/signer \u2014 rerun the Haven connector (\`
|
|
413
|
+
const ceiling = outOfDate ? `This signer is out of date: it supports ${context} versions up to ${highest}, and Haven sent version ${received}. Update @haven_ai/signer \u2014 rerun the Haven connector (\`${sdk.connectorRerunCommand()}\`), which reinstalls the pinned MCP runtime.` : `Unsupported ${context} version ${received}: this signer supports ${supported.join(", ")}.`;
|
|
416
414
|
const fallback = outOfDate ? sdk.SIGNER_UPDATE_FALLBACK : `This ${context} version (${received}) is older than what this signer enforces (${supported.join(", ")}) \u2014 updating @haven_ai/signer will not restore it. Nothing was signed or spent; stop and tell the user rather than retrying.`;
|
|
417
415
|
throw new sdk.HavenUnsupportedSignerVersionError(
|
|
418
|
-
`${ceiling} Nothing was signed. Do not rewrite the version field to a supported value: it is part of the Haven-signed binding message, so changing it invalidates the signature and would misrepresent what Haven
|
|
416
|
+
`${ceiling} Nothing was signed. Do not rewrite the version field to a supported value: it is part of the Haven-signed binding message, so changing it invalidates the signature and would misrepresent what Haven declared.`,
|
|
419
417
|
code,
|
|
420
418
|
supported,
|
|
421
419
|
received,
|
|
@@ -527,7 +525,7 @@ function signerInstructions() {
|
|
|
527
525
|
"Haven quote and prepare results report the expected-context version they will emit",
|
|
528
526
|
"(signer_compatibility.x402_expected_context_version). If that version is not in the list",
|
|
529
527
|
"above, this signer is out of date: STOP before signing, and tell the user to update",
|
|
530
|
-
|
|
528
|
+
`@haven_ai/signer by rerunning \`${sdk.connectorRerunCommand()}\`, which reinstalls the pinned`,
|
|
531
529
|
"MCP runtime. Do not edit the version field to a supported value \u2014 it is part of the",
|
|
532
530
|
"Haven-signed binding message, so changing it invalidates the signature.",
|
|
533
531
|
"",
|
|
@@ -824,7 +822,7 @@ function createToolHandlers(signer, options = {}) {
|
|
|
824
822
|
const identity = await options.signContext?.loadIdentity() ?? null;
|
|
825
823
|
if (!identity) {
|
|
826
824
|
throw new sdk.HavenSigningError(
|
|
827
|
-
|
|
825
|
+
`payment_id signing needs the agent identity (identity.json next to the signer credentials), which this signer could not load. Re-run \`${sdk.connectorRerunCommand()}\` to restore it, or pass typed_data_b64 from the quote result instead.`
|
|
828
826
|
);
|
|
829
827
|
}
|
|
830
828
|
const ctx = await fetchX402SignContext(
|
|
@@ -1239,7 +1237,7 @@ async function warnIfCredentialFilePermissive(path, log = (message) => process.s
|
|
|
1239
1237
|
|
|
1240
1238
|
// src/server.ts
|
|
1241
1239
|
var SIGNER_NAME = "@haven_ai/signer";
|
|
1242
|
-
var SIGNER_VERSION = "0.1.
|
|
1240
|
+
var SIGNER_VERSION = "0.1.35-alpha.0";
|
|
1243
1241
|
async function resolveSignerRuntime(options = {}) {
|
|
1244
1242
|
assertSupportedNodeVersion(options.nodeVersion);
|
|
1245
1243
|
if (options.delegateKey) {
|
|
@@ -1375,8 +1373,9 @@ function parseArgs(argv) {
|
|
|
1375
1373
|
"",
|
|
1376
1374
|
"Consent:",
|
|
1377
1375
|
" On first launch the signer prints the sign-only tool list and delegate",
|
|
1378
|
-
" address, then refuses to start unless acknowledged.
|
|
1379
|
-
"
|
|
1376
|
+
" address, then refuses to start unless acknowledged. Its only Haven API call",
|
|
1377
|
+
" is a read-only fetch of the signing payload for one pending payment, so it",
|
|
1378
|
+
" cannot show a live allowance summary.",
|
|
1380
1379
|
" Acknowledge with EITHER --ack OR HAVEN_SIGNER_ACK=<hash> in your environment.",
|
|
1381
1380
|
""
|
|
1382
1381
|
].join("\n")
|