@haven_ai/signer 0.1.28-alpha.0 → 0.1.30-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/dist/cli.cjs +29 -5
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +29 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +29 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +29 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.cjs
CHANGED
|
@@ -204,6 +204,7 @@ function createEdgeSigner(delegateKey, options = {}) {
|
|
|
204
204
|
},
|
|
205
205
|
signX402FundingHash(hash, expected) {
|
|
206
206
|
assertExpectedBinding(hash, expected, options.x402BindingSigner, "hash");
|
|
207
|
+
assertPayerMatchesDelegate(expected, delegateAddress, options.agentId);
|
|
207
208
|
const signature = signAndVerify(hash);
|
|
208
209
|
const x402Binding = crypto.randomUUID();
|
|
209
210
|
x402Bindings.set(x402Binding, { ...expected });
|
|
@@ -211,6 +212,7 @@ function createEdgeSigner(delegateKey, options = {}) {
|
|
|
211
212
|
},
|
|
212
213
|
async signX402FundingTypedData(typedData, expected) {
|
|
213
214
|
assertExpectedBinding(expected.payloadHash, expected, options.x402BindingSigner, "typed-data");
|
|
215
|
+
assertPayerMatchesDelegate(expected, delegateAddress, options.agentId);
|
|
214
216
|
const digest = viem.hashTypedData(typedData);
|
|
215
217
|
if (digest.toLowerCase() !== expected.typedDataHash?.toLowerCase()) {
|
|
216
218
|
throw new sdk.HavenSigningError(
|
|
@@ -380,7 +382,7 @@ function assertExpectedShape(expected) {
|
|
|
380
382
|
throw new sdk.HavenSigningError("x402 expected funding context is required before signing a merchant header.");
|
|
381
383
|
}
|
|
382
384
|
}
|
|
383
|
-
var SUPPORTED_X402_EXPECTED_VERSIONS = [1, 2];
|
|
385
|
+
var SUPPORTED_X402_EXPECTED_VERSIONS = [1, 2, 3];
|
|
384
386
|
var SUPPORTED_SWEEP_BINDING_VERSIONS = [1];
|
|
385
387
|
function assertSupportedBindingVersion(received, supported, context) {
|
|
386
388
|
if (supported.includes(received)) return;
|
|
@@ -433,9 +435,11 @@ function assertExpectedBinding(payloadHash, expected, trustedSigner, mode = "has
|
|
|
433
435
|
asset: expected.asset,
|
|
434
436
|
network: expected.network,
|
|
435
437
|
expiresAt: expected.expiresAt,
|
|
436
|
-
typedDataHash: expected.typedDataHash
|
|
438
|
+
typedDataHash: expected.typedDataHash,
|
|
439
|
+
payerDelegate: expected.payerDelegate,
|
|
440
|
+
payerAgentId: expected.payerAgentId
|
|
437
441
|
});
|
|
438
|
-
const expectedVersion = expected.typedDataHash ? 2 : 1;
|
|
442
|
+
const expectedVersion = expected.payerDelegate ? 3 : expected.typedDataHash ? 2 : 1;
|
|
439
443
|
if (expected.auth?.version !== expectedVersion || expected.auth.message !== message) {
|
|
440
444
|
throw new sdk.HavenSigningError("x402 expected context authentication message is invalid.");
|
|
441
445
|
}
|
|
@@ -446,6 +450,15 @@ function assertExpectedBinding(payloadHash, expected, trustedSigner, mode = "has
|
|
|
446
450
|
throw new sdk.HavenSigningError("x402 expected context signature could not be verified.");
|
|
447
451
|
}
|
|
448
452
|
}
|
|
453
|
+
function assertPayerMatchesDelegate(expected, delegateAddress, localAgentId) {
|
|
454
|
+
if (!expected.payerDelegate) return;
|
|
455
|
+
if (sameAddress(expected.payerDelegate, delegateAddress)) return;
|
|
456
|
+
const quoteAgent = expected.payerAgentId ?? "unknown";
|
|
457
|
+
const localAgent = localAgentId ?? "unknown";
|
|
458
|
+
throw new sdk.HavenSigningError(
|
|
459
|
+
`This quote belongs to a DIFFERENT agent: the quote was created for agent ${quoteAgent} (delegate ${expected.payerDelegate}), but this signer holds agent ${localAgent} (delegate ${delegateAddress}). A long-lived host is holding stale credentials \u2014 its session authenticates as the old agent while the signer on disk belongs to the new one. Nothing was signed. Restart the host so it re-reads its wiring, then re-quote.`
|
|
460
|
+
);
|
|
461
|
+
}
|
|
449
462
|
function assertX402PaymentWindowOpen(expected) {
|
|
450
463
|
if (!expected.expiresAt) return;
|
|
451
464
|
const expiresAtMs = Date.parse(expected.expiresAt);
|
|
@@ -585,6 +598,11 @@ var x402ExpectedShape = {
|
|
|
585
598
|
// typed data the account validates; the signer refuses to raw-sign the bare
|
|
586
599
|
// hash when it is present, and refuses to sign typed data when it is absent.
|
|
587
600
|
typed_data_hash: v3.z.string().regex(/^0x[0-9a-fA-F]+$/, "typed_data_hash must be a 0x-prefixed hex string").optional(),
|
|
601
|
+
// #1690: present on a v3 context. The delegate this quote was created FOR —
|
|
602
|
+
// the signer refuses to sign when it is not its own. Inside the Haven-signed
|
|
603
|
+
// message, so stripping it here would only break the binding signature.
|
|
604
|
+
payer_delegate: v3.z.string().regex(/^0x[0-9a-fA-F]{40}$/, "payer_delegate must be a 0x-prefixed address").optional(),
|
|
605
|
+
payer_agent_id: v3.z.string().min(1).optional(),
|
|
588
606
|
auth: v3.z.object({
|
|
589
607
|
// Open at the boundary, enforced in the signer — see bindingVersionSchema.
|
|
590
608
|
version: bindingVersionSchema,
|
|
@@ -731,6 +749,8 @@ function toExpectedX402(raw) {
|
|
|
731
749
|
paymentId: raw.payment_id,
|
|
732
750
|
payloadHash: raw.payload_hash,
|
|
733
751
|
typedDataHash: raw.typed_data_hash,
|
|
752
|
+
payerDelegate: raw.payer_delegate,
|
|
753
|
+
payerAgentId: raw.payer_agent_id,
|
|
734
754
|
resourceUrl: raw.resource_url,
|
|
735
755
|
merchantTo: raw.merchant_to,
|
|
736
756
|
amount: raw.amount,
|
|
@@ -1186,7 +1206,7 @@ async function warnIfCredentialFilePermissive(path, log = (message) => process.s
|
|
|
1186
1206
|
|
|
1187
1207
|
// src/server.ts
|
|
1188
1208
|
var SIGNER_NAME = "@haven_ai/signer";
|
|
1189
|
-
var SIGNER_VERSION = "0.1.
|
|
1209
|
+
var SIGNER_VERSION = "0.1.30-alpha.0";
|
|
1190
1210
|
async function resolveSignerRuntime(options = {}) {
|
|
1191
1211
|
assertSupportedNodeVersion(options.nodeVersion);
|
|
1192
1212
|
if (options.delegateKey) {
|
|
@@ -1199,7 +1219,11 @@ async function resolveSignerRuntime(options = {}) {
|
|
|
1199
1219
|
const creds = await loadSignerCredentials(options.credentialsPath);
|
|
1200
1220
|
return {
|
|
1201
1221
|
signer: createEdgeSigner(creds.delegateKey, {
|
|
1202
|
-
x402BindingSigner: options.x402BindingSigner ?? creds.x402BindingSigner ?? process.env.HAVEN_X402_BINDING_SIGNER
|
|
1222
|
+
x402BindingSigner: options.x402BindingSigner ?? creds.x402BindingSigner ?? process.env.HAVEN_X402_BINDING_SIGNER,
|
|
1223
|
+
// #1690: the signer's own agent id, so a payer-mismatch refusal can name
|
|
1224
|
+
// both sides. The bare-delegateKey path above has no credential file and
|
|
1225
|
+
// therefore no agent id — the guard still works there, on addresses.
|
|
1226
|
+
agentId: creds.agentId
|
|
1203
1227
|
}),
|
|
1204
1228
|
credentials: creds
|
|
1205
1229
|
};
|