Skip to content

Payment skill interoperability: spend-cap + audit-trail extension proposal #31

@rayc0

Description

@rayc0

Context

payment-skill and x402-skill are the most complete OpenClaw payment primitives available today, and the x402 rail abstraction in particular sets a clean separation between payment-method negotiation and higher-level policy. The agentic payment space has moved fast since Alipay AI Pay shipped on April 21 — demand for composable, auditable payment primitives is growing — and the ClawHavoc disclosure (138 CVEs across 14 ClawHub-listed skills, April 2026) has made cryptographic authorization a first-class concern rather than an afterthought. We are building @pqsafe/openclaw (npm, Apache-2.0), a post-quantum spend-cap layer published at clawhub.ai/skills/pqsafe-pay, and we'd like to explore how it composes with your rail layer rather than replicate it.


Proposal: SpendEnvelope as a cryptographic mandate layer over payment-skill

The core idea is that payment-skill handles the rail (which network, which credential, how to settle), while SpendEnvelope sits one layer above as an issuer-signed authorization token that a receiving agent must verify before the rail is invoked. This mirrors the FIDO Agentic Auth TWG's framing (April 28 2026) of "secure delegation" — the authorizing principal issues a bounded mandate; the executing agent enforces it without needing to call back to the issuer.

SpendEnvelope interface

/**
 * Issuer-signed authorization token.
 * Verified by the payment skill BEFORE any rail is invoked.
 * Signature: ML-DSA-65 (FIPS 204) — serialized signature is 3,309 bytes.
 */
export interface SpendEnvelope {
  /** DID or public key of the authorizing principal */
  issuer: string;

  /** Allowlisted recipient DIDs or payment addresses; empty = any */
  recipient_allowlist: string[];

  /** Maximum amount this envelope authorizes, in smallest currency unit */
  max_amount: number;

  /** ISO 4217 currency code, or chain-native token identifier */
  currency: string;

  /** Unix timestamp (seconds) — envelope is invalid after this point */
  expires_at: number;

  /** Random 128-bit value — prevents replay across invocations */
  nonce: string;

  /**
   * ML-DSA-65 signature over canonical JSON of the above fields.
   * Serialized length: 3,309 bytes (per FIPS 204 Table 2, Level 3).
   */
  signature: Uint8Array;
}

Proposed hook in payment-skill

Adding a single optional pre-flight check in the payment-skill invocation path — roughly:

import { verifySpendEnvelope } from "@pqsafe/openclaw";

async function executePayment(
  params: PaymentParams,
  ctx: SkillContext,
  envelope?: SpendEnvelope
): Promise<PaymentResult> {
  if (envelope) {
    const check = await verifySpendEnvelope(envelope, ctx);
    if (!check.ok) {
      throw new PaymentAuthorizationError(check.reason);
    }
    // Enforce spend cap even if the rail would permit more
    params.amount = Math.min(params.amount, envelope.max_amount);
  }

  // Existing x402 / rail logic continues unchanged
  return invokeRail(params, ctx);
}

verifySpendEnvelope is a pure function: it checks signature, expiry, nonce freshness, and recipient allowlist. No network call. The nil envelope path is a no-op, so existing integrations are unaffected.

Audit log extension

Your current log entry shape (as I read it from the repo) is:

interface PaymentLogEntry {
  timestamp: string;
  amount: number;
  currency: string;
  recipient: string;
  rail: string;
  tx_id: string;
}

We'd extend (not replace) with an optional authorization field:

interface PaymentLogEntry {
  timestamp: string;
  amount: number;
  currency: string;
  recipient: string;
  rail: string;
  tx_id: string;
  // Optional — present only when a SpendEnvelope was verified
  authorization?: {
    issuer: string;
    envelope_nonce: string;
    envelope_expires_at: number;
    authorized_max: number;
    sig_alg: "ML-DSA-65";
  };
}

Consumers that don't care about the PQ auth layer see an unchanged log format. Consumers that do can verify the authorization field independently.


Why composable, not competing

payment-skill stays as the rail layer — we are not proposing any changes to how it selects, authenticates, or settles payments. SpendEnvelope is a cryptographic mandate issued by a human or orchestrating agent to a sub-agent before the payment skill is even invoked. The separation is clean: rail-layer = how to pay; mandate layer = whether and how much.

The FIDO Agentic Auth TWG (chaired by CVS Health, Google, and OpenAI, April 28 2026) articulated "secure delegation" as the right mental model for agentic payments: the principal issues a scoped credential; the executor verifies it locally without a round-trip. SpendEnvelope is an implementation of that pattern using post-quantum signatures (ML-DSA-65, FIPS 204) because agentic payment contexts will outlive RSA/ECDSA security horizons.

License compatibility: @pqsafe/openclaw is Apache-2.0; payment-skill and x402-skill are MIT. Apache-2.0 is compatible with MIT — you can include or link against our package without any license contamination.


Concrete next step

Two options — whichever works better for you:

  1. 30-minute video call — happy to walk through the interface design and hear your reaction directly. I'm UTC+8, flexible on timing.
  2. Joint RFC draft — we open a shared repo (or a branch here) with a SPEC.md that formalizes the interface contract. Either of us can draft the first pass.

No pressure on timeline. If there's an existing discussion thread or design doc I should read first, point me there and I'll do the homework before reaching out again.


Disclosure

Raymond Chau, founder of PQSafe Inc. (Delaware C-Corp). We are active in the HKMA Fintech Supervisory Sandbox process and applying to YC S26. I'm not name-dropping — just being transparent about where we're coming from so you can evaluate this proposal in full context. We have no undisclosed interests in how this spec lands.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions