@paybond/kit 0.6.0 → 0.7.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # `@paybond/kit`
2
2
 
3
- Paybond Kit for TypeScript is the npm package for tenant-bound Paybond integrations. It opens gateway-authenticated Harbor sessions, verifies capability tokens, signs intent and evidence payloads, funds x402 / USDC-on-Base intents, and reads tenant-scoped Signal, fraud, ledger, protocol, and A2A data.
3
+ Paybond Kit for TypeScript is the npm package for tenant-bound Paybond integrations. It opens hosted Gateway sessions, verifies capability tokens, signs intent and evidence payloads, uses Stripe Connect or x402 / USDC-on-Base settlement rails, and reads tenant-scoped Signal, fraud, ledger, protocol, and A2A data.
4
4
 
5
5
  ## Install
6
6
 
@@ -17,17 +17,14 @@ npm install @paybond/kit
17
17
  ## Requirements
18
18
 
19
19
  - Node.js 22+
20
- - A `paybond_sk_...` service-account API key
21
- - Reachable Gateway and Harbor base URLs
20
+ - A `paybond_sk_sandbox_...` or `paybond_sk_live_...` service-account API key
22
21
  - For capability verification: a funded intent id and a capability token minted for that intent
23
22
  - For intent creation or evidence submission: 32-byte Ed25519 signing seeds owned by your application
24
23
 
25
24
  Minimal environment for the quick start:
26
25
 
27
26
  ```bash
28
- export PAYBOND_GATEWAY_URL="https://gateway.example.com"
29
- export PAYBOND_HARBOR_URL="https://harbor.example.com"
30
- export PAYBOND_API_KEY="paybond_sk_..."
27
+ export PAYBOND_API_KEY="paybond_sk_sandbox_..."
31
28
  ```
32
29
 
33
30
  Optional, if you want the quick start to verify a capability:
@@ -39,7 +36,7 @@ export PAYBOND_CAPABILITY="base64-biscuit-token"
39
36
 
40
37
  ## Tenant isolation
41
38
 
42
- Every session is bound to the tenant realm echoed by gateway-authenticated service-account introspection and Harbor access exchange flows.
39
+ Every session is bound to the tenant realm echoed by gateway-authenticated service-account introspection.
43
40
 
44
41
  - Do not pass tenant ids by hand for normal SDK usage.
45
42
  - Construct one `Paybond` session per tenant/service account.
@@ -59,9 +56,8 @@ function requiredEnv(name: string): string {
59
56
  }
60
57
 
61
58
  const paybond = await Paybond.open({
62
- gatewayBaseUrl: requiredEnv("PAYBOND_GATEWAY_URL"),
63
59
  apiKey: requiredEnv("PAYBOND_API_KEY"),
64
- harborBaseUrl: requiredEnv("PAYBOND_HARBOR_URL"),
60
+ expectedEnvironment: "sandbox",
65
61
  });
66
62
 
