@haven_ai/signer 0.1.34-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 +8 -7
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +8 -7
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +4 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +36 -10
- package/dist/index.d.ts +36 -10
- package/dist/index.js +5 -7
- 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) {
|
|
@@ -410,7 +410,7 @@ function assertSupportedBindingVersion(received, supported, context) {
|
|
|
410
410
|
const highest = Math.max(...supported);
|
|
411
411
|
const outOfDate = received > highest;
|
|
412
412
|
const code = context === "x402 expected context" ? sdk.SignerRefusalCode.UnsupportedExpectedContextVersion : sdk.SignerRefusalCode.UnsupportedSweepBindingVersion;
|
|
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 (\`
|
|
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(", ")}.`;
|
|
414
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.`;
|
|
415
415
|
throw new sdk.HavenUnsupportedSignerVersionError(
|
|
416
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.`,
|
|
@@ -525,7 +525,7 @@ function signerInstructions() {
|
|
|
525
525
|
"Haven quote and prepare results report the expected-context version they will emit",
|
|
526
526
|
"(signer_compatibility.x402_expected_context_version). If that version is not in the list",
|
|
527
527
|
"above, this signer is out of date: STOP before signing, and tell the user to update",
|
|
528
|
-
|
|
528
|
+
`@haven_ai/signer by rerunning \`${sdk.connectorRerunCommand()}\`, which reinstalls the pinned`,
|
|
529
529
|
"MCP runtime. Do not edit the version field to a supported value \u2014 it is part of the",
|
|
530
530
|
"Haven-signed binding message, so changing it invalidates the signature.",
|
|
531
531
|
"",
|
|
@@ -822,7 +822,7 @@ function createToolHandlers(signer, options = {}) {
|
|
|
822
822
|
const identity = await options.signContext?.loadIdentity() ?? null;
|
|
823
823
|
if (!identity) {
|
|
824
824
|
throw new sdk.HavenSigningError(
|
|
825
|
-
|
|
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.`
|
|
826
826
|
);
|
|
827
827
|
}
|
|
828
828
|
const ctx = await fetchX402SignContext(
|
|
@@ -1237,7 +1237,7 @@ async function warnIfCredentialFilePermissive(path, log = (message) => process.s
|
|
|
1237
1237
|
|
|
1238
1238
|
// src/server.ts
|
|
1239
1239
|
var SIGNER_NAME = "@haven_ai/signer";
|
|
1240
|
-
var SIGNER_VERSION = "0.1.
|
|
1240
|
+
var SIGNER_VERSION = "0.1.35-alpha.0";
|
|
1241
1241
|
async function resolveSignerRuntime(options = {}) {
|
|
1242
1242
|
assertSupportedNodeVersion(options.nodeVersion);
|
|
1243
1243
|
if (options.delegateKey) {
|
|
@@ -1373,8 +1373,9 @@ function parseArgs(argv) {
|
|
|
1373
1373
|
"",
|
|
1374
1374
|
"Consent:",
|
|
1375
1375
|
" On first launch the signer prints the sign-only tool list and delegate",
|
|
1376
|
-
" address, then refuses to start unless acknowledged.
|
|
1377
|
-
"
|
|
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.",
|
|
1378
1379
|
" Acknowledge with EITHER --ack OR HAVEN_SIGNER_ACK=<hash> in your environment.",
|
|
1379
1380
|
""
|
|
1380
1381
|
].join("\n")
|