{
  "$schema": "https://raw.githubusercontent.com/agentdotjson/spec/main/schemas/v0.1.0/agents.schema.json",
  "spec_version": "0.1.0",
  "name": "Recursa",
  "description": "Solana yield aggregator. Lists leveraged-lending strategies across JupLend, Kamino multi-market, and Project0; previews positions with health-factor + liquidation-price math; exposes intents (LEVERAGED LOOP, RATE ARB, EMISSIONS ARB, DIRECTIONAL, CARRY, FIXED YIELD, EARN). Designed for autonomous agent operation — every read endpoint is queryable without auth, transaction submission is signer-agnostic.",
  "homepage": "https://recursa-solana.pages.dev",
  "repository": "https://github.com/stunt101harm/recursa_solana",
  "contact": {
    "url": "https://github.com/stunt101harm/recursa_solana/issues"
  },
  "capabilities": {
    "rates": {
      "description": "Live lending rates across JupLend, Kamino multi-market, and Project0. Updated every 60s server-side.",
      "url": "https://recursa-solana.pages.dev/api/rates",
      "method": "GET",
      "auth": "none",
      "rate_limit": {
        "anonymous": "60 req/min per IP",
        "agent_authenticated": "600 req/min per signed pubkey"
      }
    },
    "funding_rates": {
      "description": "Live funding rates from Jupiter Perps and Flash Trade for delta-neutral strategy generation.",
      "url": "https://recursa-solana.pages.dev/api/funding-rates",
      "method": "GET",
      "auth": "none"
    },
    "rpc_proxy": {
      "description": "Solana JSON-RPC proxy for read-only queries. Hides our Helius API key while allowing agent-side reads.",
      "url": "https://recursa-solana.pages.dev/api/rpc",
      "method": "POST",
      "auth": "none",
      "notes": "Method allowlist applies. For high-volume agent reads, use your own RPC endpoint via the SDK config."
    },
    "ai_chat": {
      "description": "AI copilot endpoint. Stream-based chat for natural-language strategy discovery + position reasoning. Backed by Kimi K2.6.",
      "url": "https://recursa-solana.pages.dev/api/ai",
      "method": "POST",
      "auth": "none",
      "notes": "Server-Sent Events response. Useful for agents that want to delegate strategy explanation to a sibling LLM."
    },
    "preview": {
      "description": "HTTP wrapper around the SDK's previewOpenPosition() — same HF + liquidation-price math. Lets non-TS agents (Python, Go, raw curl) get a typed preview without `npm install @recursa-ai/sdk`. Always returns 200 with a typed `kind: ok | unavailable` envelope; HTTP errors only for malformed input or upstream rate-fetch failure.",
      "url": "https://recursa-solana.pages.dev/api/preview",
      "method": "POST",
      "auth": "none",
      "body_schema": {
        "token": "string (required) — collateral token symbol",
        "leverage": "number (required) — 1.01 to 5.0",
        "amount": "number (required) — collateral amount in human units",
        "debt": "string (optional) — debt token; defaults to same-asset for major liquid tokens"
      },
      "supports_idempotency": true
    }
  },
  "sdk": {
    "package": "@recursa-ai/sdk",
    "language": "typescript",
    "registry": "npm",
    "install": "npm install @recursa-ai/sdk @solana/web3.js",
    "version_compatibility": "^0.1.0",
    "repository": "https://github.com/stunt101harm/recursa_solana/tree/main/packages/sdk",
    "examples": [
      {
        "title": "List low-risk SOL strategies",
        "code": "import { createRecursaClient } from '@recursa-ai/sdk';\nconst recursa = createRecursaClient();\nconst top10 = await recursa.listStrategies({ assetCategory: 'sol', maxRisk: 'low', limit: 10 });"
      },
      {
        "title": "Preview a 2.5× SOL loop",
        "code": "const preview = await recursa.previewOpenPosition({ token: 'SOL', leverage: 2.5, amount: 1.0 });\nif (preview.kind === 'ok') console.log(`Net APY ${preview.netAPY.toFixed(2)}%`);"
      },
      {
        "title": "Build an unsigned open_position tx (same-asset loop)",
        "code": "import { Connection } from '@solana/web3.js';\nconst connection = new Connection('https://api.devnet.solana.com');\n// Fetch live Pyth price updates — see /api/post-pyth.\nconst tx = await recursa.buildOpenPositionTx({\n  preview,\n  owner: walletPubkey,\n  connection,\n  pythPriceUpdates: { collateral: collPyth, debt: debtPyth },\n});\n// Caller signs + sends.\nconst bytes = Buffer.from(tx.serializedTransactionBase64, 'base64');"
      },
      {
        "title": "Close an open position",
        "code": "const closeTx = await recursa.buildClosePositionTx({\n  position: expectedPositionPubkey,\n  owner: walletPubkey,\n  connection,\n});\n// SDK reads on-chain Position + ProtocolConfig to derive the mint + treasury ATA for you."
      },
      {
        "title": "Configure auto-close + auto-rebalance guard",
        "code": "await recursa.buildSetGuardTx({\n  position: expectedPositionPubkey,\n  owner: walletPubkey,\n  connection,\n  guard: { enabled: true, minHealthFactor: 1.4, takeProfitBps: 500, autoRebalance: true },\n});"
      },
      {
        "title": "Cross-asset loop (JitoSOL collateral, USDC debt)",
        "code": "// Fetch Jupiter quote + swap instructions server-side; pass through to the SDK.\nconst tx = await recursa.buildOpenPositionTx({\n  preview,\n  owner: walletPubkey,\n  connection,\n  pythPriceUpdates: { collateral: jitoPyth, debt: usdcPyth },\n  crossAssetSwap: {\n    swapProgramId: 'JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4',\n    swapInstructionBytes: base64Decode(swapIx.data),\n    swapAccounts: swapIx.accounts,\n    addressLookupTables: resolvedAlts,\n    otherAmountThresholdNative: BigInt(quote.otherAmountThreshold),\n  },\n});"
      }
    ]
  },
  "idl": {
    "package": "@recursa-ai/idl",
    "language": "typescript",
    "registry": "npm",
    "install": "npm install @recursa-ai/idl",
    "repository": "https://github.com/stunt101harm/recursa_solana/tree/main/packages/idl",
    "description": "Dependency-free Anchor IDL + program IDs + PDA seed helpers + instruction discriminators + borsh arg encoders. Consumers that encode instructions outside @recursa-ai/sdk (Python agents, custom Rust clients, other TS libraries) pull this directly. The IDL JSON is also available at /idl/recursa.json from the repo root."
  },
  "mcp": {
    "package": "@recursa-ai/mcp",
    "transport": "stdio",
    "registry": "npm",
    "install": "npx @recursa-ai/mcp",
    "tools": [
      "list_strategies",
      "preview_open_position",
      "get_rates",
      "find_emissions_arb",
      "explain_intent"
    ],
    "configuration": {
      "claude_desktop": "Add to ~/Library/Application Support/Claude/claude_desktop_config.json: \"recursa\": { \"command\": \"npx\", \"args\": [\"@recursa-ai/mcp\"] }",
      "cursor": "Add to .cursor/mcp.json with the same shape.",
      "raw": "Run `npx @recursa-ai/mcp` and connect any MCP-compatible client over stdio."
    }
  },
  "skill": {
    "format": "anthropic-skill-v1",
    "url": "https://github.com/stunt101harm/recursa_solana/blob/main/docs/SKILLS.md",
    "raw": "https://raw.githubusercontent.com/stunt101harm/recursa_solana/main/docs/SKILLS.md",
    "description": "Drop-in Claude skill describing every Recursa surface, the strategy taxonomy, the rate / preview math, and the safe leverage caps."
  },
  "auth": {
    "anonymous_endpoints": [
      "/api/rates",
      "/api/funding-rates",
      "/api/rpc",
      "/api/ai",
      "/.well-known/agents.json"
    ],
    "agent_tier": {
      "status": "live",
      "description": "Higher rate-limit tier for authenticated agent traffic. Agents sign a canonical timestamped message with their Solana ed25519 keypair and send three headers; the server verifies the signature + applies the agent-tier per-pubkey rate limit bucket. No nonce store — the timestamp window IS the replay protection. Same model as Sign-In With Solana so agent authors implementing one effectively get the other for free.",
      "headers": {
        "X-Recursa-Agent-Pubkey": "Base58-encoded Solana pubkey (32 bytes raw)",
        "X-Recursa-Agent-Sig": "Base58-encoded ed25519 signature (64 bytes raw) over the canonical message",
        "X-Recursa-Agent-Ts": "Unix epoch seconds. Must be within ±300s of server time."
      },
      "canonical_message_format": "recursa:agent-auth:v1:<unix-epoch-seconds>",
      "replay_window_seconds": 300,
      "rate_limits": {
        "anonymous_per_minute": 60,
        "agent_per_minute": 600
      },
      "rate_limit_response_headers": {
        "X-RateLimit-Tier": "anonymous | agent",
        "X-RateLimit-Limit": "Effective limit for this tier",
        "X-RateLimit-Remaining": "Requests remaining in the current window",
        "X-RateLimit-Reset": "Unix seconds when the current window closes",
        "Retry-After": "On 429 responses only"
      },
      "covered_endpoints": [
        "/api/preview"
      ],
      "error_codes": [
        "agent_auth_missing_headers",
        "agent_auth_invalid_pubkey",
        "agent_auth_invalid_signature_format",
        "agent_auth_timestamp_out_of_window",
        "agent_auth_signature_mismatch"
      ]
    }
  },
  "transaction_model": {
    "description": "Recursa never signs. The SDK + MCP both return base64-encoded VersionedTransaction bytes that the caller signs + submits with whatever wallet abstraction they use (Phantom, Solflare, Squads, hardware, agent-custodial). This keeps the agent surface compatible with every Solana wallet pattern.",
    "current_status": "live for same-asset + cross-asset leveraged loops, position close, and guard configuration. @recursa-ai/sdk v0.3 exposes three builders: buildOpenPositionTx, buildClosePositionTx, buildSetGuardTx — each returns a base64 VersionedTransaction ready to sign.",
    "covered_instructions": [
      "create_position (same-asset loops)",
      "create_position (cross-asset loops — caller supplies Jupiter quote + swap instructions)",
      "close_position",
      "set_guard (auto-close + auto-rebalance configuration)"
    ],
    "deferred": [
      "PT (Principal Token) loops — need Exponent's exact preflight that the SDK can't reproduce; redirects to /loop",
      "close_with_unwind — atomic unwind of Kamino / JupLend / Project0 supply + close. Currently two separate steps (redeem, then close)",
      "keeper-only instructions (keeper_close, keeper_rebalance_partial, keeper_close_delta_position) — not end-user surface; keeper integrators import @recursa-ai/idl directly",
      "delta-neutral position builders — planned after the delta-neutral hooks stabilize",
      "compute-budget priority fees — caller adds ComputeBudgetProgram preInstructions themselves when needed (requires Helius API key for best landing)"
    ],
    "deep_link_fallback": "https://recursa-solana.pages.dev/loop?token=<asset>&leverage=<n> when the SDK throws on an unsupported path (PT collateral, close-with-unwind, etc.).",
    "idl_package": "@recursa-ai/idl ships the full Anchor IDL + program IDs + PDA seed helpers + account decoders + instruction discriminators + borsh arg encoders. Depend on it directly if you want to encode instructions outside the SDK (e.g. in a language the SDK doesn't target yet)."
  },
  "intents": {
    "description": "Strategies are classified into 7 intent kinds. Agents typically filter on these.",
    "kinds": [
      "LEVERAGED LOOP",
      "RATE ARB",
      "EMISSIONS ARB",
      "DIRECTIONAL",
      "CARRY",
      "FIXED YIELD",
      "EARN"
    ]
  },
  "safety": {
    "max_leverage": 5,
    "min_health_factor_recommended": 1.4,
    "liquidation_threshold_assumed_in_preview": 0.85,
    "agent_cautions": [
      "Never recommend leverage > 5× — protocol caps + reserve risk make higher unsafe.",
      "Always surface liquidation_price to the user before they sign.",
      "For PT (Principal Token) collateral, the SDK returns 'pt-requires-exponent' rather than a faked price. Direct users to the /loop deep link for the Exponent-signed exact preflight.",
      "Cross-asset loops have additional price-correlation risk — surface the liquidation_price prominently."
    ]
  },
  "idempotency": {
    "status": "live",
    "description": "POST endpoints accept an Idempotency-Key header to prevent double-submission on agent retries. Same-key + same-body within a 60s window returns the cached response with `Idempotency-Status: replayed`. Same-key + DIFFERENT body returns 422 (`idempotency_key_reused_with_different_body`). Missing key bypasses (legacy + UI clients keep working).",
    "header": "Idempotency-Key",
    "key_format": "any opaque string 1-256 chars (UUIDv4 recommended)",
    "ttl_seconds": 60,
    "covered_endpoints": [
      "/api/post-pyth",
      "/api/notifications/subscribe",
      "/api/preview"
    ],
    "response_headers": {
      "Idempotency-Status": "stored | replayed (only present when an Idempotency-Key was sent)",
      "Idempotency-Key": "echoed back so agents can correlate"
    },
    "storage_note": "Currently per-Worker-instance in-memory storage. For cross-instance dedup beyond a few minutes, the planned Cloudflare KV upgrade lifts the TTL to 24h."
  },
  "observability": {
    "status": "live",
    "description": "Server-Sent-Events stream of position lifecycle events (opened, HF-warning, rebalance-fired, liquidation-warning, carry-flipped, closed). Lets agents react without polling individual RPCs. Per-wallet feed, ~25s windows with transparent Last-Event-ID resumption. Public — on-chain state is public anyway.",
    "url": "https://recursa-solana.pages.dev/api/agent-events",
    "method": "GET",
    "auth": "none",
    "query": {
      "wallet": "Required. Base58-encoded Solana pubkey (32-44 chars) to stream events for.",
      "since": "Optional event id to resume from. Browsers using EventSource auto-send Last-Event-ID instead."
    },
    "content_type": "text/event-stream",
    "event_kinds": [
      "position.opened",
      "position.closed",
      "position.hf_warning",
      "position.rebalance_fired",
      "position.liquidation_warning",
      "carry.funding_flip",
      "heartbeat"
    ],
    "event_id_format": "<unix-ms>-<4-hex>. Lexicographic sort == chronological sort within the same digit-count era. Safe to pass straight back as ?since= on reconnect.",
    "stream_lifecycle": "Each connection is capped at ~25s wall-clock. Server closes cleanly with a `: stream-end reconnect-ok` comment; EventSource clients auto-reconnect. Keepalive comments (`: heartbeat <ms>`) fire every ~15s so idle proxies don't drop the connection.",
    "publishers": {
      "keeper": "The Recursa keeper daemon is the primary publisher. It publishes position.hf_warning (HF dropped below 1.05× user's min threshold), position.liquidation_warning (HF < 1.2), position.closed (stop-loss fired), and position.rebalance_fired (auto-deleverage). See docs/OPS_DEPLOY_RUNBOOK.md for keeper integration.",
      "publish_endpoint": "POST /api/agent-events/publish (shared-secret auth via X-Recursa-Keeper-Secret). Agents don't typically hit this — it's for the keeper + future 1st-party publisher integrations."
    },
    "sdk": "recursa.subscribeToEvents({ wallet, onEvent }) in @recursa-ai/sdk handles the reconnect + resumption loop transparently."
  }
}
