@crediolabs/policy-synth 0.1.15 → 0.1.16

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.
@@ -4,7 +4,7 @@ import { getInterpreterInfo } from '../install/get-interpreter-info.ts';
4
4
  import type { SimulationResult } from '../verify/envelope.ts';
5
5
  import { type RecordTransactionInput, type SimulatePolicyInput, type SynthesizePolicyInput, type VerifyPolicyInput } from './schemas.ts';
6
6
  export type { GetInterpreterInfoInput, InstallPolicyInput, RecordTransactionInput, RevokePolicyInput, SimulatePolicyInput, SynthesizePolicyInput, VerifyPolicyInput, } from './schemas.ts';
7
- export { ComposeUserResponsesSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MandateSpecSchema, NetworkSchema, OraclePriceFixtureSchema, OzAdapterConfigSchema, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, SimulatePolicyInputSchema, SynthesizePolicyInputSchema, ToolErrorSchema, VerifyPolicyInputSchema, } from './schemas.ts';
7
+ export { ComposeUserResponsesSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MAINNET_RPC_URL, MandateSpecSchema, NetworkSchema, OraclePriceFixtureSchema, OzAdapterConfigSchema, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_MAINNET_ADDRESS, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SimulatePolicyInputSchema, SynthesizePolicyInputSchema, TESTNET_RPC_URL, ToolErrorSchema, VerifyPolicyInputSchema, } from './schemas.ts';
8
8
  export type RunRecordTransactionInput = RecordTransactionInput;
9
9
  export type RunSynthesizePolicyInput = SynthesizePolicyInput;
10
10
  export type RunSimulatePolicyInput = SimulatePolicyInput;
@@ -59,10 +59,12 @@ export declare function runVerifyPolicy(raw: unknown): Promise<ToolResponse<true
59
59
  * separate `interpreter.install` call is needed or possible.
60
60
  *
61
61
  * Default-deny: an interpreter policy address other than the pinned
62
- * testnet interpreter is REFUSED (the smart account would delegate to
63
- * an interpreter the caller controls); the same applies to a non-pinned
64
- * RPC URL (the auth nonce the wallet signs comes from the RPC). Both
65
- * gates accept an explicit opt-in flag. */
62
+ * interpreter for the selected network is REFUSED (the smart account
63
+ * would delegate to an interpreter the caller controls); the same
64
+ * applies to a non-pinned RPC URL (the auth nonce the wallet signs
65
+ * comes from the RPC). Both gates accept an explicit opt-in flag.
66
+ * Pin selection follows `input.network` (defaults to `testnet` so the
67
+ * pre-mainnet callers keep working unchanged). */
66
68
  export declare function runInstallPolicy(raw: unknown): Promise<ToolResponse<BuildInstallPolicyResult>>;
67
69
  /** `revoke_policy` body - thin wrapper over `buildRevokePolicyXdr`.
68
70
  * Emits an unsigned XDR for `account.remove_context_rule(ruleId)`; the
@@ -71,14 +73,23 @@ export declare function runInstallPolicy(raw: unknown): Promise<ToolResponse<Bui
71
73
  *
72
74
  * Same RPC pin as install: a non-pinned `rpcUrl` is refused unless
73
75
  * `allowUnpinnedRpcUrl: true`. Revoke does not carry an interpreter
74
- * policy payload, so the interpreter pin is not re-checked here. */
76
+ * policy payload, so the interpreter pin is not re-checked here.
77
+ * Pin selection follows `input.network` (defaults to `testnet`). */
75
78
  export declare function runRevokePolicy(raw: unknown): Promise<ToolResponse<BuildRevokePolicyResult>>;
76
79
  /** `get_interpreter_info` body - thin wrapper over `getInterpreterInfo`.
77
80
  * Returns the pinned deployment fingerprint + an optional live
78
81
  * `grammar_version()` comparison. The audit field is deliberately
79
82
  * OMITTED (phase-04's "audit #44" has no source of truth in the repo -
80
83
  * fabricating it would be a lie on a security surface; the live
81
- * mismatch check is worth MORE). */
84
+ * mismatch check is worth MORE).
85
+ *
86
+ * Network-aware: `input.network` selects which interpreter pin and RPC
87
+ * to use. Mainnet was rolled out 2026-08-04 - the same wasm hash was
88
+ * uploaded to mainnet as was exercised on testnet, so a single
89
+ * `PINNED_INTERPRETER_WASM_SHA256` constant backs both networks
90
+ * (DEPLOYMENTS.md:25-27). The address differs because instance ids are
91
+ * network-scoped. UNAUDITED at the time of writing - see
92
+ * DEPLOYMENTS.md:29-32 for what is still pending. */
82
93
  export declare function runGetInterpreterInfo(raw: unknown): Promise<ToolResponse<ReturnType<typeof getInterpreterInfo>>>;