67
63
  try {
@@ -91,7 +87,7 @@ try {
91
87
 
92
88
  Core SDK:
93
89
 
94
- - `Paybond.open(...)` for gateway-authenticated, tenant-derived Harbor sessions
90
+ - `Paybond.open(...)` for API-key-only, tenant-derived hosted sessions
95
91
  - `HarborClient` for capability verification, intent creation, x402 funding, evidence submission, and ledger reads
96
92
  - `paybond.signal` and `paybond.fraud` on `Paybond` sessions opened from one service-account API key
97
93
  - `PaybondIntents` helpers for principal-signed intent creation, x402 funding, and payee-signed evidence submission
@@ -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
@@ -1,6 +1,6 @@
1
1
  /**
2
- * Paybond Kit — TypeScript Harbor client with tenant binding, retries, optional upstream JWT,
3
- * and gateway service-account token exchange (`POST /v1/auth/harbor-access`).
2
+ * Paybond Kit — TypeScript Gateway client with tenant binding, retries, capability verification,
3
+ * and signed intent/evidence helpers.
4
4
  */
5
5
  import { type BuildSignedCreateIntentParams, type SettlementRail } from "./principal-intent.js";
6
6
  import { type SignPayeeEvidenceParams } from "./payee-evidence.js";
@@ -56,7 +56,8 @@ export type FundIntentResult = {
56
56
  capabilityToken?: string;
57
57
  funding?: IntentFundingResult;
58
58
  };
59
- /** Async supplier for short-lived Harbor JWTs minted by the Paybond gateway. */
59
+ export declare const DEFAULT_PAYBOND_GATEWAY_BASE_URL = "https://api.paybond.ai";
60
+ /** Async supplier for upstream Harbor bearer tokens on low-level direct clients. */
60
61
  export type HarborBearerSupplier = () => Promise<string | null | undefined>;
61
62
  /**
62
63
  * Structured HTTP failure from Harbor with operator-facing diagnostics.
@@ -72,7 +73,7 @@ export declare class HarborHttpError extends Error {
72
73
  });
73
74
  }
74
75
  /**
75
- * Gateway rejected the service-account exchange or returned an unusable harbor-access payload.
76
+ * Gateway rejected service-account credentials or returned an unusable tenant-principal payload.
76
77
  */
77
78
  export declare class GatewayAuthError extends Error {
78
79
  readonly statusCode: number | undefined;
@@ -642,72 +643,23 @@ export declare class ProtocolHttpError extends Error {
642
643
  errorMessage?: string;
643
644
  });
644
645
  }
645
- /**
646
- * Exchanges a `paybond_sk_` API key for short-lived Harbor JWTs and caches tenant realm from the
647
- * gateway response (no separate tenant env var for the default path).
648
- */
649
- export declare class GatewayHarborTokenProvider {
650
- private readonly gatewayBase;
651
- private readonly apiKey;
652
- private readonly path;
653
- private readonly skewMs;
654
- private readonly clock;
655
- private token;
656
- private tenantIdValue;
657
- private notAfterMonotonic;
658
- private refreshTail;
659
- constructor(init: {
660
- gatewayBaseUrl: string;
661
- apiKey: string;
662
- harborAccessPath?: string;
663
- clockSkewSeconds?: number;
664
- /** Injectable monotonic clock (milliseconds) for tests. */
665
- clock?: () => number;
666
- });
667
- get tenantId(): string | null;
668
- /**
669
- * First exchange; returns tenant realm echoed by the gateway.
670
- */
671
- ensureInitial(): Promise<string>;
672
- /** Return a valid Harbor JWT, refreshing when near expiry. */
673
- bearer(): Promise<string>;
674
- /** Force rotation (credential rotation drills). */
675
- forceRotate(): Promise<void>;
676
- private refresh;
677
- private refreshInner;
678
- }
646
+ export type PaybondEnvironment = "live" | "sandbox";
679
647
  /**
680
648
  * Tenant-scoped Harbor binding for one funded intent and one Biscuit capability token.
681
649
  */
682
650
  export declare class PaybondCapabilityBinding {
683
- readonly harbor: HarborClient;
651
+ readonly harbor: Pick<HarborClient | GatewayHarborClient, "tenantId" | "verifyCapability">;
684
652
  readonly intentId: string;
685
653
  readonly capabilityToken: string;
686
- constructor(harbor: HarborClient, intentId: string, capabilityToken: string);
654
+ constructor(harbor: Pick<HarborClient | GatewayHarborClient, "tenantId" | "verifyCapability">, intentId: string, capabilityToken: string);
687
655
  }
688
- export type ServiceAccountHarborSessionInit = {
689
- gatewayBaseUrl: string;
656
+ export type PaybondOpenOptions = {
690
657
  apiKey: string;
691
- harborBaseUrl: string;
692
- harborAccessPath?: string;
693
- clockSkewSeconds?: number;
658
+ gatewayBaseUrl?: string;
659
+ principalPath?: string;
660
+ expectedEnvironment?: PaybondEnvironment;
694
661
  maxRetries?: number;
695
662
  };
696
- /**
697
- * Harbor client plus gateway token lifecycle for one service account.
698
- */
699
- export declare class ServiceAccountHarborSession {
700
- readonly harbor: HarborClient;
701
- private readonly tokens;
702
- private constructor();
703
- /**
704
- * Build a tenant-bound {@link HarborClient} using gateway-derived tenant id and JWT supplier.
705
- */
706
- static open(init: ServiceAccountHarborSessionInit): Promise<ServiceAccountHarborSession>;
707
- rotateHarborToken(): Promise<void>;
708
- /** Reserved for future HTTP client cleanup; safe to call after work completes. */
709
- aclose(): Promise<void>;
710
- }
711
663
  type HarborClientOptions = {
712
664
  harborBearerSupplier?: HarborBearerSupplier;
713
665
  staticHarborBearerToken?: string;
@@ -792,8 +744,8 @@ export declare class HarborClient {
792
744
  */
793
745
  getLedgerAuthority(): Promise<Record<string, unknown>>;
794
746
  /**
795
- * `GET /ledger/v1/events` paginated append-only history; `afterSeq` is an exclusive cursor.
796
- * `limit` is clamped to 1256 to match Harbor.
747
+ * `GET /ledger/v1/events` - protected Harbor append-only history for trusted clients.
748
+ * `afterSeq` is an exclusive cursor; `limit` is clamped to 1..256 to match Harbor.
797
749
  */
798
750
  getLedgerEvents(options?: {
799
751
  afterSeq?: number;
@@ -804,6 +756,43 @@ export declare class HarborClient {
804
756
  */
805
757
  getLedgerMerkleLatest(): Promise<Record<string, unknown>>;
806
758
  }
759
+ type GatewayHarborClientOptions = {
760
+ staticGatewayBearerToken: string;
761
+ maxRetries?: number;
762
+ };
763
+ type GatewayHarborMutationOptions = {
764
+ idempotencyKey?: string;
765
+ recognitionProof?: AgentRecognitionProofV1 | Record<string, unknown>;
766
+ };
767
+ /**
768
+ * Gateway-backed Harbor surface for hosted Paybond integrations.
769
+ *
770
+ * This client sends the service-account API key to the public Gateway. Gateway derives tenant,
771
+ * mints upstream Harbor credentials internally, and applies recognition/guardrail checks before
772
+ * forwarding state-changing Harbor requests.
773
+ */
774
+ export declare class GatewayHarborClient {
775
+ private readonly base;
776
+ readonly tenantId: string;
777
+ private readonly staticGatewayBearerToken;
778
+ private readonly maxRetries;
779
+ constructor(gatewayBaseUrl: string, tenantId: string, options: GatewayHarborClientOptions);
780
+ private headers;
781
+ private fetchWithRetries;
782
+ private postJSON;
783
+ private mutationHeaders;
784
+ verifyCapability(input: {
785
+ intentId: string;
786
+ token: string;
787
+ operation: string;
788
+ requestedSpendCents?: number;
789
+ }): Promise<VerifyCapabilityResult>;
790
+ createIntent(body: Record<string, unknown>, options: GatewayHarborMutationOptions): Promise<Record<string, unknown>>;
791
+ fundIntent(intentId: string, options: GatewayHarborMutationOptions & {
792
+ paymentSignature?: string;
793
+ }): Promise<FundIntentResult>;
794
+ submitEvidence(intentId: string, evidenceBody: Record<string, unknown>, options: GatewayHarborMutationOptions): Promise<SubmitEvidenceResult>;
795
+ }
807
796
  type GatewaySignalClientOptions = {
808
797
  staticGatewayBearerToken: string;
809
798
  maxRetries?: number;
@@ -920,9 +909,10 @@ export declare class GatewayProtocolClient {
920
909
  }): Promise<Record<string, unknown>>;
921
910
  }
922
911
  export type ServiceAccountSignalSessionInit = {
923
- gatewayBaseUrl: string;
924
912
  apiKey: string;
913
+ gatewayBaseUrl?: string;
925
914
  principalPath?: string;
915
+ expectedEnvironment?: PaybondEnvironment;
926
916
  maxRetries?: number;
927
917
  };
928
918
  export type ServiceAccountFraudSessionInit = ServiceAccountSignalSessionInit;
@@ -951,15 +941,20 @@ export declare class ServiceAccountFraudSession {
951
941
  */
952
942
  export type PaybondCreateIntentParams = Omit<BuildSignedCreateIntentParams, "tenantId" | "intentId"> & {
953
943
  intentId?: string;
944
+ recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>;
954
945
  };
955
946
  /** Parameters for {@link PaybondIntents.submitEvidence} (tenant is taken from the bound Harbor client). */
956
- export type PaybondSubmitEvidenceParams = Omit<SignPayeeEvidenceParams, "tenantId">;
947
+ export type PaybondSubmitEvidenceParams = Omit<SignPayeeEvidenceParams, "tenantId" | "artifactsBlake3Hex" | "submittedAtRfc3339"> & {
948
+ artifactsBlake3Hex?: string[];
949
+ submittedAtRfc3339?: string;
950
+ recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>;
951
+ };
957
952
  /**
958
953
  * Ergonomic intent helpers: principal-signed intent create, x402 funding, and payee-signed evidence.
959
954
  */
960
955
  export declare class PaybondIntents {
961
956
  private readonly harbor;
962
- constructor(harbor: HarborClient);
957
+ constructor(harbor: HarborClient | GatewayHarborClient);
963
958
  /**
964
959
  * Build a principal-signed `POST /intents` body and submit it. `principalSigningSeed` must be
965
960
  * 32 bytes. `settlementRail` is signed as the requested rail; destinations stay server-owned.
@@ -972,6 +967,7 @@ export declare class PaybondIntents {
972
967
  */
973
968
  fund(params: {
974
969
  intentId: string;
970
+ recognitionProof: AgentRecognitionProofV1 | Record<string, unknown>;
975
971
  paymentSignature?: string;
976
972
  idempotencyKey?: string;
977
973
  }): Promise<FundIntentResult>;
@@ -983,21 +979,19 @@ export declare class PaybondIntents {
983
979
  }): Promise<SubmitEvidenceResult>;
984
980
  }
985
981
  /**
986
- * High-level Kit entrypoint: same session lifecycle as {@link ServiceAccountHarborSession}, plus {@link PaybondIntents}.
982
+ * High-level Kit entrypoint: tenant-bound Gateway clients plus ergonomic intent helpers.
987
983
  */
988
984
  export declare class Paybond {
989
- readonly harbor: HarborClient;
985
+ readonly harbor: GatewayHarborClient;
990
986
  readonly signal: GatewaySignalClient;
991
987
  readonly fraud: GatewayFraudClient;
992
988
  readonly a2a: GatewayA2AClient;
993
989
  readonly protocol: GatewayProtocolClient;
994
990
  readonly intents: PaybondIntents;
995
- private readonly session;
996
991
  private constructor();
997
- /** Open a tenant-bound session via gateway `harbor-access` exchange. */
998
- static open(init: ServiceAccountHarborSessionInit): Promise<Paybond>;
999
- rotateHarborToken(): Promise<void>;
1000
- /** Release HTTP resources (Harbor client + gateway token provider). */
992
+ /** Open a tenant-bound hosted Paybond session from a service-account API key. */
993
+ static open(init: PaybondOpenOptions): Promise<Paybond>;
994
+ /** Reserved for future HTTP client cleanup; safe to call after work completes. */
1001
995
  aclose(): Promise<void>;
1002
996
  }
1003
997
  export { normalizeJson, jsonValueDigest } from "./json-digest.js";
package/dist/index.js CHANGED
@@ -1,9 +1,14 @@
1
1
  /**
2
- * Paybond Kit — TypeScript Harbor client with tenant binding, retries, optional upstream JWT,
3
- * and gateway service-account token exchange (`POST /v1/auth/harbor-access`).
2
+ * Paybond Kit — TypeScript Gateway client with tenant binding, retries, capability verification,
3
+ * and signed intent/evidence helpers.
4
4
  */
5
5
  import { buildSignedCreateIntentBody, } from "./principal-intent.js";
6
6
  import { signPayeeEvidenceBinding } from "./payee-evidence.js";
7
+ export const DEFAULT_PAYBOND_GATEWAY_BASE_URL = "https://api.paybond.ai";
8
+ function defaultGatewayBaseUrl(value) {
9
+ const trimmed = value?.trim();
10
+ return trimmed ? trimmed : DEFAULT_PAYBOND_GATEWAY_BASE_URL;
11
+ }
7
12
  /**
8
13
  * Structured HTTP failure from Harbor with operator-facing diagnostics.
9
14
  */
@@ -20,7 +25,7 @@ export class HarborHttpError extends Error {
20
25
  }
21
26
  }
22
27
  /**
23
- * Gateway rejected the service-account exchange or returned an unusable harbor-access payload.
28
+ * Gateway rejected service-account credentials or returned an unusable tenant-principal payload.
24
29
  */
25
30
  export class GatewayAuthError extends Error {
26
31
  statusCode;
@@ -117,103 +122,26 @@ function parseRetryAfterSeconds(v) {
117
122
  return null;
118
123
  return Math.min(n, 30);
119
124
  }
120
- const DEFAULT_HARBOR_ACCESS_PATH = "/v1/auth/harbor-access";
121
- /**
122
- * Exchanges a `paybond_sk_` API key for short-lived Harbor JWTs and caches tenant realm from the
123
- * gateway response (no separate tenant env var for the default path).
124
- */
125
- export class GatewayHarborTokenProvider {
126
- gatewayBase;
127
- apiKey;
128
- path;
129
- skewMs;
130
- clock;
131
- token = null;
132
- tenantIdValue = null;
133
- notAfterMonotonic = 0;
134
- refreshTail = Promise.resolve();
135
- constructor(init) {
136
- this.gatewayBase = normalizeBase(init.gatewayBaseUrl);
137
- this.apiKey = init.apiKey.trim();
138
- const rawPath = (init.harborAccessPath ?? DEFAULT_HARBOR_ACCESS_PATH).trim();
139
- this.path = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
140
- this.skewMs = Math.max(0, (init.clockSkewSeconds ?? 90) * 1000);
141
- this.clock = init.clock ?? (() => performance.now());
142
- }
143
- get tenantId() {
144
- return this.tenantIdValue;
145
- }
146
- /**
147
- * First exchange; returns tenant realm echoed by the gateway.
148
- */
149
- async ensureInitial() {
150
- await this.refresh(true);
151
- if (!this.tenantIdValue) {
152
- throw new GatewayAuthError("harbor-access response missing tenant_id; upgrade gateway (PAYBOND-V1-008)");
153
- }
154
- return this.tenantIdValue;
155
- }
156
- /** Return a valid Harbor JWT, refreshing when near expiry. */
157
- async bearer() {
158
- await this.refresh(false);
159
- if (!this.token) {
160
- throw new GatewayAuthError("harbor-access did not return access_token");
161
- }
162
- return this.token;
163
- }
164
- /** Force rotation (credential rotation drills). */
165
- async forceRotate() {
166
- await this.refresh(true);
167
- }
168
- async refresh(force) {
169
- const job = this.refreshTail.then(() => this.refreshInner(force));
170
- this.refreshTail = job.then(() => undefined, () => undefined);
171
- await job;
125
+ function normalizeExpectedEnvironment(value) {
126
+ if (value === undefined)
127
+ return null;
128
+ const env = String(value).trim();
129
+ if (env === "live" || env === "sandbox") {
130
+ return env;
172
131
  }
173
- async refreshInner(force) {
174
- const now = this.clock();
175
- if (!force && this.token && now < this.notAfterMonotonic) {
176
- return;
177
- }
178
- const url = `${this.gatewayBase}${this.path}`;
179
- const res = await fetch(url, {
180
- method: "POST",
181
- headers: {
182
- authorization: `Bearer ${this.apiKey}`,
183
- accept: "application/json",
184
- },
132
+ throw new GatewayAuthError(`expectedEnvironment must be "live" or "sandbox", got ${JSON.stringify(value)}`);
133
+ }
134
+ function assertExpectedEnvironment(source, actualRaw, expected, bodyText) {
135
+ if (!expected)
136
+ return;
137
+ const actual = typeof actualRaw === "string" ? actualRaw.trim() : "";
138
+ if (!actual) {
139
+ throw new GatewayAuthError(`${source} response missing environment`, { bodyText });
140
+ }
141
+ if (actual !== expected) {
142
+ throw new GatewayAuthError(`${source} environment mismatch: expected=${expected} gateway=${actual}`, {
143
+ bodyText,
185
144
  });
186
- const text = await res.text();
187
- if (!res.ok) {
188
- throw new GatewayAuthError(`harbor-access HTTP ${res.status}`, {
189
- statusCode: res.status,
190
- bodyText: text,
191
- });
192
- }
193
- let body;
194
- try {
195
- body = JSON.parse(text);
196
- }
197
- catch {
198
- throw new GatewayAuthError("harbor-access response was not JSON", { bodyText: text });
199
- }
200
- const access = String(body.access_token ?? "").trim();
201
- if (!access) {
202
- throw new GatewayAuthError("harbor-access JSON missing access_token", { bodyText: text });
203
- }
204
- const expIn = Number(body.expires_in ?? 0);
205
- if (!Number.isFinite(expIn) || expIn <= 0) {
206
- throw new GatewayAuthError("harbor-access JSON missing expires_in", { bodyText: text });
207
- }
208
- const tidRaw = body.tenant_id;
209
- if (typeof tidRaw === "string" && tidRaw.trim()) {
210
- this.tenantIdValue = tidRaw.trim();
211
- }
212
- if (!this.tenantIdValue) {
213
- throw new GatewayAuthError("harbor-access response missing tenant_id; upgrade gateway (PAYBOND-V1-008)", { bodyText: text });
214
- }
215
- this.token = access;
216
- this.notAfterMonotonic = now + Math.max(1000, expIn * 1000 - this.skewMs);
217
145
  }
218
146
  }
219
147
  /**
@@ -229,41 +157,6 @@ export class PaybondCapabilityBinding {
229
157
  this.capabilityToken = capabilityToken;
230
158
  }
231
159
  }
232
- /**
233
- * Harbor client plus gateway token lifecycle for one service account.
234
- */
235
- export class ServiceAccountHarborSession {
236
- harbor;
237
- tokens;
238
- constructor(harbor, tokens) {
239
- this.harbor = harbor;
240
- this.tokens = tokens;
241
- }
242
- /**
243
- * Build a tenant-bound {@link HarborClient} using gateway-derived tenant id and JWT supplier.
244
- */
245
- static async open(init) {
246
- const tokens = new GatewayHarborTokenProvider({
247
- gatewayBaseUrl: init.gatewayBaseUrl,
248
- apiKey: init.apiKey,
249
- harborAccessPath: init.harborAccessPath,
250
- clockSkewSeconds: init.clockSkewSeconds,
251
- });
252
- const tenant = await tokens.ensureInitial();
253
- const harbor = new HarborClient(init.harborBaseUrl, tenant, {
254
- harborBearerSupplier: () => tokens.bearer(),
255
- maxRetries: init.maxRetries ?? 3,
256
- });
257
- return new ServiceAccountHarborSession(harbor, tokens);
258
- }
259
- async rotateHarborToken() {
260
- await this.tokens.forceRotate();
261
- }
262
- /** Reserved for future HTTP client cleanup; safe to call after work completes. */
263
- async aclose() {
264
- await Promise.resolve();
265
- }
266
- }
267
160
  /**
268
161
  * HTTP client for Harbor: capability verify, intents, evidence, and tenant-scoped ledger reads
269
162
  * (`GET /ledger/v1/*`, PAYBOND-007).
@@ -613,8 +506,8 @@ export class HarborClient {
613
506
  return body;
614
507
  }
615
508
  /**
616
- * `GET /ledger/v1/events` paginated append-only history; `afterSeq` is an exclusive cursor.
617
- * `limit` is clamped to 1256 to match Harbor.
509
+ * `GET /ledger/v1/events` - protected Harbor append-only history for trusted clients.
510
+ * `afterSeq` is an exclusive cursor; `limit` is clamped to 1..256 to match Harbor.
618
511
  */
619
512
  async getLedgerEvents(options) {
620
513
  const afterSeq = Math.max(0, Math.floor(options?.afterSeq ?? 0));
@@ -657,6 +550,152 @@ export class HarborClient {
657
550
  return body;
658
551
  }
659
552
  }
553
+ /**
554
+ * Gateway-backed Harbor surface for hosted Paybond integrations.
555
+ *
556
+ * This client sends the service-account API key to the public Gateway. Gateway derives tenant,
557
+ * mints upstream Harbor credentials internally, and applies recognition/guardrail checks before
558
+ * forwarding state-changing Harbor requests.
559
+ */
560
+ export class GatewayHarborClient {
561
+ base;
562
+ tenantId;
563
+ staticGatewayBearerToken;
564
+ maxRetries;
565
+ constructor(gatewayBaseUrl, tenantId, options) {
566
+ this.base = `${normalizeBase(gatewayBaseUrl)}/`;
567
+ this.tenantId = tenantId.trim();
568
+ this.staticGatewayBearerToken = options.staticGatewayBearerToken.trim();
569
+ this.maxRetries = Math.max(1, options.maxRetries ?? 3);
570
+ }
571
+ headers(extra) {
572
+ const headers = new Headers(extra);
573
+ headers.set("accept", "application/json");
574
+ headers.set("x-tenant-id", this.tenantId);
575
+ headers.set("authorization", `Bearer ${this.staticGatewayBearerToken}`);
576
+ return headers;
577
+ }
578
+ async fetchWithRetries(url, init) {
579
+ let lastErr;
580
+ for (let attempt = 0; attempt < this.maxRetries; attempt++) {
581
+ let res;
582
+ try {
583
+ res = await fetch(url, init);
584
+ }
585
+ catch (e) {
586
+ lastErr = e;
587
+ if (attempt + 1 >= this.maxRetries)
588
+ throw e;
589
+ await new Promise((r) => setTimeout(r, backoffMs(attempt)));
590
+ continue;
591
+ }
592
+ if ([429, 500, 502, 503, 504].includes(res.status) && attempt + 1 < this.maxRetries) {
593
+ const raSec = parseRetryAfterSeconds(res.headers.get("retry-after"));
594
+ const delayMs = raSec != null ? raSec * 1000 : backoffMs(attempt);
595
+ await new Promise((r) => setTimeout(r, delayMs));
596
+ continue;
597
+ }
598
+ return res;
599
+ }
600
+ throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
601
+ }
602
+ async postJSON(path, payload, extraHeaders) {
603
+ const url = `${this.base}${path.replace(/^\/+/, "")}`;
604
+ const res = await this.fetchWithRetries(url, {
605
+ method: "POST",
606
+ headers: this.headers({
607
+ "content-type": "application/json",
608
+ ...(extraHeaders ?? {}),
609
+ }),
610
+ body: JSON.stringify(payload),
611
+ });
612
+ const text = await res.text();
613
+ return { res, text, url };
614
+ }
615
+ mutationHeaders(operation, options, headers) {
616
+ const proof = options?.recognitionProof;
617
+ if (!proof || typeof proof !== "object" || Array.isArray(proof)) {
618
+ throw new Error(`${operation} requires recognitionProof`);
619
+ }
620
+ return gatewayMutationHeaders(proof, {
621
+ ...(headers ?? {}),
622
+ ...(options?.idempotencyKey?.trim() ? { "idempotency-key": options.idempotencyKey.trim() } : {}),
623
+ });
624
+ }
625
+ async verifyCapability(input) {
626
+ const payload = {
627
+ intent_id: input.intentId,
628
+ token: input.token,
629
+ operation: input.operation,
630
+ requested_spend_cents: input.requestedSpendCents ?? 0,
631
+ };
632
+ const { res, text, url } = await this.postJSON("/verify", payload);
633
+ if (!res.ok) {
634
+ throw new HarborHttpError(`Gateway verify HTTP ${res.status}: ${text}`, {
635
+ statusCode: res.status,
636
+ url,
637
+ bodyText: text,
638
+ });
639
+ }
640
+ const body = JSON.parse(text);
641
+ if (body.tenant !== this.tenantId) {
642
+ throw new Error(`verify tenant mismatch: client=${this.tenantId} gateway=${body.tenant}`);
643
+ }
644
+ if (body.intent_id !== input.intentId) {
645
+ throw new Error(`verify intent mismatch: requested=${input.intentId} gateway=${body.intent_id}`);
646
+ }
647
+ return {
648
+ allow: body.allow,
649
+ auditId: body.audit_id,
650
+ tenant: body.tenant,
651
+ intentId: body.intent_id,
652
+ code: body.code,
653
+ message: body.message,
654
+ };
655
+ }
656
+ async createIntent(body, options) {
657
+ const { res, text, url } = await this.postJSON("/harbor/intents", body, this.mutationHeaders("createIntent", options));
658
+ if (!res.ok) {
659
+ throw new HarborHttpError(`Gateway Harbor create intent HTTP ${res.status}: ${text}`, {
660
+ statusCode: res.status,
661
+ url,
662
+ bodyText: text,
663
+ });
664
+ }
665
+ return JSON.parse(text);
666
+ }
667
+ async fundIntent(intentId, options) {
668
+ const { res, text, url } = await this.postJSON(`/harbor/intents/${encodeURIComponent(intentId)}/fund`, {}, this.mutationHeaders("fundIntent", options, {
669
+ ...(options.paymentSignature?.trim() ? { "payment-signature": options.paymentSignature.trim() } : {}),
670
+ }));
671
+ if (![200, 202, 402].includes(res.status)) {
672
+ throw new HarborHttpError(`Gateway Harbor fund intent HTTP ${res.status}: ${text}`, {
673
+ statusCode: res.status,
674
+ url,
675
+ bodyText: text,
676
+ });
677
+ }
678
+ return parseFundIntentResponse(assertJSONObject(JSON.parse(text)), {
679
+ tenantId: this.tenantId,
680
+ intentId,
681
+ statusCode: res.status,
682
+ paymentRequired: res.headers.get("payment-required") ?? undefined,
683
+ paymentResponse: res.headers.get("payment-response") ?? undefined,
684
+ source: "gateway",
685
+ });
686
+ }
687
+ async submitEvidence(intentId, evidenceBody, options) {
688
+ const { res, text, url } = await this.postJSON(`/harbor/intents/${encodeURIComponent(intentId)}/evidence`, evidenceBody, this.mutationHeaders("submitEvidence", options));
689
+ if (!res.ok) {
690
+ throw new HarborHttpError(`Gateway Harbor evidence HTTP ${res.status}: ${text}`, {
691
+ statusCode: res.status,
692
+ url,
693
+ bodyText: text,
694
+ });
695
+ }
696
+ return parseSubmitEvidenceResponse(JSON.parse(text));
697
+ }
698
+ }
660
699
  const DEFAULT_PRINCIPAL_PATH = "/v1/auth/principal";
661
700
  const SETTLEMENT_RAIL_VALUES = new Set(["stripe_connect", "x402_usdc_base"]);
662
701
  const FRAUD_REVIEW_EVENT_TYPES = new Set([
@@ -687,6 +726,53 @@ function readStringArrayValue(value, field) {
687
726
  }
688
727
  return [...value];
689
728
  }
729
+ function parseFundIntentResponse(body, init) {
730
+ const tenant = String(body.tenant ?? "");
731
+ if (tenant !== init.tenantId) {
732
+ throw new Error(`fund tenant mismatch: client=${init.tenantId} ${init.source}=${tenant}`);
733
+ }
734
+ const echoedIntentId = String(body.intent_id ?? "");
735
+ if (echoedIntentId !== init.intentId) {
736
+ throw new Error(`fund intent mismatch: requested=${init.intentId} ${init.source}=${echoedIntentId}`);
737
+ }
738
+ if (typeof body.state !== "string" || !body.state.trim()) {
739
+ throw new Error("fund response missing state");
740
+ }
741
+ if (typeof body.currency !== "string" || !body.currency.trim()) {
742
+ throw new Error("fund response missing currency");
743
+ }
744
+ const amountCents = Number(body.amount_cents);
745
+ if (!Number.isFinite(amountCents)) {
746
+ throw new Error("fund response missing amount_cents");
747
+ }
748
+ return {
749
+ statusCode: init.statusCode,
750
+ paymentRequired: init.paymentRequired,
751
+ paymentResponse: init.paymentResponse,
752
+ intentId: echoedIntentId,
753
+ tenant,
754
+ state: body.state,
755
+ settlementRail: readSettlementRailValue(body.settlement_rail, "fund settlement_rail"),
756
+ currency: body.currency,
757
+ amountCents,
758
+ funded: Boolean(body.funded),
759
+ capabilityToken: typeof body.capability_token === "string" && body.capability_token.trim()
760
+ ? body.capability_token
761
+ : undefined,
762
+ funding: body.funding === undefined || body.funding === null
763
+ ? undefined
764
+ : parseIntentFundingResult(body.funding),
765
+ };
766
+ }
767
+ function parseSubmitEvidenceResponse(value) {
768
+ const body = assertJSONObject(value);
769
+ return {
770
+ intentId: String(body.intent_id ?? ""),
771
+ tenant: String(body.tenant ?? ""),
772
+ state: String(body.state ?? ""),
773
+ predicatePassed: typeof body.predicate_passed === "boolean" ? body.predicate_passed : undefined,
774
+ };
775
+ }
690
776
  function parseIntentFundingResult(value) {
691
777
  const body = assertJSONObject(value);
692
778
  const onchainRaw = body.onchain_transaction_hashes;
@@ -1441,10 +1527,11 @@ function gatewayMutationHeaders(recognitionProof, headers) {
1441
1527
  function encodeRecognitionProofHeader(proof) {
1442
1528
  return Buffer.from(JSON.stringify(proof), "utf8").toString("base64url");
1443
1529
  }
1444
- async function resolveGatewayTenantId(gatewayBaseUrl, apiKey, principalPath, maxRetries) {
1530
+ async function resolveGatewayTenantId(gatewayBaseUrl, apiKey, principalPath, maxRetries, expectedEnvironment) {
1445
1531
  const base = normalizeBase(gatewayBaseUrl);
1446
1532
  const path = principalPath.startsWith("/") ? principalPath : `/${principalPath}`;
1447
1533
  const url = `${base}${path}`;
1534
+ const expected = normalizeExpectedEnvironment(expectedEnvironment);
1448
1535
  let lastErr;
1449
1536
  for (let attempt = 0; attempt < maxRetries; attempt++) {
1450
1537
  let res;
@@ -1485,6 +1572,7 @@ async function resolveGatewayTenantId(gatewayBaseUrl, apiKey, principalPath, max
1485
1572
  bodyText: text,
1486
1573
  });
1487
1574
  }
1575
+ assertExpectedEnvironment("gateway principal", body.environment, expected, text);
1488
1576
  return tenant;
1489
1577
  }
1490
1578
  throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
@@ -1498,8 +1586,9 @@ export class ServiceAccountSignalSession {
1498
1586
  this.signal = signal;
1499
1587
  }
1500
1588
  static async open(init) {
1501
- const tenantId = await resolveGatewayTenantId(init.gatewayBaseUrl, init.apiKey, init.principalPath ?? DEFAULT_PRINCIPAL_PATH, Math.max(1, init.maxRetries ?? 3));
1502
- return new ServiceAccountSignalSession(new GatewaySignalClient(init.gatewayBaseUrl, tenantId, {
1589
+ const gatewayBaseUrl = defaultGatewayBaseUrl(init.gatewayBaseUrl);
1590
+ const tenantId = await resolveGatewayTenantId(gatewayBaseUrl, init.apiKey, init.principalPath ?? DEFAULT_PRINCIPAL_PATH, Math.max(1, init.maxRetries ?? 3), init.expectedEnvironment);
1591
+ return new ServiceAccountSignalSession(new GatewaySignalClient(gatewayBaseUrl, tenantId, {
1503
1592
  staticGatewayBearerToken: init.apiKey,
1504
1593
  maxRetries: init.maxRetries ?? 3,
1505
1594
  }));
@@ -1517,8 +1606,9 @@ export class ServiceAccountFraudSession {
1517
1606
  this.fraud = fraud;
1518
1607
  }
1519
1608
  static async open(init) {
1520
- const tenantId = await resolveGatewayTenantId(init.gatewayBaseUrl, init.apiKey, init.principalPath ?? DEFAULT_PRINCIPAL_PATH, Math.max(1, init.maxRetries ?? 3));
1521
- return new ServiceAccountFraudSession(new GatewayFraudClient(init.gatewayBaseUrl, tenantId, {
1609
+ const gatewayBaseUrl = defaultGatewayBaseUrl(init.gatewayBaseUrl);
1610
+ const tenantId = await resolveGatewayTenantId(gatewayBaseUrl, init.apiKey, init.principalPath ?? DEFAULT_PRINCIPAL_PATH, Math.max(1, init.maxRetries ?? 3), init.expectedEnvironment);
1611
+ return new ServiceAccountFraudSession(new GatewayFraudClient(gatewayBaseUrl, tenantId, {
1522
1612
  staticGatewayBearerToken: init.apiKey,
1523
1613
  maxRetries: init.maxRetries ?? 3,
1524
1614
  }));
@@ -1527,6 +1617,9 @@ export class ServiceAccountFraudSession {
1527
1617
  await Promise.resolve();
1528
1618
  }
1529
1619
  }
1620
+ function nowRfc3339Seconds() {
1621
+ return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
1622
+ }
1530
1623
  /**
1531
1624
  * Ergonomic intent helpers: principal-signed intent create, x402 funding, and payee-signed evidence.
1532
1625
  */
@@ -1540,14 +1633,14 @@ export class PaybondIntents {
1540
1633
  * 32 bytes. `settlementRail` is signed as the requested rail; destinations stay server-owned.
1541
1634
  */
1542
1635
  async create(params) {
1543
- const { idempotencyKey, intentId: maybeIntentId, ...fields } = params;
1636
+ const { idempotencyKey, intentId: maybeIntentId, recognitionProof, ...fields } = params;
1544
1637
  const intentId = maybeIntentId ?? globalThis.crypto.randomUUID();
1545
1638
  const body = buildSignedCreateIntentBody({
1546
1639
  tenantId: this.harbor.tenantId,
1547
1640
  intentId,
1548
1641
  ...fields,
1549
1642
  });
1550
- return this.harbor.createIntent(body, { idempotencyKey });
1643
+ return this.harbor.createIntent(body, { idempotencyKey, recognitionProof });
1551
1644
  }
1552
1645
  /**
1553
1646
  * Advance Harbor `/intents/{id}/fund` for x402 / USDC-on-Base intents.
@@ -1556,22 +1649,25 @@ export class PaybondIntents {
1556
1649
  return this.harbor.fundIntent(params.intentId, {
1557
1650
  paymentSignature: params.paymentSignature,
1558
1651
  idempotencyKey: params.idempotencyKey,
1652
+ recognitionProof: params.recognitionProof,
1559
1653
  });
1560
1654
  }
1561
1655
  /**
1562
1656
  * Sign payee evidence and POST it. `payeeSigningSeed` must be 32 bytes.
1563
1657
  */
1564
1658
  async submitEvidence(params) {
1565
- const { idempotencyKey, ...rest } = params;
1659
+ const { idempotencyKey, artifactsBlake3Hex = [], submittedAtRfc3339 = nowRfc3339Seconds(), recognitionProof, ...rest } = params;
1566
1660
  const wire = signPayeeEvidenceBinding({
1567
1661
  tenantId: this.harbor.tenantId,
1662
+ artifactsBlake3Hex,
1663
+ submittedAtRfc3339,
1568
1664
  ...rest,
1569
1665
  });
1570
- return this.harbor.submitEvidence(rest.intentId, wire, { idempotencyKey });
1666
+ return this.harbor.submitEvidence(rest.intentId, wire, { idempotencyKey, recognitionProof });
1571
1667
  }
1572
1668
  }
1573
1669
  /**
1574
- * High-level Kit entrypoint: same session lifecycle as {@link ServiceAccountHarborSession}, plus {@link PaybondIntents}.
1670
+ * High-level Kit entrypoint: tenant-bound Gateway clients plus ergonomic intent helpers.
1575
1671
  */
1576
1672
  export class Paybond {
1577
1673
  harbor;
@@ -1580,43 +1676,44 @@ export class Paybond {
1580
1676
  a2a;
1581
1677
  protocol;
1582
1678
  intents;
1583
- session;
1584
- constructor(session, signal, fraud, a2a, protocol) {
1585
- this.session = session;
1586
- this.harbor = session.harbor;
1679
+ constructor(harbor, signal, fraud, a2a, protocol) {
1680
+ this.harbor = harbor;
1587
1681
  this.signal = signal;
1588
1682
  this.fraud = fraud;
1589
1683
  this.a2a = a2a;
1590
1684
  this.protocol = protocol;
1591
- this.intents = new PaybondIntents(session.harbor);
1685
+ this.intents = new PaybondIntents(harbor);
1592
1686
  }
1593
- /** Open a tenant-bound session via gateway `harbor-access` exchange. */
1687
+ /** Open a tenant-bound hosted Paybond session from a service-account API key. */
1594
1688
  static async open(init) {
1595
- const session = await ServiceAccountHarborSession.open(init);
1596
- const signal = new GatewaySignalClient(init.gatewayBaseUrl, session.harbor.tenantId, {
1689
+ const gatewayBaseUrl = defaultGatewayBaseUrl(init.gatewayBaseUrl);
1690
+ const maxRetries = Math.max(1, init.maxRetries ?? 3);
1691
+ const tenantId = await resolveGatewayTenantId(gatewayBaseUrl, init.apiKey, init.principalPath ?? DEFAULT_PRINCIPAL_PATH, maxRetries, init.expectedEnvironment);
1692
+ const harbor = new GatewayHarborClient(gatewayBaseUrl, tenantId, {
1597
1693
  staticGatewayBearerToken: init.apiKey,
1598
- maxRetries: init.maxRetries ?? 3,
1694
+ maxRetries,
1599
1695
  });
1600
- const fraud = new GatewayFraudClient(init.gatewayBaseUrl, session.harbor.tenantId, {
1696
+ const signal = new GatewaySignalClient(gatewayBaseUrl, tenantId, {
1601
1697
  staticGatewayBearerToken: init.apiKey,
1602
- maxRetries: init.maxRetries ?? 3,
1698
+ maxRetries,
1603
1699
  });
1604
- const a2a = new GatewayA2AClient(init.gatewayBaseUrl, {
1700
+ const fraud = new GatewayFraudClient(gatewayBaseUrl, tenantId, {
1605
1701
  staticGatewayBearerToken: init.apiKey,
1606
- maxRetries: init.maxRetries ?? 3,
1702
+ maxRetries,
1607
1703
  });
1608
- const protocol = new GatewayProtocolClient(init.gatewayBaseUrl, session.harbor.tenantId, {
1704
+ const a2a = new GatewayA2AClient(gatewayBaseUrl, {
1609
1705
  staticGatewayBearerToken: init.apiKey,
1610
- maxRetries: init.maxRetries ?? 3,
1706
+ maxRetries,
1611
1707
  });
1612
- return new Paybond(session, signal, fraud, a2a, protocol);
1613
- }
1614
- async rotateHarborToken() {
1615
- await this.session.rotateHarborToken();
1708
+ const protocol = new GatewayProtocolClient(gatewayBaseUrl, tenantId, {
1709
+ staticGatewayBearerToken: init.apiKey,
1710
+ maxRetries,
1711
+ });
1712
+ return new Paybond(harbor, signal, fraud, a2a, protocol);
1616
1713
  }
1617
- /** Release HTTP resources (Harbor client + gateway token provider). */
1714
+ /** Reserved for future HTTP client cleanup; safe to call after work completes. */
1618
1715
  async aclose() {
1619
- await this.session.aclose();
1716
+ await Promise.resolve();
1620
1717
  }
1621
1718
  }
1622
1719
  export { normalizeJson, jsonValueDigest } from "./json-digest.js";
@@ -24,13 +24,10 @@ type MCPCallToolResult = {
24
24
  isError?: boolean;
25
25
  };
26
26
  export type PaybondMCPSettings = {
27
- gatewayBaseUrl: string;
28
27
  apiKey: string;
29
- harborBaseUrl?: string;
30
- harborAccessPath?: string;
28
+ gatewayBaseUrl?: string;
31
29
  principalPath?: string;
32
30
  maxRetries?: number;
33
- clockSkewSeconds?: number;
34
31
  };
35
32
  export declare class PaybondMCPServer {
36
33
  private readonly runtime;
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { GatewayAuthError, GatewayFraudClient, GatewaySignalClient, HarborHttpError, ServiceAccountHarborSession, SignalHttpError, } from "./index.js";
2
+ import { GatewayAuthError, GatewayFraudClient, GatewaySignalClient, SignalHttpError, DEFAULT_PAYBOND_GATEWAY_BASE_URL, } from "./index.js";
3
3
  const SERVER_NAME = "Paybond MCP";
4
4
  const SERVER_VERSION = "0.6.0";
5
5
  const MCP_PROTOCOL_VERSION = "2025-11-25";
6
- const DEFAULT_HARBOR_ACCESS_PATH = "/v1/auth/harbor-access";
7
6
  const DEFAULT_PRINCIPAL_PATH = "/v1/auth/principal";
8
7
  const DEFAULT_RECOGNITION_VERIFIER_ID = "paybond-gateway";
9
8
  const agentRecognitionProofHeader = "x-paybond-agent-recognition-proof";
@@ -115,16 +114,12 @@ class PaybondMCPRuntime {
115
114
  principalValue = null;
116
115
  signalValue = null;
117
116
  fraudValue = null;
118
- harborValue = null;
119
117
  constructor(settings) {
120
118
  this.settings = {
121
- gatewayBaseUrl: settings.gatewayBaseUrl,
119
+ gatewayBaseUrl: settings.gatewayBaseUrl ?? DEFAULT_PAYBOND_GATEWAY_BASE_URL,
122
120
  apiKey: settings.apiKey,
123
- harborBaseUrl: settings.harborBaseUrl,
124
- harborAccessPath: settings.harborAccessPath ?? DEFAULT_HARBOR_ACCESS_PATH,
125
121
  principalPath: settings.principalPath ?? DEFAULT_PRINCIPAL_PATH,
126
122
  maxRetries: Math.max(1, settings.maxRetries ?? 3),
127
- clockSkewSeconds: Math.max(0, settings.clockSkewSeconds ?? 90),
128
123
  };
129
124
  this.gateway = new GatewayAPIClient({
130
125
  gatewayBaseUrl: this.settings.gatewayBaseUrl,
@@ -158,20 +153,6 @@ class PaybondMCPRuntime {
158
153
  }))();
159
154
  return this.fraudValue;
160
155
  }
161
- async harbor() {
162
- if (!this.settings.harborBaseUrl) {
163
- throw new Error("PAYBOND_HARBOR_URL is required for direct Harbor mutation tools");
164
- }
165
- this.harborValue ??= ServiceAccountHarborSession.open({
166
- gatewayBaseUrl: this.settings.gatewayBaseUrl,
167
- apiKey: this.settings.apiKey,
168
- harborBaseUrl: this.settings.harborBaseUrl,
169
- harborAccessPath: this.settings.harborAccessPath,
170
- clockSkewSeconds: this.settings.clockSkewSeconds,
171
- maxRetries: this.settings.maxRetries,
172
- });
173
- return this.harborValue;
174
- }
175
156
  async listIntents(init) {
176
157
  const params = new URLSearchParams({
177
158
  limit: String(Math.max(1, Math.min(intArg(init.limit ?? 20, "limit"), 200))),
@@ -319,9 +300,6 @@ export class PaybondMCPServer {
319
300
  tools;
320
301
  initialized = false;
321
302
  constructor(settings) {
322
- if (!settings.gatewayBaseUrl.trim()) {
323
- throw new Error("PAYBOND_GATEWAY_URL is required");
324
- }
325
303
  if (!settings.apiKey.trim()) {
326
304
  throw new Error("PAYBOND_API_KEY is required");
327
305
  }
@@ -701,68 +679,27 @@ export class PaybondMCPServer {
701
679
  }),
702
680
  },
703
681
  ];
704
- if (settings.harborBaseUrl?.trim()) {
705
- tools.push({
706
- name: "paybond_create_intent_legacy",
707
- description: "Legacy direct-Harbor fallback for POST /intents. Prefer paybond_create_intent unless you explicitly need PAYBOND_HARBOR_URL direct mode.",
708
- inputSchema: objectSchema({
709
- body: { type: "object", additionalProperties: true },
710
- idempotency_key: { type: "string" },
711
- }, ["body"]),
712
- call: async (args) => (await this.runtime.harbor()).harbor.createIntent(ensureObject(args.body, "body"), { idempotencyKey: optionalString(args.idempotency_key) }),
713
- }, {
714
- name: "paybond_fund_intent_legacy",
715
- description: "Legacy direct-Harbor fallback for POST /intents/{intent_id}/fund.",
716
- inputSchema: objectSchema({
717
- intent_id: { type: "string" },
718
- payment_signature: { type: "string" },
719
- idempotency_key: { type: "string" },
720
- }, ["intent_id"]),
721
- call: async (args) => jsonObjectFromValue(await (await this.runtime.harbor()).harbor.fundIntent(uuidArg(args.intent_id, "intent_id"), {
722
- paymentSignature: optionalString(args.payment_signature),
723
- idempotencyKey: optionalString(args.idempotency_key),
724
- })),
725
- }, {
726
- name: "paybond_submit_evidence_legacy",
727
- description: "Legacy direct-Harbor fallback for POST /intents/{intent_id}/evidence.",
728
- inputSchema: objectSchema({
729
- intent_id: { type: "string" },
730
- body: { type: "object", additionalProperties: true },
731
- idempotency_key: { type: "string" },
732
- }, ["intent_id", "body"]),
733
- call: async (args) => (await this.runtime.harbor()).harbor.submitEvidence(uuidArg(args.intent_id, "intent_id"), ensureObject(args.body, "body"), { idempotencyKey: optionalString(args.idempotency_key) }),
734
- });
735
- }
736
682
  return tools;
737
683
  }
738
684
  }
739
685
  export function settingsFromEnv(env = process.env) {
740
- const gatewayBaseUrl = String(env.PAYBOND_GATEWAY_URL ?? "").trim();
741
686
  const apiKey = String(env.PAYBOND_API_KEY ?? "").trim();
742
- if (!gatewayBaseUrl) {
743
- throw new Error("PAYBOND_GATEWAY_URL is required");
744
- }
745
687
  if (!apiKey) {
746
688
  throw new Error("PAYBOND_API_KEY is required");
747
689
  }
748
690
  return {
749
- gatewayBaseUrl,
691
+ gatewayBaseUrl: DEFAULT_PAYBOND_GATEWAY_BASE_URL,
750
692
  apiKey,
751
- harborBaseUrl: optionalEnv(env.PAYBOND_HARBOR_URL),
752
- harborAccessPath: optionalEnv(env.PAYBOND_HARBOR_ACCESS_PATH) ?? DEFAULT_HARBOR_ACCESS_PATH,
753
693
  principalPath: optionalEnv(env.PAYBOND_PRINCIPAL_PATH) ?? DEFAULT_PRINCIPAL_PATH,
754
694
  maxRetries: optionalEnv(env.PAYBOND_MCP_MAX_RETRIES)
755
695
  ? intArg(optionalEnv(env.PAYBOND_MCP_MAX_RETRIES), "PAYBOND_MCP_MAX_RETRIES")
756
696
  : 3,
757
- clockSkewSeconds: optionalEnv(env.PAYBOND_MCP_CLOCK_SKEW_SECONDS)
758
- ? numberArg(optionalEnv(env.PAYBOND_MCP_CLOCK_SKEW_SECONDS), "PAYBOND_MCP_CLOCK_SKEW_SECONDS")
759
- : 90,
760
697
  };
761
698
  }
762
699
  export function main(argv = process.argv.slice(2)) {
763
700
  if (argv.includes("--help")) {
764
701
  process.stderr.write("Usage: paybond-mcp-server\n\n" +
765
- "Runs the tenant-bound Paybond MCP server over stdio using PAYBOND_GATEWAY_URL and PAYBOND_API_KEY.\n");
702
+ "Runs the tenant-bound Paybond MCP server over stdio using PAYBOND_API_KEY.\n");
766
703
  return 0;
767
704
  }
768
705
  if (argv.length > 0) {
@@ -817,17 +754,6 @@ function intArg(value, field) {
817
754
  }
818
755
  return parsed;
819
756
  }
820
- function numberArg(value, field) {
821
- const parsed = typeof value === "number"
822
- ? value
823
- : typeof value === "string" && value.trim()
824
- ? Number(value)
825
- : Number.NaN;
826
- if (!Number.isFinite(parsed)) {
827
- throw new Error(`${field} must be numeric`);
828
- }
829
- return parsed;
830
- }
831
757
  function uuidArg(value, field) {
832
758
  const raw = stringArg(value, field);
833
759
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(raw)) {
@@ -909,7 +835,6 @@ function formatError(err) {
909
835
  if (err instanceof Error ||
910
836
  err instanceof GatewayAuthError ||
911
837
  err instanceof GatewayHTTPError ||
912
- err instanceof HarborHttpError ||
913
838
  err instanceof SignalHttpError) {
914
839
  return err.message;
915
840
  }
@@ -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);
@@ -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) {
@@ -99,6 +100,7 @@ export function buildSignedCreateIntentBody(params) {
99
100
  }
100
101
  const settlementRail = validateSettlementRail(params.settlementRail);
101
102
  const predicateRef = params.predicateRef ?? "";
103
+ ensureEd25519Sha512Sync();
102
104
  const msg = intentCreationSignBytesRaw({
103
105
  tenantId: params.tenantId,
104
106
  intentId: params.intentId,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@paybond/kit",
3
- "version": "0.6.0",
4
- "description": "Paybond Kit for TypeScript: tenant-bound Harbor sessions, capability verification, and signed intent/evidence flows.",
3
+ "version": "0.7.0",
4
+ "description": "Paybond Kit for TypeScript: hosted Gateway sessions, capability verification, signed intent/evidence flows, and Stripe Connect or x402 / USDC-on-Base settlement.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",