Skip to content

Architecture ​

OpenRampKit has three runtime parts: the browser UI, your OpenRampKit server, and your app backend. Providers sit behind the server. An adapter wraps each provider. A session store holds the state. Chains are the last step for crypto money.

This page shows the components, the trust boundaries, what runs where, the data model and the package map. For the step-by-step flows, read Flows.

Components ​

  • The browser renders the modal. It knows only the client secret of one session.
  • Your app backend knows the user. It creates sessions and receives signed webhooks.
  • The OpenRampKit server holds provider keys. It plans, quotes and runs legs. It is one web-standard handler.
  • The store keeps sessions (with their webhook outbox), small key-value data (provider reference index, idempotency records, rate counters) and two work queues for the sweep.
  • Providers move the money. They send webhooks back to the server.
  • Chains hold the final crypto. Some adapters read chains directly over JSON-RPC to verify a payment.

Trust boundaries ​

BoundaryWhat crosses itHow the server protects it
Browser to serverThe client secret, method, amount, quote id, transition nameThe secret is hashed in the store. The browser cannot set the user, the destination or the address. Routes check the body, the step, the rate and the deadline.
App backend to serverCreateSessionInputsessions.create() runs in your code. Browser-created sessions need your authorize hook.
Server to app backendSigned webhook eventsHMAC-SHA256 over id.timestamp.body with webhooks.secret. Your backend checks it with openramp.webhooks.verify().
Provider to serverProvider webhooksEach adapter verifies the signature (webhook.verify) before webhook.parse. An event for an unknown reference gets 503, so the provider sends it again.
Server to chainJSON-RPC readsThe server never trusts a transaction hash from the browser alone. It checks the receipt, the amount, the recipient and the age, or the settlement receipt.
Provider page to modalpostMessage events from an IFRAME surfaceThe modal checks the exact origin and the source window. A message only triggers a status check. See Iframe messages.

The Security guide lists every check.

What runs where ​

PartRuns inKnowsDoes not know
web, client, framework wrappersThe browserThe client secret, the plan, quotes, the current stepProvider keys, the user id, how to reach providers
wagmi, solanaThe browserThe connected accounts. It signs transactions.Sessions and providers
serverYour server or edge workerProvider keys, sessions, the destination, adaptersYour users and balances
adapter-*Inside the serverOne provider's APIOther adapters, HTTP routing, storage layout
coreEverywhereTypes, the flow table, the planner, money mathNetwork, clock (planner), DOM
Your app backendYour serverUsers, auth, destinations, balancesProvider details
mcpA separate process or your serverAn app key or an in-process OpenRampProvider keys (in HTTP mode)
OpenRampSettlementA chainSettlement receipts per session idAnything off chain

Data model ​

  • Session (SessionRecord in the store, PublicSession on the wire). One deposit or one withdrawal for one user. It has a secret hash, a direction, a destination (deposit) or a source (withdraw), and a deadline. The server saves it with an optimistic version check.
  • Plan (PlanResult). The methods and the pathways that the planner found for this session. The planner is pure. See Pathways and legs.
  • Pathway. One or two legs, for example an onramp to USDC on Base, then a Relay bridge.
  • Quote. The price of one pathway for one amount. It has one LegQuote per leg. The server keeps at most 20 quotes per session.
  • Active payment (active). The pathway the user confirmed, the index of the current leg, and one ActiveLeg per leg. Each leg has a ref, the latest LegStep from its adapter (merged with the earlier steps, so it keeps its refs, output and transactions), and the delivery check of its output. The browser and your backend see it as PublicSession.payment.
  • Step. What the browser shows now. The server builds it from the current leg. See Flow state machine.
  • Result (PublicSession.result). What the user paid and what arrived, with fees, transactions (each with a role) and the delivery check.

Key-value records next to the sessions:

KeyValueWritten by
ref:{adapterId}:{ref}The session id that owns a provider reference (30 days)setLegStep when a leg gets a ref. Provider webhooks use it to find the session.
idem:{sessionId}:{route}:{key}The stored response of a request with an Idempotency-Key (24 hours)select and transitions/:name
rl:{sessionId}:{minute}A request counterRoutes that call provider APIs
Queue outbox (see Queues)Ids of sessions with webhook events to send. The events are in the session record (rec.outbox).saveSession(), then sweep()
Queue open-sessionsIds of sessions that are not finalsessions.create(), restart, provider events, then sweep()
a:{adapterId}:...Adapter data (per session, or shared)Adapters, through ctx.store and ctx.shared

The packages ​

PackageUse
@openrampkit/coreShared types, decimal money math, error codes, region policy, the flow table, the planner and quote ranking
@openrampkit/adaptercreateAdapter(), HTTP helpers, EVM helpers, OpenRampSettlement helpers, and the /testing conformance kit
@openrampkit/adapter-*One package per provider. See Adapters.
@openrampkit/servercreateOpenRamp(): the HTTP handler, sessions, planning, legs, webhooks in and out, sweep, pay links, stores
@openrampkit/clientThe HTTP client, DepositController (RampController), createMockWallet
@openrampkit/web<openramp-modal>, openDeposit(), openWithdraw(), themes, locale catalogs, provider renderers
@openrampkit/react, vue, svelte, solidThin wrappers over web
@openrampkit/wagmiwagmiWallet(): a WalletAdapter for EVM wallets
@openrampkit/solanasolanaWallet(): a WalletAdapter for Solana wallets (Wallet Standard)
@openrampkit/mcpAn MCP server so that AI agents can create deposits and payouts. See Agents.
contracts/OpenRampSettlement, a Foundry project. See On-chain settlement.

core is pure: no network, no clock in the planner, no DOM. The server, the client and the adapter test kit read the same flow table from it.

One deposit, end to end ​

Design choices ​

  • Web standards only. The server uses fetch, Request, Response and WebCrypto. The same code runs on Cloudflare Workers, Vercel, Node 20+, Bun and Deno.
  • Server-driven UI. The modal renders whatever Step the server returns. A new provider needs no UI release, as long as it uses a known surface.
  • State in a store, not in memory. Each request loads the session, changes it, and saves it with an optimistic version check. Any instance can serve any request. See Session stores.
  • Adapters are data plus functions. Static LegSpec declarations feed the planner. quote, start, transition, status and webhook run the leg.
  • The server is the source of truth. A redirect return, an iframe message or a wallet hash only starts a status check. The outcome comes from the provider or the chain.

For the reasons behind these choices, read Why OpenRampKit?.

MIT licensed