日本語

TypeScript SDK · v0.2.0

payment wrapper が requirement を受け取る前に検証する。

アプリケーションが受け取った x402 v2 requirement を、IFF の独立した observation と比較します。 consistent、diverged、stale、unobserved を、 自身の payment policy に対する別々の入力として扱ってください。

  • Node 20+
  • モダンブラウザー
  • ES modules
  • TypeScript 型
npm からインストール
npm install @ifandonlyif/x402-preflight

browser/Node library、TypeScript declaration、1-tool MCP command を含みます。

  1. 01402 を受信payment client が requirement を受け取ります。
  2. 02デコードSDK が x402 v2 envelope を読み取ります。
  3. 03比較IFF が signed observation と照合します。
  4. 04policy を適用続行するかどうかはコードが決定します。

01 · インストール

1 つの package、API key は不要。

Preflight verification は IFF の公開 production API を使用します。owner authentication は不要です。

ターミナル
npm install @ifandonlyif/x402-preflight
Runtime contract

global fetch と WebCrypto を使用します。wallet key を持たず、payment を実行せず、402 response を変更しません。

02 · 直接検証

payment ではなく比較から始める。

この Node 20+ の例は requirement だけを IFF に送信します。URL と payment field は fixture です。client が実際に受け取った endpoint と x402 v2 envelope を使ってください。provider には request せず、payment もしません。

payTo が変わったときに比較する項目

preflight.mjs として保存し、node preflight.mjs で実行
import { verify, VerifyRequestError } from "@ifandonlyif/x402-preflight";

// Demonstration data, not a request to buy this service.
const endpoint = "https://api.example.com/v1/quote";
const paymentRequired = {
  x402Version: 2,
  accepts: [{
    scheme: "exact",
    network: "eip155:8453",
    asset: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    amount: "1000",
    payTo: "0x1111111111111111111111111111111111111111",
    maxTimeoutSeconds: 60,
  }],
};

// Example application policy, NOT wrapFetch's default policy.
function decide(result) {
  switch (result?.verdict) {
    case "consistent":
      return result.matches_last_observed === true
        ? { state: "consistent", action: "continue_to_own_policy" }
        : { state: "unexpected_result", action: "hold" };
    case "diverged":
      return { state: "diverged", action: "hold" };
    case "stale":
      return {
        state: result.matches_last_observed === true ? "stale_match"
          : result.matches_last_observed === false ? "stale_mismatch"
          : "stale_unknown",
        action: "hold",
      };
    case "unobserved":
      return { state: "unobserved", action: "hold" };
    default:
      return { state: "unexpected_result", action: "hold" };
  }
}

async function preflightDecision(url, requirement) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), 3000);
  try {
    const result = await verify(url, requirement, {
      fetch: (input, init) => fetch(input, {
        ...init, signal: controller.signal, redirect: "error",
      }),
    });
    return decide(result);
  } catch (error) {
    if (error instanceof VerifyRequestError) {
      return error.status === 429
        ? { state: "iff_rate_limited", action: "retry_later" }
        : { state: "iff_request_failed", action: "hold", status: error.status };
    }
    return {
      state: controller.signal.aborted ? "iff_timeout" : "iff_unavailable",
      action: "hold",
    };
  } finally {
    clearTimeout(timer);
  }
}

// Log the decision only, not the requirement or raw error body.
console.log(await preflightDecision(endpoint, paymentRequired));
// No payment follows, including when the state is consistent.
範囲が明確な明示的 policy

3 秒の deadline は application code です。verify() に built-in timeout や retry はありません。この例は stale と unknown をすべて保留します。continue_to_own_policy は比較が通過したことだけを意味し、wallet、budget、merchant、payment rule は引き続き適用されます。unknown fixture は通常 unobserved を返します。

credentials、query、fragment を含まない完全な public HTTPS endpoint path と、受け取った envelope を使用してください(推測した price や address は不可)。PAYMENT-REQUIRED header のデコードには decodePaymentRequiredHeader() を使います。browser では provider の CORS policy がその header の読み取りを許可する必要があります。

03 · 強制せずに評価

既存の policy と並行して観察する。

payment decision を変えずに comparison outcome を収集します。Shadow evaluation は application-side の verify() 呼び出しであり、SDK mode や wrapFetch() の default ではありません。

再利用可能な observation helper · payment client は呼び出しません
import { verify, VerifyRequestError } from "@ifandonlyif/x402-preflight";

export async function shadowPreflight(url, paymentRequired, record = console.info) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), 3000);
  let event;
  try {
    const result = await verify(url, paymentRequired, {
      fetch: (input, init) => fetch(input, {
        ...init, signal: controller.signal, redirect: "error",
      }),
    });
    const verdicts = ["consistent", "diverged", "stale", "unobserved"];
    event = {
      mode: "shadow",
      state: verdicts.includes(result?.verdict) ? result.verdict : "unexpected_result",
      matches_last_observed: typeof result?.matches_last_observed === "boolean"
        ? result.matches_last_observed : null,
    };
  } catch (error) {
    event = {
      mode: "shadow",
      state: error instanceof VerifyRequestError && error.status === 429
        ? "iff_rate_limited" : controller.signal.aborted ? "iff_timeout" : "iff_unavailable",
    };
  } finally {
    clearTimeout(timer);
  }
  // Do not expose the URL, requirement, API error body or wallet details.
  try { await record(event); } catch { /* Telemetry must not change policy. */ }
}

// At the point where your client already has the actual requirement:
// void shadowPreflight(exactEndpointUrl, actualPaymentRequired);
// Keep the existing payment decision separate; this helper never authorizes it.

