@paybond/kit 0.5.0 → 0.6.1

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 CHANGED
@@ -111,7 +111,7 @@ Advanced exports:
111
111
 
112
112
  `allowedTools` values are your own tool or operation names, not a Paybond-owned catalog. Harbor enforces string matching against whatever names you chose when creating the intent.
113
113
 
114
- `settlementRail` on intent creation is only a rail request. Stripe destinations and x402 receive addresses stay tenant-owned server-side config and are never supplied by the SDK caller.
114
+ `settlementRail` on intent creation is a principal-signed rail request. Stripe destinations and x402 receive addresses stay tenant-owned server-side config and are never supplied by the SDK caller.
115
115
 
116
116
  The protocol-v2 surface is trust-first: signed mandates, recognition proofs, and receipts work across supported settlement adapters instead of treating any single rail as the product boundary.
117
117
 
@@ -0,0 +1 @@
1
+ export declare function ensureEd25519Sha512Sync(): void;
@@ -0,0 +1,14 @@
1
+ import { createHash } from "node:crypto";
2
+ import { etc } from "@noble/ed25519";
3
+ export function ensureEd25519Sha512Sync() {
4
+ if (etc.sha512Sync) {
5
+ return;
6
+ }
7
+ etc.sha512Sync = (...messages) => {
8
+ const hash = createHash("sha512");
9
+ for (const message of messages) {
10
+ hash.update(message);
11
+ }
12
+ return new Uint8Array(hash.digest());
13
+ };
14
+ }
package/dist/index.d.ts CHANGED
@@ -792,8 +792,8 @@ export declare class HarborClient {
792
792
  */
793
793
  getLedgerAuthority(): Promise<Record<string, unknown>>;
794
794
  /**
795
- * `GET /ledger/v1/events` paginated append-only history; `afterSeq` is an exclusive cursor.
796
- * `limit` is clamped to 1256 to match Harbor.
795
+ * `GET /ledger/v1/events` - protected Harbor append-only history for trusted clients.
796
+ * `afterSeq` is an exclusive cursor; `limit` is clamped to 1..256 to match Harbor.
797
797
  */
798
798
  getLedgerEvents(options?: {
799
799
  afterSeq?: number;
@@ -946,14 +946,17 @@ export declare class ServiceAccountFraudSession {
946
946
  }
947
947
  /**
948
948
  * Parameters for {@link PaybondIntents.create} (tenant is taken from the bound Harbor client).
949
- * `settlementRail`, when set, requests one allowed rail; Harbor still resolves the destination
950
- * from tenant-owned settlement config.
949
+ * `settlementRail` is principal-signed and requests one allowed rail; Harbor still resolves the
950
+ * destination from tenant-owned settlement config.
951
951
  */
952
952
  export type PaybondCreateIntentParams = Omit<BuildSignedCreateIntentParams, "tenantId" | "intentId"> & {
953
953
  intentId?: string;
954
954
  };
955
955
  /** Parameters for {@link PaybondIntents.submitEvidence} (tenant is taken from the bound Harbor client). */
956
- export type PaybondSubmitEvidenceParams = Omit<SignPayeeEvidenceParams, "tenantId">;
956
+ export type PaybondSubmitEvidenceParams = Omit<SignPayeeEvidenceParams, "tenantId" | "artifactsBlake3Hex" | "submittedAtRfc3339"> & {
957
+ artifactsBlake3Hex?: string[];
958
+ submittedAtRfc3339?: string;
959
+ };
957
960
  /**
958
961
  * Ergonomic intent helpers: principal-signed intent create, x402 funding, and payee-signed evidence.
959
962
  */
@@ -962,7 +965,7 @@ export declare class PaybondIntents {
962
965
  constructor(harbor: HarborClient);
963
966
  /**
964
967
  * Build a principal-signed `POST /intents` body and submit it. `principalSigningSeed` must be
965
- * 32 bytes. `settlementRail` only requests an allowed rail; destinations stay server-owned.
968
+ * 32 bytes. `settlementRail` is signed as the requested rail; destinations stay server-owned.
966
969
  */
967
970
  create(params: PaybondCreateIntentParams & {
968
971
  idempotencyKey?: string;
package/dist/index.js CHANGED
@@ -613,8 +613,8 @@ export class HarborClient {
613
613
  return body;
614
614
  }
615
615
  /**
616
- * `GET /ledger/v1/events` paginated append-only history; `afterSeq` is an exclusive cursor.
617
- * `limit` is clamped to 1256 to match Harbor.
616
+ * `GET /ledger/v1/events` - protected Harbor append-only history for trusted clients.
617
+ * `afterSeq` is an exclusive cursor; `limit` is clamped to 1..256 to match Harbor.
618
618
  */
619
619
  async getLedgerEvents(options) {
620
620
  const afterSeq = Math.max(0, Math.floor(options?.afterSeq ?? 0));
@@ -1527,6 +1527,9 @@ export class ServiceAccountFraudSession {
1527
1527
  await Promise.resolve();
1528
1528
  }
1529
1529
  }
1530
+ function nowRfc3339Seconds() {
1531
+ return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
1532
+ }
1530
1533
  /**
1531
1534
  * Ergonomic intent helpers: principal-signed intent create, x402 funding, and payee-signed evidence.
1532
1535
  */
@@ -1537,7 +1540,7 @@ export class PaybondIntents {
1537
1540
  }
1538
1541
  /**
1539
1542
  * Build a principal-signed `POST /intents` body and submit it. `principalSigningSeed` must be
1540
- * 32 bytes. `settlementRail` only requests an allowed rail; destinations stay server-owned.
1543
+ * 32 bytes. `settlementRail` is signed as the requested rail; destinations stay server-owned.
1541
1544
  */
1542
1545
  async create(params) {
1543
1546
  const { idempotencyKey, intentId: maybeIntentId, ...fields } = params;
@@ -1562,9 +1565,11 @@ export class PaybondIntents {
1562
1565
  * Sign payee evidence and POST it. `payeeSigningSeed` must be 32 bytes.
1563
1566
  */
1564
1567
  async submitEvidence(params) {
1565
- const { idempotencyKey, ...rest } = params;
1568
+ const { idempotencyKey, artifactsBlake3Hex = [], submittedAtRfc3339 = nowRfc3339Seconds(), ...rest } = params;
1566
1569
  const wire = signPayeeEvidenceBinding({
1567
1570
  tenantId: this.harbor.tenantId,
1571
+ artifactsBlake3Hex,
1572
+ submittedAtRfc3339,
1568
1573
  ...rest,
1569
1574
  });
1570
1575
  return this.harbor.submitEvidence(rest.intentId, wire, { idempotencyKey });
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { GatewayAuthError, GatewayFraudClient, GatewaySignalClient, HarborHttpError, ServiceAccountHarborSession, SignalHttpError, } from "./index.js";
3
3
  const SERVER_NAME = "Paybond MCP";
4
- const SERVER_VERSION = "0.5.0";
4
+ const SERVER_VERSION = "0.6.0";
5
5
  const MCP_PROTOCOL_VERSION = "2025-11-25";
6
6
  const DEFAULT_HARBOR_ACCESS_PATH = "/v1/auth/harbor-access";
7
7
  const DEFAULT_PRINCIPAL_PATH = "/v1/auth/principal";
@@ -4,6 +4,7 @@
4
4
  import { sign, getPublicKey } from "@noble/ed25519";
5
5
  import { createHash } from "blake3";
6
6
  import { parse as parseUuid } from "uuid";
7
+ import { ensureEd25519Sha512Sync } from "./ed25519-sync.js";
7
8
  import { jsonValueDigest } from "./json-digest.js";
8
9
  function concatBytes(...parts) {
9
10
  const n = parts.reduce((a, p) => a + p.length, 0);
@@ -65,6 +66,7 @@ export function signPayeeEvidenceBinding(params) {
65
66
  if (params.payeeSigningSeed.length !== 32) {
66
67
  throw new Error("payeeSigningSeed must be 32 bytes");
67
68
  }
69
+ ensureEd25519Sha512Sync();
68
70
  const artifactBin = params.artifactsBlake3Hex.map((h) => hexToBytes(h));
69
71
  const payloadDigest = jsonValueDigest(params.payload);
70
72
  const artDigest = artifactsDigest(artifactBin);
@@ -17,6 +17,7 @@ export declare function intentCreationSignBytesRaw(input: {
17
17
  predicate: Record<string, unknown>;
18
18
  predicateRef: string;
19
19
  allowedTools: string[];
20
+ settlementRail: SettlementRail;
20
21
  }): Uint8Array;
21
22
  export type BuildSignedCreateIntentParams = {
22
23
  tenantId: string;
@@ -32,11 +33,11 @@ export type BuildSignedCreateIntentParams = {
32
33
  deadlineRfc3339: string;
33
34
  allowedTools: string[];
34
35
  predicateRef?: string;
35
- /** Optional rail request for the new intent. Harbor resolves the destination server-side. */
36
- settlementRail?: SettlementRail;
36
+ /** Rail request for the new intent. Harbor resolves the destination server-side. */
37
+ settlementRail: SettlementRail;
37
38
  };
38
39
  /**
39
40
  * Build a Harbor `POST /intents` JSON body with principal Ed25519 detached signature.
40
- * `settlementRail` only requests an allowed rail; destinations remain tenant-owned server-side.
41
+ * `settlementRail` is included in the signature; destinations remain tenant-owned server-side.
41
42
  */
42
43
  export declare function buildSignedCreateIntentBody(params: BuildSignedCreateIntentParams): Record<string, unknown>;
@@ -4,6 +4,7 @@
4
4
  */
5
5
  import { sign, getPublicKey } from "@noble/ed25519";
6
6
  import { parse as parseUuid } from "uuid";
7
+ import { ensureEd25519Sha512Sync } from "./ed25519-sync.js";
7
8
  import { jsonValueDigest } from "./json-digest.js";
8
9
  const SETTLEMENT_RAIL_VALUES = new Set(["stripe_connect", "x402_usdc_base"]);
9
10
  function validateSettlementRail(value) {
@@ -49,15 +50,15 @@ function allowedToolsDigest(tools) {
49
50
  .filter((v, i, a) => a.indexOf(v) === i);
50
51
  return jsonValueDigest(sorted);
51
52
  }
52
- /** Bincode payload for principal intent creation (wire format revision byte `2`). */
53
+ /** Bincode payload for principal intent creation (wire format revision byte `4`). */
53
54
  function encodeIntentCreationSign(input) {
54
- const version = new Uint8Array([2]);
55
+ const version = new Uint8Array([4]);
55
56
  const intentBytes = parseUuid(input.intentId);
56
57
  if (intentBytes.length !== 16) {
57
58
  throw new Error("intentId must be a UUID string");
58
59
  }
59
60
  // Serde `Uuid` + bincode uses a u64 length prefix (16) before the raw 16 bytes (matches Rust).
60
- return concatBytes(version, encodeBincodeString(input.tenantId), encodeU64(16), intentBytes, encodeBincodeString(input.principalDid), encodeBincodeString(input.payeeDid), encodeI64(input.amountCents), encodeBincodeString(input.currency), encodeBincodeString(input.deadlineRfc3339), input.budgetDigest, input.evidenceSchemaDigest, input.predicateDslDigest, encodeBincodeString(input.predicateRef), input.allowedToolsDigest);
61
+ return concatBytes(version, encodeBincodeString(input.tenantId), encodeU64(16), intentBytes, encodeBincodeString(input.principalDid), encodeBincodeString(input.payeeDid), encodeI64(input.amountCents), encodeBincodeString(input.currency), encodeBincodeString(input.deadlineRfc3339), input.budgetDigest, input.evidenceSchemaDigest, input.predicateDslDigest, encodeBincodeString(input.predicateRef), input.allowedToolsDigest, encodeBincodeString(input.settlementRail));
61
62
  }
62
63
  export function intentCreationSignBytesRaw(input) {
63
64
  const budgetDigest = jsonValueDigest(input.budget);
@@ -77,6 +78,7 @@ export function intentCreationSignBytesRaw(input) {
77
78
  predicateDslDigest,
78
79
  predicateRef: input.predicateRef,
79
80
  allowedToolsDigest: allowedDigest,
81
+ settlementRail: input.settlementRail,
80
82
  });
81
83
  }
82
84
  function bytesToBase64(bytes) {
@@ -87,7 +89,7 @@ function bytesToBase64(bytes) {
87
89
  }
88
90
  /**
89
91
  * Build a Harbor `POST /intents` JSON body with principal Ed25519 detached signature.
90
- * `settlementRail` only requests an allowed rail; destinations remain tenant-owned server-side.
92
+ * `settlementRail` is included in the signature; destinations remain tenant-owned server-side.
91
93
  */
92
94
  export function buildSignedCreateIntentBody(params) {
93
95
  if (params.principalSigningSeed.length !== 32) {
@@ -96,10 +98,9 @@ export function buildSignedCreateIntentBody(params) {
96
98
  if (params.allowedTools.length === 0) {
97
99
  throw new Error("allowedTools must be non-empty");
98
100
  }
99
- const settlementRail = params.settlementRail
100
- ? validateSettlementRail(params.settlementRail)
101
- : undefined;
101
+ const settlementRail = validateSettlementRail(params.settlementRail);
102
102
  const predicateRef = params.predicateRef ?? "";
103
+ ensureEd25519Sha512Sync();
103
104
  const msg = intentCreationSignBytesRaw({
104
105
  tenantId: params.tenantId,
105
106
  intentId: params.intentId,
@@ -113,6 +114,7 @@ export function buildSignedCreateIntentBody(params) {
113
114
  predicate: params.predicate,
114
115
  predicateRef,
115
116
  allowedTools: params.allowedTools,
117
+ settlementRail,
116
118
  });
117
119
  const sig = sign(msg, params.principalSigningSeed);
118
120
  const pub = getPublicKey(params.principalSigningSeed);
@@ -128,15 +130,13 @@ export function buildSignedCreateIntentBody(params) {
128
130
  evidence_schema: params.evidenceSchema,
129
131
  deadline: params.deadlineRfc3339,
130
132
  predicate_dsl: params.predicate,
131
- signing_version: 2,
133
+ settlement_rail: settlementRail,
134
+ signing_version: 4,
132
135
  policy_binding: null,
133
136
  allowed_tools: params.allowedTools,
134
137
  };
135
138
  if (predicateRef.trim() !== "") {
136
139
  body.predicate_ref = predicateRef;
137
140
  }
138
- if (settlementRail) {
139
- body.settlement_rail = settlementRail;
140
- }
141
141
  return body;
142
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paybond/kit",
3
- "version": "0.5.0",
3
+ "version": "0.6.1",
4
4
  "description": "Paybond Kit for TypeScript: tenant-bound Harbor sessions, capability verification, and signed intent/evidence flows.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",