🌈 jup.sh docs

CLI Technical Design

The CLI is the first real product surface for jup.sh.

The website explains the idea. The CLI validates whether an agent can create a payment intent, receive a deterministic policy result, and hand off to Risk Review when needed.

Goal

Make this command meaningful:

jup-sh pay --agent deepseek --token SOL --amount 20 --settle USDC

The CLI should produce a structured local payment intent:

agent intent -> policy checks -> quote estimate -> final decision -> local record

It should not sign, submit, or execute a payment in the alpha.

Command Surface

flowchart TB
  Root["jup-sh"]
  Pay["pay"]
  Intent["intent"]
  Policy["policy"]

  Root --> Pay
  Root --> Intent
  Root --> Policy
  Intent --> List["list"]
  Intent --> Show["show"]
  Intent --> Export["export"]
  Policy --> ShowPolicy["show"]
  Policy --> InitPolicy["init"]

Primary command:

jup-sh pay --agent deepseek --token SOL --amount 20 --settle USDC

Important options:

Option Required Description
--agent yes Agent name, such as deepseek, claude, qwen, or codex.
--token yes Payer token symbol.
--amount yes Settlement amount. Preferred syntax is --amount 20 --settle USDC.
--settle yes Settlement token. The alpha supports USDC.
--recipient no Recipient address or local label. Unknown recipients trigger review by default.
--reference no External reference or memo.
--json no Print only the JSON contract.
--quote-provider no mock by default. Use jupiter for quote-only real routing.
--jupiter-quote-url no Defaults to Jupiter Swap quote API.
--jupiter-api-key no Optional API key. Defaults to JUPITER_API_KEY when set.
--slippage-bps no Slippage tolerance for Jupiter quotes. Defaults to 50.
--review-base-url no Defaults to https://jup.sh.
--policy no Optional policy file. Defaults to ./jup.policy.json when present.
--store no Intent storage directory. Defaults to .jup-sh/intents.

The legacy form --settle 20 USDC is still accepted for compatibility, but new examples should use --amount 20 --settle USDC.

Runtime Architecture

flowchart LR
  Clap["clap parser"]
  Input["CreatePaymentIntentInput"]
  Policy["Policy loader"]
  Quoter["SettlementQuoter"]
  Core["jup_sh_core"]
  Store["intent store"]
  Output["human output or JSON"]

  Clap --> Input
  Clap --> Policy
  Clap --> Quoter
  Input --> Core
  Policy --> Core
  Quoter --> Core
  Core --> Store
  Core --> Output

The CLI owns parsing, storage, environment variables, and terminal behavior. The core crate owns payment intent creation, policy evaluation, quote policy, and output models.

Rust Workspace

rust/
  Cargo.toml
  crates/
    core/
      src/lib.rs
    cli/
      src/main.rs

Responsibilities:

Crate Owns
jup_sh_core Data models, policy evaluation, quote abstraction, intent creation.
jup-sh-cli Commands, CLI args, policy file loading, intent persistence, output formatting.

This split matters because a future SDK or backend should reuse the core crate without inheriting CLI-specific behavior.

Core API

The central core call is:

create_payment_intent_with_quoter(input, policy, review_base_url, quoter)

Inputs:

CreatePaymentIntentInput {
    agent: "deepseek".into(),
    pay_token: "SOL".into(),
    settle_amount: 20.0,
    settle_token: "USDC".into(),
    recipient: None,
    reference: None,
}

Output:

PaymentIntent {
    intent_id,
    agent,
    pay_token,
    recipient,
    reference,
    settlement,
    quote,
    status,
    decision,
    next_action,
    risk_level,
    reasons,
    policy_checks,
    review_url,
    created_at,
}

Policy Evaluation

Policy is deterministic and local in the alpha.

Default shape:

{
  "maxAutoSettleUSDC": 5,
  "maxAllowedSettleUSDC": 100,
  "maxPriceImpactBps": 100,
  "reviewHighPriceImpact": true,
  "verifiedTokens": ["USDC", "SOL", "JUP", "BONK"],
  "trustedRecipients": [],
  "reviewUnknownRecipients": true
}

Policy checks are returned as evidence:

{
  "name": "recipient_trust",
  "status": "review",
  "message": "recipient is not trusted"
}

Decision mapping:

Decision Status Next action Exit code
auto_pay ready_for_authorization ready_for_authorization 0
review_required review_required open_review 2
rejected rejected rejected 1

Quote Boundary

classDiagram
  class SettlementQuoter {
    <<trait>>
    quote_settlement(input) SettlementQuote
  }

  class MockSettlementQuoter
  class JupiterSettlementQuoter

  SettlementQuoter <|.. MockSettlementQuoter
  SettlementQuoter <|.. JupiterSettlementQuoter

The mock provider is the default for local development and tests.

Jupiter mode is quote-only:

jup-sh pay \
  --agent deepseek \
  --token SOL \
  --amount 20 \
  --settle USDC \
  --quote-provider jupiter

The quote is used by policy checks such as quote_price_impact. The alpha does not request swap transaction payloads.

Intent Storage

Local intents are stored as JSON files:

.jup-sh/intents/<intent_id>.json

Commands:

jup-sh intent list
jup-sh intent show intent_xxx
jup-sh intent export intent_xxx

Export turns the saved local intent into a static Risk Review URL:

https://jup.sh/pay/intent_xxx#intent=<base64url-json-payload>

JSON Output Mode

pay --json is the agent contract. It prints one JSON object to stdout and uses exit codes for control flow.

See CLI JSON Contract for the full schema.

Release Gate

The alpha release gate is:

npm run release:check

It runs:

Non-Goals

The CLI alpha does not implement:

Implementation Sequence

flowchart LR
  A["1. stable CLI contract"]
  B["2. quote-aware policy"]
  C["3. Risk Review export"]
  D["4. transaction request"]
  E["5. local signing boundary"]
  F["6. settlement status"]

  A --> B --> C --> D --> E --> F

The current alpha covers steps 1 through 3. Later phases should not skip the authorization boundary: agents create intents, users or local policy authorize funds.

The draft Transaction Request Skeleton Design documents step 4 before runtime implementation. It should remain separate from pay --json until request expiry, replay protection, review gating, and wallet handoff rules are explicit.