StableOps

Signer

Use StableOps Agent Payments Signer CLI to validate pairing challenges, run a loopback sidecar, and sign constrained payments with local keys or AWS KMS.

@stableops/agent-payments-signer keeps wallet keys in the user's environment. The user runs a CLI command for one-time wallet registration. For daily payments, MCP calls a local signing sidecar:

Agent → stableops-agent-payments-mcp → signing sidecar /v1/sign → local wallet key or AWS KMS

The sidecar signs only EIP-3009, Permit2, or Solana payments that match a short-lived StableOps execution grant. The grant binds the Agent, wallet, network, asset, recipient, amount, nonce, and validity window. Mismatches, expiration, unknown issuer keys, and replay conflicts are rejected.

Two private keys serve different purposes:

Private keyConfigured byWhere it is configured
Payment-wallet private keyCustomerOn the signer host. Local EVM uses STABLEOPS_EVM_PRIVATE_KEY_FILE, Solana uses STABLEOPS_SOLANA_PRIVATE_KEY_FILE, and AWS KMS uses its KMS key ID.
Execution-grant signing private keyControl-plane operatorIn the API service as AGENT_PAYMENTS_GRANT_PRIVATE_KEY_BASE64. Customers and signer sidecars never receive this private key.

The signer automatically retrieves only the public key corresponding to the execution-grant signing key. It uses that public key to verify grants from the StableOps control plane, not to sign payments. Payments are still signed by the customer's payment-wallet private key or AWS KMS key.

Command-line interface

The npm package includes the stableops-agent-payments-signer CLI and requires Node.js 20 or later. Install it globally to invoke it directly from a terminal:

pnpm add -g @stableops/agent-payments-signer

Use --help to list the commands supported by the installed version:

stableops-agent-payments-signer --help

The CLI has two primary commands:

CommandLifetimePurpose
pair-walletOne-shotValidates and signs one unexpired wallet-registration challenge, writes the signature to standard output, and exits. Pass the challenge with --challenge <base64url>. When the option is omitted, the command reads the complete challenge from standard input.
serveLong-runningStarts a signing sidecar bound to loopback, with health-check and execution-grant-constrained payment-signing endpoints. Key, network, grant-verification, authorization-store, and service-authentication settings are read from environment variables.

Both commands accept --signer <local|aws-kms|solana>. Alternatively, set STABLEOPS_SIGNER_TYPE. The command-line option takes precedence when both are present. local uses a local EVM test key, aws-kms uses an EVM key in AWS KMS, and solana uses a local Solana key. The CLI never accepts local private-key values as arguments or environment variables—only paths to protected key files.

A signer being able to create a signature does not mean a paid service accepts that network. Start with the Base Sepolia local EVM signer. Before choosing another signer and network, confirm that the service's payment requirement matches the supported scope, then complete a small payment that requires human approval.

Register a wallet: pair-wallet

Obtain a one-time challenge. Run the one-shot command in a trusted terminal.

MCP returns the complete challenge text. You can pass it directly to the CLI through standard input.

First, set the private-key file path in a trusted terminal:

export STABLEOPS_EVM_PRIVATE_KEY_FILE='/protected/absolute/path/evm-private-key'

Choose the signer that controls the address in the challenge:

  • Local EVM Sandbox: set STABLEOPS_EVM_PRIVATE_KEY_FILE as above and use --signer local.
  • EVM with AWS KMS: set STABLEOPS_KMS_KEY_ID and AWS_REGION, then use --signer aws-kms. The CLI derives the KMS key's EVM address and rejects a challenge for another address.
  • Solana: set STABLEOPS_SOLANA_PRIVATE_KEY_FILE to the protected Base58 key file and use --signer solana.

Copy only the complete seven-line challenge from StableOps Agent Wallet Pairing through Expires At: .... Keep the field names at the start of each line with no added indentation or inserted line breaks. Do not copy the surrounding explanation or the separate challenge ID.

Choose one input method for your system and run the signer only once. On macOS, pipe the challenge from the clipboard:

pbpaste | stableops-agent-payments-signer pair-wallet --signer <local|aws-kms|solana>

On other systems, start the interactive command:

stableops-agent-payments-signer pair-wallet --signer <local|aws-kms|solana>

When the command waits for input, paste the seven lines. Press Enter after the last line, then press Ctrl-D on the empty next line to close standard input. The CLI validates the challenge format, expiry, and wallet address, then writes only the signature to standard output. Local EVM and AWS KMS signers print a hexadecimal signature beginning with 0x; the Solana signer prints a Base58 signature without that prefix. Return the output to the Agent unchanged and do not convert it to Base64.

Only use --challenge when you have first encoded the complete challenge as unpadded Base64URL:

stableops-agent-payments-signer pair-wallet --signer <local|aws-kms|solana> --challenge <complete-challenge-as-unpadded-Base64URL>
pair-wallet signs only a StableOps pairing challenge whose format, expiry, and wallet address pass validation. It does not accept arbitrary messages or handle daily payments.

See Quickstart: Register and bind a payment wallet for the complete flow.

Daily payments: serve

serve starts the sidecar at 127.0.0.1:8789 with GET /health and POST /v1/sign. Runtime MCP calls it with the sidecar URL and authentication token. The Agent does not execute the signer CLI directly.