IFF への comparison request を 1 回行い、provider には request しません。IFF は提出された requirement を HTTPS で受け取ります。helper が制限するのは log の内容だけです。DISCOVERY_ENABLED が有効な場合、unobserved URL は discovery candidate として保持されることがあります。requirement は保存されません。

04 · Payment wrapper

wrapper が通過させる内容を把握する。

unpaid の base fetch をラップしてから payment library に渡してください。payment-enabled の fetch をラップすると比較が遅れることがあります。

TypeScript
import {
  wrapFetch, X402DivergenceError, X402StaleMismatchError,
} from "@ifandonlyif/x402-preflight";

const preflightFetch = wrapFetch(fetch, {
  onDiverged: "throw",
  onStale: "strict",
  onUnobserved: "warn",
});

export async function fetchWithPreflight(url, init) {
  try {
    return await preflightFetch(url, { ...init, redirect: "error" });
  } catch (error) {
    if (error instanceof X402DivergenceError) {
      console.warn("IFF requirement diverged; payment flow stopped.");
    } else if (error instanceof X402StaleMismatchError) {
      console.warn("IFF stale evidence mismatched; payment flow stopped.");
    }
    // Do not turn a rejected preflight into a bare-fetch/payment retry.
    throw error;
  }
}

// Give fetchWithPreflight to your payment library as its base fetch.
// This snippet alone does not send a request or enable payments.
onDiverged
default は throw です。mismatch は payment logic が進む前に wrapper を停止します。
onStale
default は strict です。stale mismatch は throw し、stale でも一致する evidence は warning になります。
onUnobserved
default は warn です。missing evidence は表示されますが、unsafe とは表示されません。
重要:この wrapper は IFF error に対して fail-open です。

v0.2.0 では、IFF network error、timeout、429、その他の non-success response があると、warning なしで元の 402 が変更されずに通過します。non-402 とデコードできない 402 も通過します。built-in deadline や automatic retry はありません。unavailability 時に flow を保留する必要がある場合は、直接の例を使用してください。

比較が受け入れられると元の response body は読めます。divergence または stale-mismatch error では fetch promise が reject されます。matches_last_observed がない stale result は warning になります。wrapper は API result 内の cryptographic proof を検証しません。

05 · MCP

MCP で agent を接続する。

3 つの別々の MCP 接続方法があります。

公開 HTTP alpha

evidence、fingerprint、transparency log、receipt check 用の匿名・読み取り専用 tools 13 個(Streamable HTTP)。stateless JSON で、authentication、OAuth、SSE session はありません。verification は hosted service 上で実行されます。payment と submit_discovery_candidate はありません。

https://iff-mcp-api-production.up.railway.app/mcp/iff

公開 npm command

package には stdio command iff-x402-preflight-mcp が含まれ、tool は verify_x402_endpoint の 1 つです。

global install なしで実行
npx --package=@ifandonlyif/x402-preflight \
  iff-x402-preflight-mcp

ローカル Go の内部 alpha プレビュー

完全版の Go stdio server は内部 alpha です。source と binary は配布されておらず、npm package、hosted HTTP service、公開 Apostille repository のいずれにも含まれません。

  • iff-mcp:15 tools。公開 evidence、requirement comparison、transparency log data、STH・inclusion・consistency・Service Receipt のローカル検証。payment は実行しません。
  • apostille-mcp:20 tools。offline verification と、任意の registration、local signing、submission、private ERC-8004 workflow。authenticated action には明示的な設定と operator 指定の key file が必要です。verification を trust するには独立した完全一致の issuer/key pin が必要です。

Apostille MCP の security model を読む。

06 · Verdict と failure handling

4 つの state、隠れた score はありません。

SDK は trust label を付けず、API vocabulary をそのまま返します。

consistent

受け取った requirement は comparison window と一致します。

diverged

amount または payee fingerprint が observation と異なります。

stale

evidence は存在しますが freshness window 外です。

unobserved

IFF には比較可能な fingerprint observation がありません。

application example と v0.2.0 wrapper default の比較
結果直接の例default の wrapFetch
consistent自身の policy による判断に進むだけです。元の 402 を返します。
diverged保留し、観察された変更を確認します。Throw X402DivergenceError.
stale + matchfresh evidence が得られるまで保留します。warning を出して元の 402 を返します。
stale + mismatch保留します。stale でも mismatch は消えません。Throw X402StaleMismatchError.
unobserved保留します。比較可能な evidence がないことは risk verdict ではありません。warning を出して元の 402 を返します。
IFF timeout/ネットワーク障害保留し、verification unavailable を報告します。failure 後に元の 402 を返します。built-in timeout はありません。
IFF 429自身の rate-limit policy に従って後で retry します。warning なしで元の 402 を返します。

429、network error、IFF 5xx は diverged の証拠ではありません。busy-loop は避け、直接 HTTP API では Retry-After を尊重するか、jitter と retry cap を持つ bounded backoff を使います。VerifyRequestError が公開するのは status と body で、header は含みません。400/422 は retry 前に修正してください。これらの snippet は automatic retry を行いません。

pilot を強制適用する前に、両方の stale case を含む 7 行すべてを fixture で replay し、API outage と endpoint evidence を分けて扱ってください。versioned v0.2.0 implementation を確認する。

07 · 境界

evidence は入力であり、保証ではありません。

  • SDK は paid canary transaction を決して送信しません。
  • match は payment 後の delivery を保証せず、payment-safety の主張でもありません。composite score はありません。
  • monitor signature が示すのは provenance と tamper evidence であり、attestation や trusted execution ではありません。
  • Service Receipt v2 は core preflight flow 外の実験的な direct-API showcase です(local verifier、英語)。x402 payment receipt ではなく、payment、service delivery、caller identity、remote execution、result correctness、safety を証明しません。
  • payment policy と risk は application の責任です。

質問またはセキュリティ報告: [email protected]