83
94
  /** Build a canonical ToolError for a thrown exception caught by the tool
84
95
  * envelope. The MCP SDK stringifies thrown objects as "[object Object]" by
package/dist/run/index.js CHANGED
@@ -22,12 +22,12 @@ import { placeholderOzConfig, recordTransaction, synthesizeFromMandate, synthesi
22
22
  import { buildInstallPolicyXdr, buildRevokePolicyXdr, rpcClientFromServer, } from "../install/build-install-policy.js";
23
23
  import { getInterpreterInfo } from "../install/get-interpreter-info.js";
24
24
  import { simulatePolicy, verifyPolicy } from "../verify/index.js";
25
- import { GetInterpreterInfoInputSchema, InstallPolicyInputSchema, NETWORK_PASSPHRASES, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, RecordTransactionInputSchema, RevokePolicyInputSchema, SimulatePolicyInputSchema, SynthesizePolicyInputSchema, TESTNET_RPC_URL, VerifyPolicyInputSchema, } from "./schemas.js";
25
+ import { GetInterpreterInfoInputSchema, InstallPolicyInputSchema, NETWORK_PASSPHRASES, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_WASM_SHA256, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SimulatePolicyInputSchema, SynthesizePolicyInputSchema, VerifyPolicyInputSchema, } from "./schemas.js";
26
26
  // Re-export the underlying Zod schemas so the MCP package (and any other
27
27
  // downstream consumer) can import the canonical input shapes from the same
28
28
  // module that owns the tool-body glue. The strict schemas are the source of
29
29
  // truth - MCP tool shapes are derived from them.
30
- export { ComposeUserResponsesSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MandateSpecSchema, NetworkSchema, OraclePriceFixtureSchema, OzAdapterConfigSchema, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, SimulatePolicyInputSchema, SynthesizePolicyInputSchema, ToolErrorSchema, VerifyPolicyInputSchema, } from "./schemas.js";
30
+ export { ComposeUserResponsesSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MAINNET_RPC_URL, MandateSpecSchema, NetworkSchema, OraclePriceFixtureSchema, OzAdapterConfigSchema, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_MAINNET_ADDRESS, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SimulatePolicyInputSchema, SynthesizePolicyInputSchema, TESTNET_RPC_URL, ToolErrorSchema, VerifyPolicyInputSchema, } from "./schemas.js";
31
31
  /** `record_transaction` body - wraps `recordTransaction`. The tool input
32
32
  * matches the core RecordInput minus the injected `fetcher` (the transport
33
33
  * layer does not own the RPC). Returns the core ToolResponse unchanged.
@@ -199,10 +199,12 @@ export async function runVerifyPolicy(raw) {
199
199
  * separate `interpreter.install` call is needed or possible.
200
200
  *
201
201
  * Default-deny: an interpreter policy address other than the pinned
202
- * testnet interpreter is REFUSED (the smart account would delegate to
203
- * an interpreter the caller controls); the same applies to a non-pinned
204
- * RPC URL (the auth nonce the wallet signs comes from the RPC). Both
205
- * gates accept an explicit opt-in flag. */
202
+ * interpreter for the selected network is REFUSED (the smart account
203
+ * would delegate to an interpreter the caller controls); the same
204
+ * applies to a non-pinned RPC URL (the auth nonce the wallet signs
205
+ * comes from the RPC). Both gates accept an explicit opt-in flag.
206
+ * Pin selection follows `input.network` (defaults to `testnet` so the
207
+ * pre-mainnet callers keep working unchanged). */
206
208
  export async function runInstallPolicy(raw) {
207
209
  const parsed = InstallPolicyInputSchema.safeParse(raw);
208
210
  if (!parsed.success) {
@@ -212,17 +214,20 @@ export async function runInstallPolicy(raw) {
212
214
  };
213
215
  }
214
216
  const input = parsed.data;
217
+ const network = input.network ?? 'testnet';
215
218
  // ---- Pinning gates (default-deny) ----
216
- const pinningError = enforceInterpreterPin(input.rule.policies, input.allowUnpinnedInterpreter);
219
+ const expectedInterpreter = PINNED_INTERPRETER_ADDRESS_BY_NETWORK[network];
220
+ const expectedRpc = RPC_URL_BY_NETWORK[network];
221
+ const pinningError = enforceInterpreterPin(input.rule.policies, input.allowUnpinnedInterpreter, expectedInterpreter);
217
222
  if (pinningError) {
218
223
  return { ok: false, error: pinningError };
219
224
  }
220
- if (input.rpcUrl && input.rpcUrl !== TESTNET_RPC_URL && input.allowUnpinnedRpcUrl !== true) {
225
+ if (input.rpcUrl && input.rpcUrl !== expectedRpc && input.allowUnpinnedRpcUrl !== true) {
221
226
  return {
222
227
  ok: false,
223
228
  error: {
224
229
  code: 'INSTALL_BUILD_FAILED',
225
- message: `install_policy: rpcUrl must equal the pinned TESTNET_RPC_URL; set allowUnpinnedRpcUrl: true to opt in to a custom endpoint`,
230
+ message: `install_policy: rpcUrl must equal the pinned ${expectedRpc} (${network}); set allowUnpinnedRpcUrl: true to opt in to a custom endpoint`,
226
231
  severity: 'error',
227
232
  retryable: false,
228
233
  remediation: { toolCall: { name: 'install_policy', args: {} } },
@@ -231,7 +236,7 @@ export async function runInstallPolicy(raw) {
231
236
  }
232
237
  let rpcClient;
233
238
  try {
234
- rpcClient = buildRpcClientFromInput(input.rpcUrl);
239
+ rpcClient = buildRpcClientFromInput(input.rpcUrl, network);
235
240
  }
236
241
  catch (e) {
237
242
  return {
@@ -248,7 +253,7 @@ export async function runInstallPolicy(raw) {
248
253
  const result = await buildInstallPolicyXdr({
249
254
  smartAccount: input.smartAccount,
250
255
  sourceAccount: input.sourceAccount,
251
- networkPassphrase: NETWORK_PASSPHRASES.testnet,
256
+ networkPassphrase: NETWORK_PASSPHRASES[network],
252
257
  rule: input.rule,
253
258
  installNonce: input.installNonce,
254
259
  encodedPredicate,
@@ -272,7 +277,8 @@ export async function runInstallPolicy(raw) {
272
277
  *
273
278
  * Same RPC pin as install: a non-pinned `rpcUrl` is refused unless
274
279
  * `allowUnpinnedRpcUrl: true`. Revoke does not carry an interpreter
275
- * policy payload, so the interpreter pin is not re-checked here. */
280
+ * policy payload, so the interpreter pin is not re-checked here.
281
+ * Pin selection follows `input.network` (defaults to `testnet`). */
276
282
  export async function runRevokePolicy(raw) {
277
283
  const parsed = RevokePolicyInputSchema.safeParse(raw);
278
284
  if (!parsed.success) {
@@ -282,12 +288,14 @@ export async function runRevokePolicy(raw) {
282
288
  };
283
289
  }
284
290
  const input = parsed.data;
285
- if (input.rpcUrl && input.rpcUrl !== TESTNET_RPC_URL && input.allowUnpinnedRpcUrl !== true) {
291
+ const network = input.network ?? 'testnet';
292
+ const expectedRpc = RPC_URL_BY_NETWORK[network];
293
+ if (input.rpcUrl && input.rpcUrl !== expectedRpc && input.allowUnpinnedRpcUrl !== true) {
286
294
  return {
287
295
  ok: false,
288
296
  error: {
289
297
  code: 'REVOKE_BUILD_FAILED',
290
- message: `revoke_policy: rpcUrl must equal the pinned TESTNET_RPC_URL; set allowUnpinnedRpcUrl: true to opt in to a custom endpoint`,
298
+ message: `revoke_policy: rpcUrl must equal the pinned ${expectedRpc} (${network}); set allowUnpinnedRpcUrl: true to opt in to a custom endpoint`,
291
299
  severity: 'error',
292
300
  retryable: false,
293
301
  remediation: { toolCall: { name: 'revoke_policy', args: {} } },
@@ -296,7 +304,7 @@ export async function runRevokePolicy(raw) {
296
304
  }
297
305
  let rpcClient;
298
306
  try {
299
- rpcClient = buildRpcClientFromInput(input.rpcUrl);
307
+ rpcClient = buildRpcClientFromInput(input.rpcUrl, network);
300
308
  }
301
309
  catch (e) {
302
310
  return {
@@ -309,7 +317,7 @@ export async function runRevokePolicy(raw) {
309
317
  smartAccount: input.smartAccount,
310
318
  sourceAccount: input.sourceAccount,
311
319
  ruleId: input.ruleId,
312
- networkPassphrase: NETWORK_PASSPHRASES.testnet,
320
+ networkPassphrase: NETWORK_PASSPHRASES[network],
313
321
  rpc: rpcClient,
314
322
  ...(input.baseFee !== undefined ? { baseFee: input.baseFee } : {}),
315
323
  });
@@ -327,7 +335,15 @@ export async function runRevokePolicy(raw) {
327
335
  * `grammar_version()` comparison. The audit field is deliberately
328
336
  * OMITTED (phase-04's "audit #44" has no source of truth in the repo -
329
337
  * fabricating it would be a lie on a security surface; the live
330
- * mismatch check is worth MORE). */
338
+ * mismatch check is worth MORE).
339
+ *
340
+ * Network-aware: `input.network` selects which interpreter pin and RPC
341
+ * to use. Mainnet was rolled out 2026-08-04 - the same wasm hash was
342
+ * uploaded to mainnet as was exercised on testnet, so a single
343
+ * `PINNED_INTERPRETER_WASM_SHA256` constant backs both networks
344
+ * (DEPLOYMENTS.md:25-27). The address differs because instance ids are
345
+ * network-scoped. UNAUDITED at the time of writing - see
346
+ * DEPLOYMENTS.md:29-32 for what is still pending. */
331
347
  export async function runGetInterpreterInfo(raw) {
332
348
  const parsed = GetInterpreterInfoInputSchema.safeParse(raw);
333
349
  if (!parsed.success) {
@@ -338,34 +354,15 @@ export async function runGetInterpreterInfo(raw) {
338
354
  }
339
355
  const input = parsed.data;
340
356
  const network = input.network ?? 'testnet';
341
- // Asking about mainnet used to return the TESTNET pin with `network:
342
- // 'mainnet'` stamped on it. DEPLOYMENTS.md:3 says plainly "Testnet only.
343
- // Nothing is deployed to mainnet yet", so that answer described a contract
344
- // that does not exist at an address that is not on that network. This tool
345
- // exists to tell a caller what they are installing against; inventing a
346
- // mainnet deployment is the same failure as inventing an audit reference.
347
- if (network === 'mainnet') {
348
- return {
349
- ok: false,
350
- error: {
351
- code: 'RECORDING_FAILED',
352
- message: 'get_interpreter_info: no interpreter is deployed to mainnet. The pinned address and wasm hash are testnet-only; mainnet deployment is gated on the interpreter audit.',
353
- severity: 'error',
354
- retryable: false,
355
- remediation: {
356
- toolCall: { name: 'get_interpreter_info', args: { network: 'testnet' } },
357
- },
358
- },
359
- };
360
- }
357
+ const pinnedAddress = PINNED_INTERPRETER_ADDRESS_BY_NETWORK[network];
361
358
  try {
362
359
  let deployedGrammarVersion;
363
360
  if (input.verifyLive === true) {
364
- const rpcClient = buildRpcClientFromInput(input.rpcUrl);
365
- deployedGrammarVersion = await rpcClient.getContractVersion(PINNED_INTERPRETER_TESTNET_ADDRESS);
361
+ const rpcClient = buildRpcClientFromInput(input.rpcUrl, network);
362
+ deployedGrammarVersion = await rpcClient.getContractVersion(pinnedAddress);
366
363
  }
367
364
  const info = getInterpreterInfo({
368
- pinnedAddress: PINNED_INTERPRETER_TESTNET_ADDRESS,
365
+ pinnedAddress,
369
366
  pinnedGrammarVersion: PINNED_INTERPRETER_GRAMMAR_VERSION,
370
367
  pinnedWasmHash: PINNED_INTERPRETER_WASM_SHA256,
371
368
  network,
@@ -380,41 +377,40 @@ export async function runGetInterpreterInfo(raw) {
380
377
  };
381
378
  }
382
379
  }
383
- /** Build an InstallRpcClient from an optional URL override, falling back
384
- * to the public testnet RPC (matching the design-decision rule that we
385
- * reuse `record/rpc.ts` and never hard-code a different RPC client). */
386
- function buildRpcClientFromInput(urlOverride) {
387
- if (urlOverride) {
388
- return rpcClientFromServer(new rpc.Server(urlOverride, { allowHttp: false }), NETWORK_PASSPHRASES.testnet);
389
- }
390
- // Default: public testnet RPC. Matches the brief; the mainnet pin would
391
- // need a separate deploy and is out of scope for this tool.
392
- //
380
+ /** Build an InstallRpcClient from an optional URL override + selected
381
+ * network, falling back to the pinned RPC for the network. The caller
382
+ * has already been gated against the pinned URL elsewhere, so the
383
+ * fallback here only ever picks from a finite, audited pair. */
384
+ function buildRpcClientFromInput(urlOverride, network) {
385
+ const url = urlOverride ?? RPC_URL_BY_NETWORK[network];
386
+ const passphrase = NETWORK_PASSPHRASES[network];
393
387
  // NOT `createRpcServer` - that returns an RpcFetcher, a bare
394
388
  // `(hash) => Promise<SorobanTxResponse|null>` for the RECORDER. Passing it
395
389
  // here produced a client whose `getAccount` was undefined, so every live
396
390
  // call died with "server.getAccount is not a function". The install path
397
391
  // needs the full Server surface.
398
- return rpcClientFromServer(new rpc.Server(TESTNET_RPC_URL, { allowHttp: false }), NETWORK_PASSPHRASES.testnet);
392
+ return rpcClientFromServer(new rpc.Server(url, { allowHttp: false }), passphrase);
399
393
  }
400
394
  /** Default-deny: refuse any interpreter policy whose address differs from
401
- * the pinned testnet interpreter. An interpreter the caller controls
402
- * can permit anything, so the smart account's authorization must bind
403
- * to the pinned contract unless the caller explicitly opts in via
404
- * `allowUnpinnedInterpreter`. OZ built-in policies are not interpreters
405
- * and pass through unchanged. Returns a ToolError to surface through
406
- * the run-layer envelope, or null when the policies are all pinned. */
407
- function enforceInterpreterPin(policies, allowUnpinned) {
395
+ * the pinned interpreter for the selected network. An interpreter the
396
+ * caller controls can permit anything, so the smart account's
397
+ * authorization must bind to the pinned contract unless the caller
398
+ * explicitly opts in via `allowUnpinnedInterpreter`. OZ built-in
399
+ * policies are not interpreters and pass through unchanged. Returns a
400
+ * ToolError to surface through the run-layer envelope, or null when the
401
+ * policies are all pinned. The caller resolves the expected pin per
402
+ * network; this function stays pure so it is easy to test. */
403
+ function enforceInterpreterPin(policies, allowUnpinned, expectedInterpreterAddress) {
408
404
  for (const p of policies) {
409
405
  if (p.kind !== 'interpreter')
410
406
  continue;
411
- if (p.interpreterAddress === PINNED_INTERPRETER_TESTNET_ADDRESS)
407
+ if (p.interpreterAddress === expectedInterpreterAddress)
412
408
  continue;
413
409
  if (allowUnpinned === true)
414
410
  continue;
415
411
  return {
416
412
  code: 'INSTALL_BUILD_FAILED',
417
- message: `install_policy: interpreter policy address ${p.interpreterAddress} != pinned ${PINNED_INTERPRETER_TESTNET_ADDRESS}; set allowUnpinnedInterpreter: true to opt in to a non-pinned interpreter`,
413
+ message: `install_policy: interpreter policy address ${p.interpreterAddress} != pinned ${expectedInterpreterAddress}; set allowUnpinnedInterpreter: true to opt in to a non-pinned interpreter`,
418
414
  severity: 'error',
419
415
  retryable: false,
420
416
  remediation: { toolCall: { name: 'install_policy', args: {} } },
@@ -2641,9 +2641,16 @@ export declare const VerifyPolicyInputSchema: z.ZodObject<{
2641
2641
  }> | undefined;
2642
2642
  }>;
2643
2643
  export type VerifyPolicyInput = z.infer<typeof VerifyPolicyInputSchema>;
2644
- /** Pinned interpreter address (testnet). Mirrors DEPLOYMENTS.md:156-157.
2644
+ /** Pinned interpreter address (testnet). Mirrors DEPLOYMENTS.md:128.
2645
2645
  * Single source for the MCP layer; do not embed elsewhere. */
2646
2646
  export declare const PINNED_INTERPRETER_TESTNET_ADDRESS = "CDR4NLV22STCXFGZPNKDQTEANWLF7LZ6AJLY6B7CLJXKHDZGYJWIOKGP";
2647
+ /** Pinned interpreter address (mainnet). Mirrors DEPLOYMENTS.md:18. Mainnet
2648
+ * has now been deployed (2026-08-04); the mainnet interpreter IS the binary
2649
+ * that was exercised on testnet (same wasm sha256, see
2650
+ * PINNED_INTERPRETER_WASM_SHA256). The address differs because instance
2651
+ * ids are network-scoped. UNAUDITED at the time of writing - see
2652
+ * DEPLOYMENTS.md:29-32 for the recorded decision and what is still pending. */
2653
+ export declare const PINNED_INTERPRETER_MAINNET_ADDRESS = "CALZAMUPREIRY4TULBEXIK77AUTOEJG63XLCPUWEHHQDOVK6ZVVS7VQ2";
2647
2654
  /** Pinned interpreter wasm sha256 (hex). Mirrors DEPLOYMENTS.md:156-157. */
2648
2655
  export declare const PINNED_INTERPRETER_WASM_SHA256 = "6e6c13d93e197aa380303a42cd120f5ddb080dd36ef2a343ee1dbd04ca52a443";
2649
2656
  /** The grammar version the interpreter enforces (matches SELF_VERSION in
@@ -2654,6 +2661,17 @@ export declare const PINNED_INTERPRETER_GRAMMAR_VERSION = 1;
2654
2661
  * than a Server; the two are deliberately different surfaces, so this is
2655
2662
  * pinned beside the addresses it is used with rather than shared. */
2656
2663
  export declare const TESTNET_RPC_URL = "https://soroban-testnet.stellar.org";
2664
+ /** Mainnet Soroban RPC. Pinned beside MAINNET counterparts so the install
2665
+ * and get_interpreter_info gates compare against the matching network's
2666
+ * RPC rather than always testnet. Public mainnet endpoint, the same one
2667
+ * the deploy script hit during the 2026-08-04 mainnet rollout. */
2668
+ export declare const MAINNET_RPC_URL = "https://mainnet.sorobanrpc.com";
2669
+ /** Pin + RPC lookup for the gate enforcement. The interpreters' wasm sha256
2670
+ * is identical across both networks (the same binary was uploaded both
2671
+ * places; DEPLOYMENTS.md:25-27), so `PINNED_INTERPRETER_WASM_SHA256` stays
2672
+ * a single constant - only the addresses and RPCs are network-scoped. */
2673
+ export declare const PINNED_INTERPRETER_ADDRESS_BY_NETWORK: Record<Network, string>;
2674
+ export declare const RPC_URL_BY_NETWORK: Record<Network, string>;
2657
2675
  /** Soroban `valid_until` window (ledgers) added to the latest ledger when
2658
2676
  * building the auth entry. ~25 minutes at 5s/ledger. */
2659
2677
  export declare const DEFAULT_AUTH_VALID_UNTIL_LEDGERS = 300;
@@ -2667,6 +2685,14 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
2667
2685
  /** The signer that authorises the install (G... wallet). Used only for
2668
2686
  * sequence number + auth nonce simulation; never persisted, never signed. */
2669
2687
  sourceAccount: z.ZodString;
2688
+ /** Target network for the install. Selects which interpreter pin and
2689
+ * which RPC URL are valid by default. Defaults to `testnet` so the
2690
+ * pre-mainnet callers keep working: they were always pointing at
2691
+ * the testnet pin, so defaulting testnet here is the conservative
2692
+ * continuation, NOT a silent fall-through that bypasses the gate.
2693
+ * A caller that targets mainnet MUST set this to `mainnet` (the
2694
+ * pin and RPC pin do not move by themselves). */
2695
+ network: z.ZodOptional<z.ZodEnum<["mainnet", "testnet"]>>;
2670
2696
  /** The proposed rule draft. Mirrors the core `ContextRuleDraft` shape. */
2671
2697
  rule: z.ZodEffects<z.ZodObject<{
2672
2698
  contextRuleType: z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
@@ -3121,18 +3147,22 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
3121
3147
  }, z.ZodTypeAny, "passthrough">>;
3122
3148
  /** Per-rule install nonce; 1 for a fresh install. */
3123
3149
  installNonce: z.ZodNumber;
3124
- /** Optional RPC URL override. Defaults to the public testnet endpoint
3125
- * (`TESTNET_RPC_URL`); the override is refused unless
3150
+ /** Optional RPC URL override. Defaults to the pinned RPC for the
3151
+ * selected `network` (testnet by default, mainnet when
3152
+ * `network: 'mainnet'`); the override is refused unless
3126
3153
  * `allowUnpinnedRpcUrl: true`, because the auth nonce and
3127
3154
  * rootInvocation in the response come from whichever RPC answered. */
3128
3155
  rpcUrl: z.ZodOptional<z.ZodString>;
3129
3156
  /** Opt-in to using a non-pinned RPC URL. Without this flag the tool
3130
- * refuses any `rpcUrl` other than `TESTNET_RPC_URL` because the
3131
- * caller's auth-digest binds to whatever the RPC returned. */
3157
+ * refuses any `rpcUrl` other than the pinned RPC for the selected
3158
+ * network because the caller's auth-digest binds to whatever the
3159
+ * RPC returned. */
3132
3160
  allowUnpinnedRpcUrl: z.ZodOptional<z.ZodBoolean>;
3133
3161
  /** Opt-in to pointing the rule's interpreter policy at any address
3134
- * other than the pinned testnet interpreter. Default-deny: a caller
3135
- * that controls the interpreter can permit everything. */
3162
+ * other than the pinned interpreter for the selected network.
3163
+ * Default-deny: a caller that controls the interpreter can permit
3164
+ * everything. Selecting `network: 'mainnet'` is NOT an opt-in -
3165
+ * the mainnet pin is its own deny-by-default anchor. */
3136
3166
  allowUnpinnedInterpreter: z.ZodOptional<z.ZodBoolean>;
3137
3167
  /** Base fee in stroops; defaults to BASE_FEE (100). */
3138
3168
  baseFee: z.ZodOptional<z.ZodNumber>;
@@ -3175,6 +3205,7 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
3175
3205
  } & {
3176
3206
  [k: string]: unknown;
3177
3207
  };
3208
+ network?: "mainnet" | "testnet" | undefined;
3178
3209
  rpcUrl?: string | undefined;
3179
3210
  allowUnpinnedRpcUrl?: boolean | undefined;
3180
3211
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -3218,6 +3249,7 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
3218
3249
  } & {
3219
3250
  [k: string]: unknown;
3220
3251
  };
3252
+ network?: "mainnet" | "testnet" | undefined;
3221
3253
  rpcUrl?: string | undefined;
3222
3254
  allowUnpinnedRpcUrl?: boolean | undefined;
3223
3255
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -3261,6 +3293,7 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
3261
3293
  } & {
3262
3294
  [k: string]: unknown;
3263
3295
  };
3296
+ network?: "mainnet" | "testnet" | undefined;
3264
3297
  rpcUrl?: string | undefined;
3265
3298
  allowUnpinnedRpcUrl?: boolean | undefined;
3266
3299
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -3304,6 +3337,7 @@ export declare const InstallPolicyInputSchema: z.ZodEffects<z.ZodObject<{
3304
3337
  } & {
3305
3338
  [k: string]: unknown;
3306
3339
  };
3340
+ network?: "mainnet" | "testnet" | undefined;
3307
3341
  rpcUrl?: string | undefined;
3308
3342
  allowUnpinnedRpcUrl?: boolean | undefined;
3309
3343
  allowUnpinnedInterpreter?: boolean | undefined;
@@ -3318,9 +3352,12 @@ export declare const RevokePolicyInputSchema: z.ZodEffects<z.ZodObject<{
3318
3352
  * verify, since the account's source is not in this repo. Proven on
3319
3353
  * testnet: the account's deployer can revoke. */
3320
3354
  sourceAccount: z.ZodString;
3355
+ /** Target network for the revoke. Same `testnet`-default as install,
3356
+ * so pre-mainnet callers keep working without an explicit flag. */
3357
+ network: z.ZodOptional<z.ZodEnum<["mainnet", "testnet"]>>;
3321
3358
  /** The rule id to remove from the smart account + interpreter. */
3322
3359
  ruleId: z.ZodNumber;
3323
- /** Optional RPC URL override; same pin as install. */
3360
+ /** Optional RPC URL override; same per-network pin as install. */
3324
3361
  rpcUrl: z.ZodOptional<z.ZodString>;
3325
3362
  /** Opt-in to using a non-pinned RPC URL. Same semantics as install. */
3326
3363
  allowUnpinnedRpcUrl: z.ZodOptional<z.ZodBoolean>;
@@ -3330,6 +3367,7 @@ export declare const RevokePolicyInputSchema: z.ZodEffects<z.ZodObject<{
3330
3367
  smartAccount: string;
3331
3368
  sourceAccount: string;
3332
3369
  ruleId: number;
3370
+ network?: "mainnet" | "testnet" | undefined;
3333
3371
  rpcUrl?: string | undefined;
3334
3372
  allowUnpinnedRpcUrl?: boolean | undefined;
3335
3373
  baseFee?: number | undefined;
@@ -3337,6 +3375,7 @@ export declare const RevokePolicyInputSchema: z.ZodEffects<z.ZodObject<{
3337
3375
  smartAccount: string;
3338
3376
  sourceAccount: string;
3339
3377
  ruleId: number;
3378
+ network?: "mainnet" | "testnet" | undefined;
3340
3379
  rpcUrl?: string | undefined;
3341
3380
  allowUnpinnedRpcUrl?: boolean | undefined;
3342
3381
  baseFee?: number | undefined;
@@ -3344,6 +3383,7 @@ export declare const RevokePolicyInputSchema: z.ZodEffects<z.ZodObject<{
3344
3383
  smartAccount: string;
3345
3384
  sourceAccount: string;
3346
3385
  ruleId: number;
3386
+ network?: "mainnet" | "testnet" | undefined;
3347
3387
  rpcUrl?: string | undefined;
3348
3388
  allowUnpinnedRpcUrl?: boolean | undefined;
3349
3389
  baseFee?: number | undefined;
@@ -3351,6 +3391,7 @@ export declare const RevokePolicyInputSchema: z.ZodEffects<z.ZodObject<{
3351
3391
  smartAccount: string;
3352
3392
  sourceAccount: string;
3353
3393
  ruleId: number;
3394
+ network?: "mainnet" | "testnet" | undefined;
3354
3395
  rpcUrl?: string | undefined;
3355
3396
  allowUnpinnedRpcUrl?: boolean | undefined;
3356
3397
  baseFee?: number | undefined;
@@ -424,9 +424,16 @@ const MandateSpecSchemaForRule = z
424
424
  // install/revoke/get_info schemas live HERE (after the helper) so the const
425
425
  // reference resolves at module init. They are the new tools on top of the
426
426
  // existing four.
427
- /** Pinned interpreter address (testnet). Mirrors DEPLOYMENTS.md:156-157.
427
+ /** Pinned interpreter address (testnet). Mirrors DEPLOYMENTS.md:128.
428
428
  * Single source for the MCP layer; do not embed elsewhere. */
429
429
  export const PINNED_INTERPRETER_TESTNET_ADDRESS = 'CDR4NLV22STCXFGZPNKDQTEANWLF7LZ6AJLY6B7CLJXKHDZGYJWIOKGP';
430
+ /** Pinned interpreter address (mainnet). Mirrors DEPLOYMENTS.md:18. Mainnet
431
+ * has now been deployed (2026-08-04); the mainnet interpreter IS the binary
432
+ * that was exercised on testnet (same wasm sha256, see
433
+ * PINNED_INTERPRETER_WASM_SHA256). The address differs because instance
434
+ * ids are network-scoped. UNAUDITED at the time of writing - see
435
+ * DEPLOYMENTS.md:29-32 for the recorded decision and what is still pending. */
436
+ export const PINNED_INTERPRETER_MAINNET_ADDRESS = 'CALZAMUPREIRY4TULBEXIK77AUTOEJG63XLCPUWEHHQDOVK6ZVVS7VQ2';
430
437
  /** Pinned interpreter wasm sha256 (hex). Mirrors DEPLOYMENTS.md:156-157. */
431
438
  export const PINNED_INTERPRETER_WASM_SHA256 = '6e6c13d93e197aa380303a42cd120f5ddb080dd36ef2a343ee1dbd04ca52a443';
432
439
  /** The grammar version the interpreter enforces (matches SELF_VERSION in
@@ -437,6 +444,23 @@ export const PINNED_INTERPRETER_GRAMMAR_VERSION = 1;
437
444
  * than a Server; the two are deliberately different surfaces, so this is
438
445
  * pinned beside the addresses it is used with rather than shared. */
439
446
  export const TESTNET_RPC_URL = 'https://soroban-testnet.stellar.org';
447
+ /** Mainnet Soroban RPC. Pinned beside MAINNET counterparts so the install
448
+ * and get_interpreter_info gates compare against the matching network's
449
+ * RPC rather than always testnet. Public mainnet endpoint, the same one
450
+ * the deploy script hit during the 2026-08-04 mainnet rollout. */
451
+ export const MAINNET_RPC_URL = 'https://mainnet.sorobanrpc.com';
452
+ /** Pin + RPC lookup for the gate enforcement. The interpreters' wasm sha256
453
+ * is identical across both networks (the same binary was uploaded both
454
+ * places; DEPLOYMENTS.md:25-27), so `PINNED_INTERPRETER_WASM_SHA256` stays
455
+ * a single constant - only the addresses and RPCs are network-scoped. */
456
+ export const PINNED_INTERPRETER_ADDRESS_BY_NETWORK = {
457
+ testnet: PINNED_INTERPRETER_TESTNET_ADDRESS,
458
+ mainnet: PINNED_INTERPRETER_MAINNET_ADDRESS,
459
+ };
460
+ export const RPC_URL_BY_NETWORK = {
461
+ testnet: TESTNET_RPC_URL,
462
+ mainnet: MAINNET_RPC_URL,
463
+ };
440
464
  /** Soroban `valid_until` window (ledgers) added to the latest ledger when
441
465
  * building the auth entry. ~25 minutes at 5s/ledger. */
442
466
  export const DEFAULT_AUTH_VALID_UNTIL_LEDGERS = 300;
@@ -454,22 +478,34 @@ export const InstallPolicyInputSchema = z
454
478
  /** The signer that authorises the install (G... wallet). Used only for
455
479
  * sequence number + auth nonce simulation; never persisted, never signed. */
456
480
  sourceAccount: z.string(),
481
+ /** Target network for the install. Selects which interpreter pin and
482
+ * which RPC URL are valid by default. Defaults to `testnet` so the
483
+ * pre-mainnet callers keep working: they were always pointing at
484
+ * the testnet pin, so defaulting testnet here is the conservative
485
+ * continuation, NOT a silent fall-through that bypasses the gate.
486
+ * A caller that targets mainnet MUST set this to `mainnet` (the
487
+ * pin and RPC pin do not move by themselves). */
488
+ network: NetworkSchema.optional(),
457
489
  /** The proposed rule draft. Mirrors the core `ContextRuleDraft` shape. */
458
490
  rule: MandateSpecSchemaForRule,
459
491
  /** Per-rule install nonce; 1 for a fresh install. */
460
492
  installNonce: z.number().int().positive(),
461
- /** Optional RPC URL override. Defaults to the public testnet endpoint
462
- * (`TESTNET_RPC_URL`); the override is refused unless
493
+ /** Optional RPC URL override. Defaults to the pinned RPC for the
494
+ * selected `network` (testnet by default, mainnet when
495
+ * `network: 'mainnet'`); the override is refused unless
463
496
  * `allowUnpinnedRpcUrl: true`, because the auth nonce and
464
497
  * rootInvocation in the response come from whichever RPC answered. */
465
498
  rpcUrl: z.string().url().optional(),
466
499
  /** Opt-in to using a non-pinned RPC URL. Without this flag the tool
467
- * refuses any `rpcUrl` other than `TESTNET_RPC_URL` because the
468
- * caller's auth-digest binds to whatever the RPC returned. */
500
+ * refuses any `rpcUrl` other than the pinned RPC for the selected
501
+ * network because the caller's auth-digest binds to whatever the
502
+ * RPC returned. */
469
503
  allowUnpinnedRpcUrl: z.boolean().optional(),
470
504
  /** Opt-in to pointing the rule's interpreter policy at any address
471
- * other than the pinned testnet interpreter. Default-deny: a caller
472
- * that controls the interpreter can permit everything. */
505
+ * other than the pinned interpreter for the selected network.
506
+ * Default-deny: a caller that controls the interpreter can permit
507
+ * everything. Selecting `network: 'mainnet'` is NOT an opt-in -
508
+ * the mainnet pin is its own deny-by-default anchor. */
473
509
  allowUnpinnedInterpreter: z.boolean().optional(),
474
510
  /** Base fee in stroops; defaults to BASE_FEE (100). */
475
511
  baseFee: z.number().int().positive().optional(),
@@ -486,9 +522,12 @@ export const RevokePolicyInputSchema = z
486
522
  * verify, since the account's source is not in this repo. Proven on
487
523
  * testnet: the account's deployer can revoke. */
488
524
  sourceAccount: z.string(),
525
+ /** Target network for the revoke. Same `testnet`-default as install,
526
+ * so pre-mainnet callers keep working without an explicit flag. */
527
+ network: NetworkSchema.optional(),
489
528
  /** The rule id to remove from the smart account + interpreter. */
490
529
  ruleId: z.number().int().nonnegative(),
491
- /** Optional RPC URL override; same pin as install. */
530
+ /** Optional RPC URL override; same per-network pin as install. */
492
531
  rpcUrl: z.string().url().optional(),
493
532
  /** Opt-in to using a non-pinned RPC URL. Same semantics as install. */
494
533
  allowUnpinnedRpcUrl: z.boolean().optional(),
@@ -4,7 +4,7 @@ import { getInterpreterInfo } from '../install/get-interpreter-info.ts';
4
4
  import type { SimulationResult } from '../verify/envelope.ts';
5
5
  import { type RecordTransactionInput, type SimulatePolicyInput, type SynthesizePolicyInput, type VerifyPolicyInput } from './schemas.ts';
6
6
  export type { GetInterpreterInfoInput, InstallPolicyInput, RecordTransactionInput, RevokePolicyInput, SimulatePolicyInput, SynthesizePolicyInput, VerifyPolicyInput, } from './schemas.ts';
7
- export { ComposeUserResponsesSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MandateSpecSchema, NetworkSchema, OraclePriceFixtureSchema, OzAdapterConfigSchema, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, SimulatePolicyInputSchema, SynthesizePolicyInputSchema, ToolErrorSchema, VerifyPolicyInputSchema, } from './schemas.ts';
7
+ export { ComposeUserResponsesSchema, GetInterpreterInfoInputSchema, InstallPolicyInputSchema, InterpreterOptionsSchema, MAINNET_RPC_URL, MandateSpecSchema, NetworkSchema, OraclePriceFixtureSchema, OzAdapterConfigSchema, PINNED_INTERPRETER_ADDRESS_BY_NETWORK, PINNED_INTERPRETER_GRAMMAR_VERSION, PINNED_INTERPRETER_MAINNET_ADDRESS, PINNED_INTERPRETER_TESTNET_ADDRESS, PINNED_INTERPRETER_WASM_SHA256, PredicateLeafSchema, PredicateNodeSchema, RecordedTransactionSchema, RecordTransactionInputSchema, RevokePolicyInputSchema, RPC_URL_BY_NETWORK, SimulatePolicyInputSchema, SynthesizePolicyInputSchema, TESTNET_RPC_URL, ToolErrorSchema, VerifyPolicyInputSchema, } from './schemas.ts';
8
8
  export type RunRecordTransactionInput = RecordTransactionInput;
9
9
  export type RunSynthesizePolicyInput = SynthesizePolicyInput;
10
10
  export type RunSimulatePolicyInput = SimulatePolicyInput;
@@ -59,10 +59,12 @@ export declare function runVerifyPolicy(raw: unknown): Promise<ToolResponse<true
59
59
  * separate `interpreter.install` call is needed or possible.
60
60
  *
61
61
  * Default-deny: an interpreter policy address other than the pinned
62
- * testnet interpreter is REFUSED (the smart account would delegate to
63
- * an interpreter the caller controls); the same applies to a non-pinned
64
- * RPC URL (the auth nonce the wallet signs comes from the RPC). Both
65
- * gates accept an explicit opt-in flag. */
62
+ * interpreter for the selected network is REFUSED (the smart account
63
+ * would delegate to an interpreter the caller controls); the same
64
+ * applies to a non-pinned RPC URL (the auth nonce the wallet signs
65
+ * comes from the RPC). Both gates accept an explicit opt-in flag.
66
+ * Pin selection follows `input.network` (defaults to `testnet` so the
67
+ * pre-mainnet callers keep working unchanged). */
66
68
  export declare function runInstallPolicy(raw: unknown): Promise<ToolResponse<BuildInstallPolicyResult>>;
67
69
  /** `revoke_policy` body - thin wrapper over `buildRevokePolicyXdr`.
68
70
  * Emits an unsigned XDR for `account.remove_context_rule(ruleId)`; the
@@ -71,14 +73,23 @@ export declare function runInstallPolicy(raw: unknown): Promise<ToolResponse<Bui
71
73
  *
72
74
  * Same RPC pin as install: a non-pinned `rpcUrl` is refused unless
73
75
  * `allowUnpinnedRpcUrl: true`. Revoke does not carry an interpreter
74
- * policy payload, so the interpreter pin is not re-checked here. */
76
+ * policy payload, so the interpreter pin is not re-checked here.
77
+ * Pin selection follows `input.network` (defaults to `testnet`). */
75
78
  export declare function runRevokePolicy(raw: unknown): Promise<ToolResponse<BuildRevokePolicyResult>>;
76
79
  /** `get_interpreter_info` body - thin wrapper over `getInterpreterInfo`.
77
80
  * Returns the pinned deployment fingerprint + an optional live
78
81
  * `grammar_version()` comparison. The audit field is deliberately
79
82
  * OMITTED (phase-04's "audit #44" has no source of truth in the repo -
80
83
  * fabricating it would be a lie on a security surface; the live
81
- * mismatch check is worth MORE). */
84
+ * mismatch check is worth MORE).
85
+ *
86
+ * Network-aware: `input.network` selects which interpreter pin and RPC
87
+ * to use. Mainnet was rolled out 2026-08-04 - the same wasm hash was
88
+ * uploaded to mainnet as was exercised on testnet, so a single
89
+ * `PINNED_INTERPRETER_WASM_SHA256` constant backs both networks
90
+ * (DEPLOYMENTS.md:25-27). The address differs because instance ids are
91
+ * network-scoped. UNAUDITED at the time of writing - see
92
+ * DEPLOYMENTS.md:29-32 for what is still pending. */
82
93
  export declare function runGetInterpreterInfo(raw: unknown): Promise<ToolResponse<ReturnType<typeof getInterpreterInfo>>>;
83
94
  /** Build a canonical ToolError for a thrown exception caught by the tool
84
95
  * envelope. The MCP SDK stringifies thrown objects as "[object Object]" by