Common configuration

You only need to prepare the authorization record path and sidecar token. The signer discovers execution-grant verification keys automatically:

VariableHow to configure it
STABLEOPS_AUTHORIZATION_STORE_FILEAbsolute path for the persistent JSON record. Do not create the file in advance. The signer creates it with mode 0600 when recording the first authorization.
STABLEOPS_SIDECAR_TOKENA shared authentication token you generate. Generate one random value containing at least 32 bytes, then configure the exact same value in the signer and runtime MCP.

On macOS or Linux, prepare the storage directory. Replace the example username with the actual username:

mkdir -p /Users/alice/.config/stableops/signer
chmod 700 /Users/alice/.config/stableops/signer

Generate the sidecar token once:

openssl rand -hex 32

Copy the 64-character hexadecimal output and use the same value in the signer and runtime MCP. Do not run the command separately for each process, because that produces different tokens.

Then configure the signer:

export STABLEOPS_AUTHORIZATION_STORE_FILE='/Users/alice/.config/stableops/signer/grant-authorizations.json'
export STABLEOPS_SIDECAR_TOKEN='64-character hexadecimal value from openssl'

Automatic execution-grant key discovery

When serve starts, it requests this public endpoint:

GET https://api.stableops.dev/v1/public/agent-payments/execution-grant-keys

The endpoint returns the current control plane's Ed25519 key IDs and public keys as JWKS. It requires no API Key and never returns the signing private key. The signer validates the key type, algorithm, and encoding before starting. It refuses to start if the endpoint is unavailable or invalid.

Verification keys are not unique to a user, organization, or wallet. Each StableOps API deployment publishes a set of verification keys shared by the organizations it serves. Every execution grant still separately binds the organization, Agent, Intent, wallet, environment, network, asset, recipient, amount, nonce, and validity window.

Configure STABLEOPS_GRANT_KEY_ID and STABLEOPS_GRANT_PUBLIC_KEY_FILE together only for offline deployments or public-key pinning. When set, the signer reads the local PEM file and does not call the discovery endpoint. Update both values together during key rotation.

StableOps publishes verification keys automatically. You do not need to configure a control-plane URL, open a support ticket, or create an execution-grant key for your organization.

Runtime MCP connects with:

{
  "STABLEOPS_SIDECAR_URL": "http://127.0.0.1:8789",
  "STABLEOPS_SIDECAR_TOKEN": "same-high-entropy-token-as-the-signer"
}

Local EVM test signer

local is limited to Sandbox EVM test networks. Its private key must match the wallet registered with StableOps:

export STABLEOPS_EVM_PRIVATE_KEY_FILE='/protected/absolute/path/evm-private-key'

stableops-agent-payments-signer serve --signer local
The environment variable contains an absolute file path, not the private key itself. The file must contain 0x followed by 64 hexadecimal digits. It must be a regular, non-symlink file restricted to mode 0600. The CLI reports a specific error when the 0x prefix is missing.

local defaults to SANDBOX, Base Sepolia network eip155:84532, and port 8789. Set STABLEOPS_SIGNER_NETWORK for another Sandbox EVM network. Local EVM mode cannot connect to Live.

Solana signer

Solana uses a Base58-encoded private key. Set the environment and Solana CAIP-2 network explicitly:

export STABLEOPS_SIGNER_ENVIRONMENT=LIVE
export STABLEOPS_SIGNER_NETWORK='solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp'
export STABLEOPS_SOLANA_PRIVATE_KEY_FILE='/protected/absolute/path/solana-private-key'

stableops-agent-payments-signer serve --signer solana

The Solana key file also requires an absolute path, a regular file, and mode 0600. The facilitator fee payer covers transaction fees, so the payment wallet does not need SOL for the x402 payment.

AWS KMS signer

A Live EVM topology should keep the private key in AWS KMS. AwsKmsSigner requires a SIGN_VERIFY key using ECC_SECG_P256K1 with ECDSA_SHA_256:

export STABLEOPS_SIGNER_ENVIRONMENT=LIVE
export STABLEOPS_SIGNER_NETWORK='eip155:8453'
export STABLEOPS_KMS_KEY_ID='replace-with-the-KMS-key-id-or-ARN'
export STABLEOPS_WALLET_ADDRESS='0x-replace-with-the-KMS-wallet-address'
export AWS_REGION='replace-with-the-KMS-region'

stableops-agent-payments-signer serve --signer aws-kms

At startup, the signer retrieves the KMS public key and verifies its EVM address. It refuses to start when that address differs from STABLEOPS_WALLET_ADDRESS. KMS signs only payment digests that passed execution-grant validation.

AWS KMS supports EVM networks only. Live payments also require the organization mainnet risk gates.

Deployment checklist

  • Bind the sidecar to loopback only. Never expose it publicly.
  • Give runtime MCP only the sidecar URL and token, not the wallet key or KMS administration rights.
  • Discover grant verification keys from a trusted control-plane URL. When pinning a local key, rotate the key ID and public-key file together.
  • Use a separate signer, wallet, and authorization record for each network.
  • A single instance may use the file authorization record. Multi-replica deployments need shared storage with atomic writes and uniqueness constraints.
  • Log identifiers and error categories, never private keys, full grants, or payment signatures.

How is this guide?

Last updated

On this page