@hpp-io/x402-mcp-bridge 0.0.1 → 0.0.3
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 +125 -77
- package/bin/hpp-x402.js +5 -0
- package/bin/x402-mcp-bridge.js +0 -0
- package/dist/a2a.d.ts +70 -0
- package/dist/a2a.js +210 -0
- package/dist/a2a.js.map +1 -0
- package/dist/autoTopup.d.ts +2 -1
- package/dist/autoTopup.js.map +1 -1
- package/dist/cli/channel.d.ts +1 -0
- package/dist/cli/channel.js +323 -0
- package/dist/cli/channel.js.map +1 -0
- package/dist/cli/hpp-x402.d.ts +2 -0
- package/dist/cli/hpp-x402.js +375 -0
- package/dist/cli/hpp-x402.js.map +1 -0
- package/dist/cli/install-claude-code.d.ts +24 -0
- package/dist/cli/install-claude-code.js +50 -0
- package/dist/cli/install-claude-code.js.map +1 -0
- package/dist/cli/install-claude.d.ts +2 -20
- package/dist/cli/install-claude.js +12 -63
- package/dist/cli/install-claude.js.map +1 -1
- package/dist/cli/install-cursor.d.ts +7 -0
- package/dist/cli/install-cursor.js +26 -0
- package/dist/cli/install-cursor.js.map +1 -0
- package/dist/cli/install-mcp-host.d.ts +35 -0
- package/dist/cli/install-mcp-host.js +82 -0
- package/dist/cli/install-mcp-host.js.map +1 -0
- package/dist/cli/install-openclaw.d.ts +7 -0
- package/dist/cli/install-openclaw.js +28 -0
- package/dist/cli/install-openclaw.js.map +1 -0
- package/dist/cli/install-windsurf.d.ts +7 -0
- package/dist/cli/install-windsurf.js +37 -0
- package/dist/cli/install-windsurf.js.map +1 -0
- package/dist/cli/policy.d.ts +1 -0
- package/dist/cli/policy.js +212 -0
- package/dist/cli/policy.js.map +1 -0
- package/dist/cli/revoke.d.ts +1 -1
- package/dist/cli/revoke.js +1 -5
- package/dist/cli/revoke.js.map +1 -1
- package/dist/cli/safe.d.ts +4172 -9580
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.js +24 -6
- package/dist/cli/setup.js.map +1 -1
- package/dist/client.d.ts +25 -10
- package/dist/client.js +139 -2
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +50 -11
- package/dist/config.js +50 -10
- package/dist/config.js.map +1 -1
- package/dist/discovery.d.ts +43 -0
- package/dist/discovery.js +62 -0
- package/dist/discovery.js.map +1 -0
- package/dist/discoveryTools.d.ts +75 -0
- package/dist/discoveryTools.js +101 -0
- package/dist/discoveryTools.js.map +1 -0
- package/dist/funds/direct-balance.d.ts +27 -0
- package/dist/funds/direct-balance.js +58 -0
- package/dist/funds/direct-balance.js.map +1 -0
- package/dist/funds.d.ts +26 -0
- package/dist/funds.js +2 -0
- package/dist/funds.js.map +1 -0
- package/dist/httpX402.d.ts +45 -0
- package/dist/httpX402.js +214 -0
- package/dist/httpX402.js.map +1 -0
- package/dist/index.js +66 -20
- package/dist/index.js.map +1 -1
- package/dist/keychain.d.ts +23 -0
- package/dist/keychain.js +111 -0
- package/dist/keychain.js.map +1 -0
- package/dist/policy.d.ts +73 -0
- package/dist/policy.js +249 -0
- package/dist/policy.js.map +1 -0
- package/dist/server.d.ts +18 -1
- package/dist/server.js +120 -10
- package/dist/server.js.map +1 -1
- package/docs/policy.md +101 -0
- package/package.json +11 -8
- package/bin/hpp-x402-safe-revoke.js +0 -5
- package/bin/hpp-x402-safe-setup.js +0 -5
package/dist/index.js
CHANGED
|
@@ -10,10 +10,13 @@ import { loadConfig } from "./config.js";
|
|
|
10
10
|
import { setLogLevel, log } from "./log.js";
|
|
11
11
|
import { RawEoaSigner } from "./signers/raw-eoa.js";
|
|
12
12
|
import { AutoTopup } from "./autoTopup.js";
|
|
13
|
+
import { DirectBalance } from "./funds/direct-balance.js";
|
|
13
14
|
import { connectUpstream } from "./client.js";
|
|
14
15
|
import { startBridgeServer } from "./server.js";
|
|
16
|
+
import { DiscoveryClient } from "./discovery.js";
|
|
17
|
+
import { isKeychainURI, resolveKeychain } from "./keychain.js";
|
|
15
18
|
const PKG_NAME = "@hpp-io/x402-mcp-bridge";
|
|
16
|
-
const PKG_VERSION = "0.0.
|
|
19
|
+
const PKG_VERSION = "0.0.3"; // mirrors package.json — bump together
|
|
17
20
|
export async function runBridge(env = process.env) {
|
|
18
21
|
const cfg = loadConfig(env);
|
|
19
22
|
setLogLevel(cfg.LOG_LEVEL);
|
|
@@ -21,39 +24,82 @@ export async function runBridge(env = process.env) {
|
|
|
21
24
|
name: PKG_NAME,
|
|
22
25
|
version: PKG_VERSION,
|
|
23
26
|
chain: cfg.HPP_NETWORK,
|
|
27
|
+
walletMode: cfg.walletMode,
|
|
24
28
|
safe: cfg.SAFE_ADDRESS,
|
|
25
29
|
resourceServer: cfg.RESOURCE_SERVER_URL,
|
|
26
30
|
});
|
|
27
|
-
// ---- Signer (
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
: undefined,
|
|
36
|
-
});
|
|
37
|
-
// ---- Upstream MCP client (with x402 payment wrapper) ----------------
|
|
38
|
-
const upstream = await connectUpstream({
|
|
39
|
-
url: cfg.RESOURCE_SERVER_URL,
|
|
40
|
-
network: cfg.HPP_NETWORK,
|
|
41
|
-
signer,
|
|
42
|
-
autoTopup,
|
|
43
|
-
bridgeName: PKG_NAME,
|
|
44
|
-
bridgeVersion: PKG_VERSION,
|
|
31
|
+
// ---- Signer (raw key from env or OS keychain) -----------------------
|
|
32
|
+
const delegateKey = isKeychainURI(cfg.DELEGATE_PRIVATE_KEY)
|
|
33
|
+
? resolveKeychain(cfg.DELEGATE_PRIVATE_KEY)
|
|
34
|
+
: cfg.DELEGATE_PRIVATE_KEY;
|
|
35
|
+
const signer = new RawEoaSigner(delegateKey);
|
|
36
|
+
log.info("signer.loaded", {
|
|
37
|
+
address: signer.address,
|
|
38
|
+
source: isKeychainURI(cfg.DELEGATE_PRIVATE_KEY) ? "keychain" : "env",
|
|
45
39
|
});
|
|
40
|
+
// ---- Funds (wallet mode) --------------------------------------------
|
|
41
|
+
// Safe mode: autoTopup pulls USDC.e from the Safe within the on-chain daily
|
|
42
|
+
// cap when the delegate is short. Light mode: the delegate holds USDC.e
|
|
43
|
+
// directly; DirectBalance only checks the balance and surfaces a funding
|
|
44
|
+
// instruction when short (no Safe, no on-chain topup, no native gas).
|
|
45
|
+
const funds = cfg.walletMode === "safe"
|
|
46
|
+
? new AutoTopup(signer, cfg.SAFE_ADDRESS, cfg.ALLOWANCE_MODULE_ADDRESS, cfg.USDCE_ADDRESS, cfg.chainId, cfg.HPP_RPC_URL, {
|
|
47
|
+
headroomX: BigInt(cfg.TOPUP_HEADROOM_X),
|
|
48
|
+
fixedAmountAtomic: cfg.TOPUP_AMOUNT_ATOMIC
|
|
49
|
+
? BigInt(cfg.TOPUP_AMOUNT_ATOMIC)
|
|
50
|
+
: undefined,
|
|
51
|
+
})
|
|
52
|
+
: new DirectBalance(signer.address, cfg.USDCE_ADDRESS, cfg.chainId, cfg.HPP_RPC_URL);
|
|
53
|
+
// ---- Upstream MCP client (optional) ---------------------------------
|
|
54
|
+
// When RESOURCE_SERVER_URL is unset, run in local-tools-only mode (credit
|
|
55
|
+
// top-up / A2A) and skip the upstream connection.
|
|
56
|
+
let upstream;
|
|
57
|
+
if (cfg.RESOURCE_SERVER_URL) {
|
|
58
|
+
upstream = await connectUpstream({
|
|
59
|
+
url: cfg.RESOURCE_SERVER_URL,
|
|
60
|
+
network: cfg.HPP_NETWORK,
|
|
61
|
+
rpcUrl: cfg.HPP_RPC_URL,
|
|
62
|
+
signer,
|
|
63
|
+
funds,
|
|
64
|
+
bridgeName: PKG_NAME,
|
|
65
|
+
bridgeVersion: PKG_VERSION,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
log.info("upstream.skipped", {
|
|
70
|
+
reason: "RESOURCE_SERVER_URL unset — local tools only (x402_http_call, pay_a2a_agent)",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
46
73
|
// ---- stdio MCP server (host-facing) ---------------------------------
|
|
74
|
+
// HPP_X402_A2A_RPC_TIMEOUT_MS — per-request timeout for pay_a2a_agent's
|
|
75
|
+
// A2A JSON-RPC calls. Falls back to the in-module default when unset or
|
|
76
|
+
// not a positive integer.
|
|
77
|
+
const a2aRpcTimeoutRaw = process.env.HPP_X402_A2A_RPC_TIMEOUT_MS;
|
|
78
|
+
const a2aRpcTimeoutParsed = a2aRpcTimeoutRaw ? Number(a2aRpcTimeoutRaw) : NaN;
|
|
79
|
+
const a2aRpcTimeoutMs = Number.isFinite(a2aRpcTimeoutParsed) && a2aRpcTimeoutParsed > 0
|
|
80
|
+
? a2aRpcTimeoutParsed
|
|
81
|
+
: undefined;
|
|
82
|
+
// Curated service discovery — register hpp_discover / hpp_call when enabled.
|
|
83
|
+
const discovery = cfg.discoveryEnabled
|
|
84
|
+
? new DiscoveryClient(cfg.discoveryUrl)
|
|
85
|
+
: undefined;
|
|
86
|
+
if (discovery)
|
|
87
|
+
log.info("discovery.enabled", { url: cfg.discoveryUrl });
|
|
47
88
|
await startBridgeServer({
|
|
48
89
|
upstream,
|
|
49
90
|
name: PKG_NAME,
|
|
50
91
|
version: PKG_VERSION,
|
|
92
|
+
signer,
|
|
93
|
+
network: cfg.HPP_NETWORK,
|
|
94
|
+
funds,
|
|
95
|
+
discovery,
|
|
96
|
+
a2aRpcTimeoutMs,
|
|
51
97
|
});
|
|
52
98
|
// Graceful shutdown: when the host closes our stdin (or Claude Desktop
|
|
53
99
|
// restarts), node exits. Belt-and-suspenders: handle signals too.
|
|
54
100
|
const shutdown = async (sig) => {
|
|
55
101
|
log.info("bridge.shutdown", { signal: sig });
|
|
56
|
-
await upstream
|
|
102
|
+
await upstream?.close();
|
|
57
103
|
process.exit(0);
|
|
58
104
|
};
|
|
59
105
|
process.on("SIGINT", () => void shutdown("SIGINT"));
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAe,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type KeychainURI = `keychain://${string}`;
|
|
2
|
+
export declare function isKeychainURI(value: string): value is KeychainURI;
|
|
3
|
+
export declare function isHexKey(value: string): value is `0x${string}`;
|
|
4
|
+
/**
|
|
5
|
+
* Parse a `keychain://hpp-x402/<account>` URI into its account name.
|
|
6
|
+
* Throws on a malformed URI so the bridge fails loudly at startup
|
|
7
|
+
* instead of silently looking up the wrong slot.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseKeychainURI(uri: string): {
|
|
10
|
+
service: string;
|
|
11
|
+
account: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function buildKeychainURI(account: string): KeychainURI;
|
|
14
|
+
/**
|
|
15
|
+
* Resolve a keychain URI to the stored hex private key. Throws if the
|
|
16
|
+
* keychain entry is missing or holds a value that doesn't look like a
|
|
17
|
+
* key (catches "I stored garbage in this slot" mistakes early).
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveKeychain(uri: string): `0x${string}`;
|
|
20
|
+
export declare function setKeychain(account: string, hexKey: `0x${string}`): void;
|
|
21
|
+
export declare function deleteKeychain(account: string): boolean;
|
|
22
|
+
export declare function getKeychainURIPattern(): string;
|
|
23
|
+
export declare const KEYCHAIN_SERVICE = "hpp-x402";
|
package/dist/keychain.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OS keychain wrapper around @napi-rs/keyring.
|
|
3
|
+
*
|
|
4
|
+
* Lets users store the delegate private key in macOS Keychain / Windows
|
|
5
|
+
* Credential Vault / Linux libsecret instead of a plaintext .env. The
|
|
6
|
+
* bridge accepts `DELEGATE_PRIVATE_KEY` as either:
|
|
7
|
+
*
|
|
8
|
+
* - `0x[64 hex]` — raw key (dev / quickstart)
|
|
9
|
+
* - `keychain://hpp-x402/<account>` — opaque URI; resolved at startup
|
|
10
|
+
*
|
|
11
|
+
* The service component of the URI is fixed to `hpp-x402` for now; the
|
|
12
|
+
* account is user-chosen (e.g. `delegate-default`, `delegate-mainnet`).
|
|
13
|
+
* Keeping the service constant means a single namespace appears in the
|
|
14
|
+
* OS Keychain UI, and the CLI commands stay short.
|
|
15
|
+
*
|
|
16
|
+
* Security notes:
|
|
17
|
+
* - The plaintext key still exists in process memory after `getPassword`.
|
|
18
|
+
* Defense-in-depth means rotating the delegate quickly is cheap (it
|
|
19
|
+
* only holds today's allowance, not the Safe balance).
|
|
20
|
+
* - We never log the resolved key. Errors surface only the URI.
|
|
21
|
+
*/
|
|
22
|
+
import { Entry } from "@napi-rs/keyring";
|
|
23
|
+
const KEYCHAIN_PREFIX = "keychain://";
|
|
24
|
+
const SERVICE = "hpp-x402";
|
|
25
|
+
const HEX_KEY_RE = /^0x[0-9a-fA-F]{64}$/;
|
|
26
|
+
const ACCOUNT_RE = /^[a-zA-Z0-9._-]+$/;
|
|
27
|
+
export function isKeychainURI(value) {
|
|
28
|
+
return value.startsWith(KEYCHAIN_PREFIX);
|
|
29
|
+
}
|
|
30
|
+
export function isHexKey(value) {
|
|
31
|
+
return HEX_KEY_RE.test(value);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Parse a `keychain://hpp-x402/<account>` URI into its account name.
|
|
35
|
+
* Throws on a malformed URI so the bridge fails loudly at startup
|
|
36
|
+
* instead of silently looking up the wrong slot.
|
|
37
|
+
*/
|
|
38
|
+
export function parseKeychainURI(uri) {
|
|
39
|
+
if (!isKeychainURI(uri)) {
|
|
40
|
+
throw new Error(`not a keychain URI: ${uri}`);
|
|
41
|
+
}
|
|
42
|
+
const rest = uri.slice(KEYCHAIN_PREFIX.length);
|
|
43
|
+
const slash = rest.indexOf("/");
|
|
44
|
+
if (slash === -1) {
|
|
45
|
+
throw new Error(`keychain URI missing account: ${uri} (expected keychain://${SERVICE}/<account>)`);
|
|
46
|
+
}
|
|
47
|
+
const service = rest.slice(0, slash);
|
|
48
|
+
const account = rest.slice(slash + 1);
|
|
49
|
+
if (service !== SERVICE) {
|
|
50
|
+
throw new Error(`keychain URI service must be "${SERVICE}", got "${service}"`);
|
|
51
|
+
}
|
|
52
|
+
if (!ACCOUNT_RE.test(account)) {
|
|
53
|
+
throw new Error(`keychain URI account "${account}" must match ${ACCOUNT_RE} (no spaces, slashes, etc.)`);
|
|
54
|
+
}
|
|
55
|
+
return { service, account };
|
|
56
|
+
}
|
|
57
|
+
export function buildKeychainURI(account) {
|
|
58
|
+
if (!ACCOUNT_RE.test(account)) {
|
|
59
|
+
throw new Error(`invalid account name "${account}" — must match ${ACCOUNT_RE}`);
|
|
60
|
+
}
|
|
61
|
+
return `keychain://${SERVICE}/${account}`;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resolve a keychain URI to the stored hex private key. Throws if the
|
|
65
|
+
* keychain entry is missing or holds a value that doesn't look like a
|
|
66
|
+
* key (catches "I stored garbage in this slot" mistakes early).
|
|
67
|
+
*/
|
|
68
|
+
export function resolveKeychain(uri) {
|
|
69
|
+
const { service, account } = parseKeychainURI(uri);
|
|
70
|
+
const entry = new Entry(service, account);
|
|
71
|
+
let secret;
|
|
72
|
+
try {
|
|
73
|
+
secret = entry.getPassword();
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw new Error(`keychain read failed for ${uri}: ${err.message}\n` +
|
|
77
|
+
`Set it first with: hpp-x402-keychain set ${account}`);
|
|
78
|
+
}
|
|
79
|
+
if (!secret) {
|
|
80
|
+
throw new Error(`keychain entry not found: ${uri}\n` +
|
|
81
|
+
`Set it first with: hpp-x402-keychain set ${account}`);
|
|
82
|
+
}
|
|
83
|
+
if (!isHexKey(secret)) {
|
|
84
|
+
throw new Error(`keychain entry ${uri} does not contain a 0x + 64-hex private key`);
|
|
85
|
+
}
|
|
86
|
+
return secret;
|
|
87
|
+
}
|
|
88
|
+
export function setKeychain(account, hexKey) {
|
|
89
|
+
if (!isHexKey(hexKey)) {
|
|
90
|
+
throw new Error("expected 0x + 64 hex chars");
|
|
91
|
+
}
|
|
92
|
+
if (!ACCOUNT_RE.test(account)) {
|
|
93
|
+
throw new Error(`invalid account name "${account}"`);
|
|
94
|
+
}
|
|
95
|
+
const entry = new Entry(SERVICE, account);
|
|
96
|
+
entry.setPassword(hexKey);
|
|
97
|
+
}
|
|
98
|
+
export function deleteKeychain(account) {
|
|
99
|
+
const entry = new Entry(SERVICE, account);
|
|
100
|
+
try {
|
|
101
|
+
return entry.deletePassword();
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function getKeychainURIPattern() {
|
|
108
|
+
return `keychain://${SERVICE}/<account>`;
|
|
109
|
+
}
|
|
110
|
+
export const KEYCHAIN_SERVICE = SERVICE;
|
|
111
|
+
//# sourceMappingURL=keychain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keychain.js","sourceRoot":"","sources":["../src/keychain.ts"],"names":[],"mappings":"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"}
|
package/dist/policy.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export interface HostLimits {
|
|
2
|
+
/** Reject non-https targets. Defaults true. */
|
|
3
|
+
requireHttps?: boolean;
|
|
4
|
+
/** Max atomic units per single call. */
|
|
5
|
+
maxPerCallAtomic?: string;
|
|
6
|
+
/** Max atomic units per host per UTC day. (Ledger = follow-up; parsed now.) */
|
|
7
|
+
maxPerDayAtomic?: string;
|
|
8
|
+
/**
|
|
9
|
+
* H2 idempotency: min interval (ms) between *paid* calls to this host.
|
|
10
|
+
* A paid call within the window returns the previous result instead of
|
|
11
|
+
* paying again — blunts agent-retry / heartbeat double-fire. 0/undefined
|
|
12
|
+
* disables. GET/unpaid (non-402) calls are never throttled.
|
|
13
|
+
*/
|
|
14
|
+
cooldownMs?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface HostPolicy {
|
|
17
|
+
/** header name -> value-source string */
|
|
18
|
+
headers?: Record<string, string>;
|
|
19
|
+
limits?: HostLimits;
|
|
20
|
+
}
|
|
21
|
+
export interface PolicyDefaults {
|
|
22
|
+
/** Allow calling hosts with no explicit entry. Defaults false (deny). */
|
|
23
|
+
allowUnlisted?: boolean;
|
|
24
|
+
limits?: HostLimits;
|
|
25
|
+
}
|
|
26
|
+
export type PolicyFile = {
|
|
27
|
+
_defaults?: PolicyDefaults;
|
|
28
|
+
} & {
|
|
29
|
+
[host: string]: HostPolicy | PolicyDefaults | undefined;
|
|
30
|
+
};
|
|
31
|
+
/** Load the policy file. Missing file = empty policy (deny-by-default). */
|
|
32
|
+
export declare function loadPolicy(): PolicyFile;
|
|
33
|
+
/** Absolute path of the active policy file (for tooling/diagnostics). */
|
|
34
|
+
export declare function policyFilePath(): string;
|
|
35
|
+
/** Write the policy file (used by the `hpp-x402-policy` CLI). 0600. */
|
|
36
|
+
export declare function savePolicy(policy: PolicyFile): void;
|
|
37
|
+
/** Resolve a value-source string to its secret/value. Throws if unresolved. */
|
|
38
|
+
export declare function resolveSource(src: string): string;
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the auth headers to inject for `host`. Throws (no silent
|
|
41
|
+
* unauthenticated send) if a configured source can't be resolved.
|
|
42
|
+
*/
|
|
43
|
+
export declare function resolveCredentials(policy: PolicyFile, host: string): Record<string, string>;
|
|
44
|
+
export interface AccessDecision {
|
|
45
|
+
ok: boolean;
|
|
46
|
+
error?: string;
|
|
47
|
+
/** Effective limits (defaults merged with host) — used for amount checks. */
|
|
48
|
+
limits: HostLimits;
|
|
49
|
+
}
|
|
50
|
+
/** ③ pre-flight: host allowlist (deny-by-default) + https. */
|
|
51
|
+
export declare function checkAccess(policy: PolicyFile, url: string): AccessDecision;
|
|
52
|
+
/** ③ per-call amount cap. Returns a deny reason or null. */
|
|
53
|
+
export declare function checkAmount(limits: HostLimits, amountAtomic: bigint): string | null;
|
|
54
|
+
/**
|
|
55
|
+
* Cooldown gate for a paid call to `host`. If a paid call happened within
|
|
56
|
+
* `cooldownMs`, returns the previous result text so the caller can return it
|
|
57
|
+
* verbatim instead of paying again. `now` is injectable for tests.
|
|
58
|
+
*/
|
|
59
|
+
export declare function checkCooldown(host: string, cooldownMs: number | undefined, now?: number): {
|
|
60
|
+
throttled: boolean;
|
|
61
|
+
cachedResult?: string;
|
|
62
|
+
remainingMs?: number;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Best-effort per-host advisory lock around the paid ceremony. Returns a
|
|
66
|
+
* release fn, or null when another in-flight payment to this host holds it
|
|
67
|
+
* (caller must NOT pay — closes the cooldown's check-then-act race for
|
|
68
|
+
* concurrent calls). Stale locks (crashed process, older than LOCK_TTL_MS)
|
|
69
|
+
* are reclaimed.
|
|
70
|
+
*/
|
|
71
|
+
export declare function acquireHostLock(host: string): (() => void) | null;
|
|
72
|
+
/** Record a successful paid call to `host` (for the cooldown gate). */
|
|
73
|
+
export declare function recordPaid(host: string, resultText: string, now?: number): void;
|
package/dist/policy.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host policy — generic credential injection + spend guardrails for the
|
|
3
|
+
* HTTP x402 tool (`x402_http_call`).
|
|
4
|
+
*
|
|
5
|
+
* The proxy tool is generic (`{url, method, body}`); each x402 service has
|
|
6
|
+
* its own auth header and spend limits. We keep that service-specific part
|
|
7
|
+
* as *data* (this policy file), not code, so adding a service is one entry
|
|
8
|
+
* and no service name is ever hardcoded.
|
|
9
|
+
*
|
|
10
|
+
* File: `$HPP_X402_CRED_FILE` or `${HPP_X402_HOME|~/.hpp-x402}/policy.json`.
|
|
11
|
+
* Shape (host-keyed):
|
|
12
|
+
*
|
|
13
|
+
* {
|
|
14
|
+
* "_defaults": { "allowUnlisted": false,
|
|
15
|
+
* "limits": { "requireHttps": true, "maxPerCallAtomic": "1000000" } },
|
|
16
|
+
* "hub-stage.hpp.io": {
|
|
17
|
+
* "headers": { "X-Api-Key": "file:~/.hpphub/config.json#api_key" },
|
|
18
|
+
* "limits": { "requireHttps": true, "maxPerCallAtomic": "5000000" }
|
|
19
|
+
* }
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* Security: secrets are referenced by *value-source* (file:/env:/keychain://),
|
|
23
|
+
* resolved locally at call time, never passed through tool args (LLM-visible)
|
|
24
|
+
* and never logged.
|
|
25
|
+
*/
|
|
26
|
+
import { Entry } from "@napi-rs/keyring";
|
|
27
|
+
import { readFileSync, writeFileSync, mkdirSync, openSync, closeSync, unlinkSync, statSync } from "node:fs";
|
|
28
|
+
import { homedir } from "node:os";
|
|
29
|
+
import { resolve as pathResolve, dirname } from "node:path";
|
|
30
|
+
function expandHome(p) {
|
|
31
|
+
if (!p.startsWith("~"))
|
|
32
|
+
return p;
|
|
33
|
+
return pathResolve(homedir(), p.slice(1).replace(/^[/\\]+/, ""));
|
|
34
|
+
}
|
|
35
|
+
function policyPath() {
|
|
36
|
+
if (process.env.HPP_X402_CRED_FILE)
|
|
37
|
+
return expandHome(process.env.HPP_X402_CRED_FILE);
|
|
38
|
+
const home = process.env.HPP_X402_HOME ?? pathResolve(homedir(), ".hpp-x402");
|
|
39
|
+
return pathResolve(home, "policy.json");
|
|
40
|
+
}
|
|
41
|
+
/** Load the policy file. Missing file = empty policy (deny-by-default). */
|
|
42
|
+
export function loadPolicy() {
|
|
43
|
+
const path = policyPath();
|
|
44
|
+
try {
|
|
45
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
if (err.code === "ENOENT")
|
|
49
|
+
return {};
|
|
50
|
+
throw new Error(`x402 policy file unreadable (${path}): ${err.message}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function getHostPolicy(policy, host) {
|
|
54
|
+
if (host === "_defaults")
|
|
55
|
+
return undefined;
|
|
56
|
+
return policy[host]; // exact match only — no wildcard (C1)
|
|
57
|
+
}
|
|
58
|
+
/** Absolute path of the active policy file (for tooling/diagnostics). */
|
|
59
|
+
export function policyFilePath() {
|
|
60
|
+
return policyPath();
|
|
61
|
+
}
|
|
62
|
+
/** Write the policy file (used by the `hpp-x402-policy` CLI). 0600. */
|
|
63
|
+
export function savePolicy(policy) {
|
|
64
|
+
const path = policyPath();
|
|
65
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
66
|
+
writeFileSync(path, JSON.stringify(policy, null, 2) + "\n", { mode: 0o600 });
|
|
67
|
+
}
|
|
68
|
+
/** Resolve a value-source string to its secret/value. Throws if unresolved. */
|
|
69
|
+
export function resolveSource(src) {
|
|
70
|
+
if (src.startsWith("literal:"))
|
|
71
|
+
return src.slice("literal:".length);
|
|
72
|
+
if (src.startsWith("env:")) {
|
|
73
|
+
const v = process.env[src.slice("env:".length)];
|
|
74
|
+
if (!v)
|
|
75
|
+
throw new Error(`env var not set`);
|
|
76
|
+
return v;
|
|
77
|
+
}
|
|
78
|
+
if (src.startsWith("keychain://")) {
|
|
79
|
+
const rest = src.slice("keychain://".length);
|
|
80
|
+
const slash = rest.indexOf("/");
|
|
81
|
+
if (slash === -1)
|
|
82
|
+
throw new Error(`malformed keychain source (need keychain://svc/acct)`);
|
|
83
|
+
const secret = new Entry(rest.slice(0, slash), rest.slice(slash + 1)).getPassword();
|
|
84
|
+
if (!secret)
|
|
85
|
+
throw new Error(`keychain entry not found`);
|
|
86
|
+
return secret;
|
|
87
|
+
}
|
|
88
|
+
if (src.startsWith("file:")) {
|
|
89
|
+
const body = src.slice("file:".length);
|
|
90
|
+
const hash = body.indexOf("#");
|
|
91
|
+
if (hash === -1)
|
|
92
|
+
throw new Error(`file source needs #<field> (e.g. file:~/x.json#api_key)`);
|
|
93
|
+
const json = JSON.parse(readFileSync(expandHome(body.slice(0, hash)), "utf8"));
|
|
94
|
+
let v = json;
|
|
95
|
+
for (const k of body.slice(hash + 1).split(".")) {
|
|
96
|
+
if (v == null || typeof v !== "object") {
|
|
97
|
+
v = undefined;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
v = v[k];
|
|
101
|
+
}
|
|
102
|
+
if (typeof v !== "string" || v === "")
|
|
103
|
+
throw new Error(`file source field empty/not-a-string`);
|
|
104
|
+
return v;
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`unknown value-source scheme`);
|
|
107
|
+
}
|
|
108
|
+
/** Mask a source for error text (literal: hides its value; others are safe). */
|
|
109
|
+
function maskSource(src) {
|
|
110
|
+
return src.startsWith("literal:") ? "literal:***" : src;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Resolve the auth headers to inject for `host`. Throws (no silent
|
|
114
|
+
* unauthenticated send) if a configured source can't be resolved.
|
|
115
|
+
*/
|
|
116
|
+
export function resolveCredentials(policy, host) {
|
|
117
|
+
const hp = getHostPolicy(policy, host);
|
|
118
|
+
const out = {};
|
|
119
|
+
if (!hp?.headers)
|
|
120
|
+
return out;
|
|
121
|
+
for (const [name, source] of Object.entries(hp.headers)) {
|
|
122
|
+
try {
|
|
123
|
+
out[name] = resolveSource(source);
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
throw new Error(`credential for ${host} header "${name}" unresolved (${maskSource(source)}): ${err.message}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
/** ③ pre-flight: host allowlist (deny-by-default) + https. */
|
|
132
|
+
export function checkAccess(policy, url) {
|
|
133
|
+
let u;
|
|
134
|
+
try {
|
|
135
|
+
u = new URL(url);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return { ok: false, error: `invalid url`, limits: {} };
|
|
139
|
+
}
|
|
140
|
+
const host = u.host.toLowerCase();
|
|
141
|
+
const hp = getHostPolicy(policy, host);
|
|
142
|
+
const limits = { ...(policy._defaults?.limits ?? {}), ...(hp?.limits ?? {}) };
|
|
143
|
+
if (!hp && policy._defaults?.allowUnlisted !== true) {
|
|
144
|
+
return { ok: false, error: `host not allowlisted: ${host}`, limits };
|
|
145
|
+
}
|
|
146
|
+
const requireHttps = limits.requireHttps ?? true;
|
|
147
|
+
if (requireHttps && u.protocol !== "https:") {
|
|
148
|
+
return { ok: false, error: `https required for ${host}`, limits };
|
|
149
|
+
}
|
|
150
|
+
return { ok: true, limits };
|
|
151
|
+
}
|
|
152
|
+
/** ③ per-call amount cap. Returns a deny reason or null. */
|
|
153
|
+
export function checkAmount(limits, amountAtomic) {
|
|
154
|
+
if (limits.maxPerCallAtomic != null) {
|
|
155
|
+
const cap = BigInt(limits.maxPerCallAtomic);
|
|
156
|
+
if (amountAtomic > cap) {
|
|
157
|
+
return `amount ${amountAtomic} exceeds per-call cap ${cap}`;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// maxPerDayAtomic: enforced once the daily ledger lands (follow-up).
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
function spendStatePath() {
|
|
164
|
+
const home = process.env.HPP_X402_HOME ?? pathResolve(homedir(), ".hpp-x402");
|
|
165
|
+
return pathResolve(home, "topup-state.json");
|
|
166
|
+
}
|
|
167
|
+
function loadSpendState() {
|
|
168
|
+
try {
|
|
169
|
+
return JSON.parse(readFileSync(spendStatePath(), "utf8"));
|
|
170
|
+
}
|
|
171
|
+
catch {
|
|
172
|
+
return {};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Cooldown gate for a paid call to `host`. If a paid call happened within
|
|
177
|
+
* `cooldownMs`, returns the previous result text so the caller can return it
|
|
178
|
+
* verbatim instead of paying again. `now` is injectable for tests.
|
|
179
|
+
*/
|
|
180
|
+
export function checkCooldown(host, cooldownMs, now = Date.now()) {
|
|
181
|
+
if (!cooldownMs || cooldownMs <= 0)
|
|
182
|
+
return { throttled: false };
|
|
183
|
+
const entry = loadSpendState()[host];
|
|
184
|
+
if (!entry)
|
|
185
|
+
return { throttled: false };
|
|
186
|
+
const elapsed = now - entry.lastPaidAt;
|
|
187
|
+
if (elapsed < cooldownMs) {
|
|
188
|
+
return { throttled: true, cachedResult: entry.lastResult, remainingMs: cooldownMs - elapsed };
|
|
189
|
+
}
|
|
190
|
+
return { throttled: false };
|
|
191
|
+
}
|
|
192
|
+
const LOCK_TTL_MS = 120_000;
|
|
193
|
+
function lockPath(host) {
|
|
194
|
+
const home = process.env.HPP_X402_HOME ?? pathResolve(homedir(), ".hpp-x402");
|
|
195
|
+
return pathResolve(home, "locks", `${host.replace(/[^a-zA-Z0-9._-]/g, "_")}.lock`);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Best-effort per-host advisory lock around the paid ceremony. Returns a
|
|
199
|
+
* release fn, or null when another in-flight payment to this host holds it
|
|
200
|
+
* (caller must NOT pay — closes the cooldown's check-then-act race for
|
|
201
|
+
* concurrent calls). Stale locks (crashed process, older than LOCK_TTL_MS)
|
|
202
|
+
* are reclaimed.
|
|
203
|
+
*/
|
|
204
|
+
export function acquireHostLock(host) {
|
|
205
|
+
const p = lockPath(host);
|
|
206
|
+
try {
|
|
207
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
/* noop */
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
if (Date.now() - statSync(p).mtimeMs > LOCK_TTL_MS)
|
|
214
|
+
unlinkSync(p);
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
/* no existing lock */
|
|
218
|
+
}
|
|
219
|
+
let fd;
|
|
220
|
+
try {
|
|
221
|
+
fd = openSync(p, "wx"); // O_CREAT|O_EXCL|O_WRONLY — fails if it exists
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return null; // held by another in-flight payment
|
|
225
|
+
}
|
|
226
|
+
closeSync(fd);
|
|
227
|
+
return () => {
|
|
228
|
+
try {
|
|
229
|
+
unlinkSync(p);
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
/* noop */
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/** Record a successful paid call to `host` (for the cooldown gate). */
|
|
237
|
+
export function recordPaid(host, resultText, now = Date.now()) {
|
|
238
|
+
try {
|
|
239
|
+
const state = loadSpendState();
|
|
240
|
+
state[host] = { lastPaidAt: now, lastResult: resultText };
|
|
241
|
+
const home = process.env.HPP_X402_HOME ?? pathResolve(homedir(), ".hpp-x402");
|
|
242
|
+
mkdirSync(home, { recursive: true });
|
|
243
|
+
writeFileSync(spendStatePath(), JSON.stringify(state, null, 2), { mode: 0o600 });
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// best-effort — cooldown is a safety net, never block a paid call on it
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
//# sourceMappingURL=policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../src/policy.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
import type { UpstreamClient } from "./client.js";
|
|
2
|
+
import type { DiscoveryClient } from "./discovery.js";
|
|
3
|
+
import type { RawEoaSigner } from "./signers/raw-eoa.js";
|
|
4
|
+
import type { Funds } from "./funds.js";
|
|
5
|
+
import type { Network } from "@x402/core/types";
|
|
2
6
|
export interface BridgeServerOptions {
|
|
3
|
-
|
|
7
|
+
/** Upstream resource server. Undefined = local-tools-only mode. */
|
|
8
|
+
upstream?: UpstreamClient;
|
|
4
9
|
name: string;
|
|
5
10
|
version: string;
|
|
11
|
+
/** Delegate signer + network — used by the local `pay_a2a_agent` tool. */
|
|
12
|
+
signer: RawEoaSigner;
|
|
13
|
+
network: Network;
|
|
14
|
+
funds?: Funds;
|
|
15
|
+
/** Curated-discovery client. Present = register hpp_discover / hpp_call. */
|
|
16
|
+
discovery?: DiscoveryClient;
|
|
17
|
+
/**
|
|
18
|
+
* Per-request timeout for the A2A JSON-RPC calls in `pay_a2a_agent`.
|
|
19
|
+
* Surfaced via env `HPP_X402_A2A_RPC_TIMEOUT_MS` (set at CLI entry);
|
|
20
|
+
* defaults to {@link DEFAULT_A2A_RPC_TIMEOUT_MS} (60s) when omitted.
|
|
21
|
+
*/
|
|
22
|
+
a2aRpcTimeoutMs?: number;
|
|
6
23
|
}
|
|
7
24
|
export declare function startBridgeServer(opts: BridgeServerOptions): Promise<void>;
|