The Swift SDK for the Machine Payments Protocol (MPP): pay for, and charge for, machine-to-machine API calls over HTTP 402.
MPP standardizes machine-to-machine payments using a challenge → credential → receipt flow: a server answers 402 Payment Required with a WWW-Authenticate: Payment challenge, the client replies with an Authorization: Payment credential, and the server returns the resource plus a Payment-Receipt. It works over HTTP, JSON-RPC/MCP, and WebSocket, with pluggable payment rails (Tempo, Stripe, and more).
This is the canonical Swift implementation of the protocol. The client and server are independent: a Swift client can pay any MPP server in any language, and a Swift server can charge any client.
An auto-generated code wiki is browsable at DeepWiki for a quick tour of the codebase. It is regenerated periodically, so it can lag the newest modules; this README and ARCHITECTURE.md are the authoritative, current references.
Status: pre-1.0 (the package is
0.0.x; no 1.0 tag yet) but broad, built one module at a time with a strict quality bar (spec-traced tests, cross-SDK conformance vs the reference SDKs, no flaky tests). The protocol core, body-digest, server middleware, the 402 client flow, and the EVM message-signing layer (Keccak-256, secp256k1, EIP-712 proof + session voucher) are implemented. The Tempo rail is well advanced: the zero-amount proof charge, the session-channel server (open/topUp/voucher/close), the blob-free on-chain layer (a JSON-RPC client, the escrowgetChannelread, and an RPC-backed channel-state provider), and the bespoke0x76transaction builder (open/topUp/close, an opt-in Rust FFI to Tempo's owntempo-primitives, linking on macOS/iOS/Linux) are all in. The full write path is proven on-chain: a gated end-to-end test opens and closes a real payment channel against the live Moderato testnet. The 402 channel-payment client (TempoChannelMethod: open/voucher/topUp/close, an injected deposit policy, on-chain channel recovery, and a separate access-key signer) is in, and the whole channel rail is cross-SDK conformance-proven against the reference SDK in both directions, live on Moderato (open → voucher → close, settled on-chain). Discovery (parse + generate + validate), the 402-protected reverse proxy (MPPProxy, with a live Hummingbird bindingMPPHummingbird), and the JSON-RPC / MCP binding (MPPMCP, cross-SDK conformance-proven both directions over stdio) are in. The subscription rail is in, end to end: a signed Tempo key authorization (TempoKeyAuthorization, RLP, byte-verified against the chain's own codec), the client (TempoSubscriptionMethod) and server (TempoSubscriptionVerifier, which re-encodes the expected authorization and compares so it never trusts the client decode), aSubscriptionStorewith a two-phase atomic renewal engine, and a concrete on-chain renewer (TempoSubscriptionRenewerover anAccessKeyStore+ the0x76charge builder) that settles the recurringtransferWithMemolive, with fee-payer gas sponsorship so the charge fits the access key's spending limit. It is cross-SDK conformance-proven against the reference SDK in both directions (activation offline, and a recurring charge settled live on Moderato). Metered streaming is in too: a pay-as-you-goSessionStreamthat meters chunks against a channel over both Server-Sent Events and WebSocket frames, with cross-SDK codec parity for both, plus a live WebSocket transport (MPPWebSocketorchestration +MPPWebSocketLiveswift-websocket adapters, server and client) that drives a metered session over a real socket. The Stripe rail is in (MPPStripeclient +MPPStripeServer: a Shared Payment Token charge into a PaymentIntent, full Connect parity, live-verified against a real test-mode charge). And a shippedmppcommand-line tool (paya 402 URL curl-style, plussign/discover/account/services/init) rides a vendor-neutralPaymentAuthorizerapproval seam (MPPAuth: per-request Touch ID, a TTY prompt, or headless auto with a spending cap). Remaining for 1.0: server hardening (idempotency + rate limiting), an HTML payment page, and conformance-vector completeness. See what's covered and the module table below.
MPP composes along three axes: the transport the 402 flow rides, the rail that moves value, and the side you are (payer/client vs payee/server). Client and server are independent products, so a Swift client can pay any-language server and a Swift server can charge any client. Here is the surface:
Transports (the wire the challenge / credential / receipt flow rides):
- HTTP -
MPPServermiddleware +MPPClient(overURLSessionTransport); a live server binding (MPPHummingbird) and a 402-protected reverse proxy (MPPProxy). - JSON-RPC / MCP -
MPPMCPgates atools/calland pays it transparently; conformance-proven both directions over stdio. - WebSocket + SSE - metered, pay-as-you-go streaming with in-band voucher top-up:
MPPWebSocket(transport-agnostic session orchestration, server + client) +MPPWebSocketLive(live swift-websocket adapters), plus the Server-Sent Events codec.
Rails (how value actually settles):
- Tempo (EVM) -
MPPTempo/MPPTempoServer: a zero-amount proof-of-wallet-control charge; a full payment-channel session (open / voucher / topUp / close, settled on-chain); a subscription rail (a signed key authorization, a two-phase atomic renewal engine, and a concrete on-chain renewer with fee-payer gas sponsorship); and metered streaming over the channel. The0x76channel / charge transactions are built by a pinned Rust FFI (MPPTempoFFI), so only a wallet links it. - Stripe -
MPPStripe/MPPStripeServer: a Shared Payment Token charge into a PaymentIntent, full Stripe Connect parity, live-verified.
Tooling:
mpp- a shipped command-line tool:paya 402 URL (curl-like),signa challenge,discover/validate,account(macOS Keychain),services,init. Approval rides a vendor-neutralPaymentAuthorizerseam (MPPAuth): per-request Touch ID, a TTY prompt, or headless auto with a spending cap.MPPDiscovery- OpenAPIx-payment-infodiscovery: parse, generate, validate.
Cross-SDK conformance: every rail and transport with a reference peer is proven against the reference TypeScript SDK (mppx) in both directions (our client against their server, and their client against our server), offline in CI and, for the on-chain Tempo paths, live on the Moderato testnet. See CONFORMANCE.md.
Swift Package Manager:
.package(url: "https://github.com/amitach/mpp-swift", from: "0.0.1")Then depend on the products you need (you pull only those):
.product(name: "MPPCore", package: "mpp-swift"),A consumer that builds Tempo channel transactions (a wallet) additionally depends on
MPPTempoFFI; on Apple this downloads the checksum-pinned tempo-tx-ffi xcframework (no
Rust toolchain). Every other product links zero Rust.
| Product | Status | Purpose |
|---|---|---|
MPPCore |
available | Protocol primitives: Challenge, Credential, JCS, Amount, ProblemDetails, RouteBinding, multi-challenge parsing |
MPPBodyDigest |
available | RFC 9530 Content-Digest (SHA-256) |
MPPServer |
available | Framework-agnostic middleware over swift-http-types: challenge mint, replay, verify pipeline |
MPPClient |
available | The 402 client flow (send → parse → select → build → retry) over URLSessionTransport (with redirect-downgrade protection); plus the vendor-neutral PaymentAuthorizer consent seam (the protocol + AllowAllAuthorizer + SpendingCapAuthorizer) consulted once at the credential-build chokepoint, shared by the HTTP and MCP clients |
MPPAuth |
available | The interactive/headless payment-approval implementations + the macOS key store, used by the mpp CLI: TTYPaymentAuthorizer (y/n prompt) and BiometricPaymentAuthorizer (LocalAuthentication Touch ID), the displaySafe terminal sanitizer for server-controlled strings, and KeychainAccountStore (biometric-gated secp256k1 key items) |
MPPEVM |
available | EVM message-signing + JSON-RPC: Keccak-256, secp256k1 recoverable signer, EIP-712 zero-amount proof + did:pkh source, session voucher, channel id, 0x-hex codec; plus TempoKeyAuthorization (the subscription key authorization) and a strict-canonical pure-Swift RLP codec, byte-verified against the chain's own tempo-primitives codec |
MPPTempo |
available | Tempo rail: zero-amount proof charge; the 402 channel-payment client TempoChannelMethod (auto open + accumulating vouchers, client-initiated topUp/close, an injected deposit policy, on-chain channel recovery, optional access-key signer) behind the TempoOpenTxBuilder / TempoTopUpTxBuilder / TempoCloseTxBuilder seams; the EVMRPC JSON-RPC client; the escrow getChannel read (TempoEscrow → OnChainChannel); ChannelAmount. The 0x76 builders are the rust/tempo-tx-ffi FFI (below); the TempoChannelSession actor (in MPPTempoFFI) is the direct-wallet lifecycle. Also the subscription client TempoSubscriptionMethod (decodes the tempo/subscription 402, signs the TempoKeyAuthorization delegating the server-issued access key to the recurring transferWithMemo) and SubscriptionRequest. The 0x76 subscription-charge builder (and its optional fee-payer gas sponsor) lives in MPPTempoFFI. Cross-SDK conformance-proven both directions, live |
MPPTempoServer |
available | Tempo SERVER side: zero-amount proof verify, the 4-action SessionMethod (open/topUp/voucher/close), ChannelStore, and RPCChannelStateProvider (reads + relays signed txs + settle via the seam). A session reuses one challenge across its lifecycle, so the verifier honours PaymentMethodServer.reusesChallenge (the channel-store cumulative is the anti-replay). Plus the subscription server TempoSubscriptionVerifier (re-encode-and-compare: rebuilds the expected key authorization from its own challenge and requires byte-equality, never trusting the client decode), the SubscriptionStore + two-phase atomic renewal SubscriptionEngine (claim → charge via an injected SubscriptionRenewer seam → commit, with timeout takeover and supersession), the concrete on-chain renewer TempoSubscriptionRenewer (settles the recurring transferWithMemo via the 0x76 charge builder, signed by the server-held access key from the AccessKeyStore as a Tempo V2 keychain signature, with an idempotency Attribution memo and optional fee-payer gas sponsorship; the first charge attaches the key authorization to provision the access key on-chain), and metered streaming: SessionStream (pay-as-you-go: each chunk draws one tick, emits payment-need-voucher when short, then the terminal receipt) with the SSE codec SessionStreamEvent and the WebSocket frame codec SessionWebSocketFrame |
MPPTempoFFI |
available (opt-in) | The Rust 0x76 transaction builders over an FFI boundary: FFITempoTxBuilder (channel open/topUp/close) and FFISubscriptionChargeTxBuilder (the recurring transferWithMemo, with an optional fee-payer gas sponsor), plus the TempoChannelSession direct-wallet lifecycle actor. Only a transaction-building consumer (a wallet) links it; on Apple a checksum-pinned tempo-tx-ffi xcframework (no Rust toolchain), Linux builds from source. See below |
MPPDiscovery |
available | OpenAPI x-payment-info discovery: parse, generate an OpenAPI 3.1 document from routes (auto-declaring the required 402), and structural + semantic validate (a payable op MUST have a 402, SHOULD have a requestBody); served at /openapi.json |
MPPMCP |
available | JSON-RPC / Model Context Protocol payment binding (over the official MCP Swift SDK): a server gate (tools/call → 402 challenge / verify / receipt) and a client wrapper that pays transparently. Cross-SDK conformance-proven both directions over stdio. Requires Swift 6.2 (the MCP SDK's transitive graph) |
MPPProxy |
available | Framework-neutral 402-protected reverse proxy: routes /{service}/... to upstream origins, gates each route with MPPServerMiddleware, scrubs credential/cookie/hop-by-hop headers in both directions, injects upstream auth, and serves /openapi.json + /llms.txt. Pure request/response logic over swift-http-types; no server dependency |
MPPHummingbird |
available | The live HTTP-server binding for MPPProxy (and single gated routes), over Hummingbird 2. The only product that links swift-nio; requires macOS 14 / iOS 17 |
MPPStripe |
available | Stripe rail CLIENT: presents a Shared Payment Token in the credential (no Stripe secret, no crypto) |
MPPStripeServer |
available | Stripe rail SERVER: StripeChargeVerifier mints a PaymentIntent from the SPT via StripePaymentIntentClient (Basic auth, hashed Idempotency-Key, no secret leak), maps status → receipt/reject; full Stripe Connect parity. Live-verified against a real test-mode PaymentIntent |
MPPWebSocket |
available | Metered-session WebSocket transport, transport-agnostic: SessionWebSocketServer (drives SessionStream over a socket; actor-serialized) + SessionWebSocketClient (consumes it, auto-answers payment-need-voucher with a fresh voucher), over the {mpp:...} SessionWebSocketFrame codec. No external deps |
MPPWebSocketLive |
available | The live socket adapters for MPPWebSocket, on hummingbird's swift-websocket (WSCore inbound/outbound + WSClient); bridges a real WebSocket into the orchestration and confines the nio WebSocket graph to this target |
mpp (executable) |
available | The shipped command-line tool: pay a 402 URL (curl-like), sign a challenge, discover generate/validate, account (macOS Keychain), services, init. swift-argument-parser; curl-like exit codes; approval via the MPPAuth seam |
The bespoke Tempo 0x76 transaction (channel open/topUp/close) is the one piece we deliberately do not build in Swift. Swift could encode it, but the format is Tempo-specific and evolving, so binding Tempo's own tempo-primitives (the chain's canonical implementation) gives byte-for-byte parity and turns an upgrade into a version bump, instead of a drift-prone Swift port we would have to chase by hand. It is produced by rust/tempo-tx-ffi and exposed to Swift over an FFI boundary. It is only needed by a consumer that builds Tempo channel transactions (a wallet): a non-Tempo consumer, or a Tempo server that only verifies and reads, should link none of it. It builds the three channel-bookend transactions (open / topUp / close) and the recurring subscription charge (a transferWithMemo, signed by an access key as a Tempo V2 keychain signature, with an optional fee-payer gas sponsor), and is wired into the Swift package behind the dedicated opt-in MPPTempoFFI product (the FFITempoTxBuilder and FFISubscriptionChargeTxBuilder), so only a transaction-building consumer pulls the binary and the default graph links zero Rust (see ARCHITECTURE.md). It links on macOS (universal), iOS (device + simulator), and Linux. It is a build input that ships in the artifact (unlike the dev-only npm test tooling); its dependency tree is pinned (Cargo.lock), built in CI on macOS and Linux, byte-golden-tested, and cargo audit-scanned. Its output is proven on-chain: a gated live Moderato e2e opens and closes a real channel through it. It is published as a checksummed GitHub-release xcframework (.github/workflows/release-ffi.yml, tag tempo-tx-ffi-v*), so an external Apple consumer installs it with no env var and no Rust toolchain. The from-source MPP_TEMPO_FFI path remains for dev/CI, and a Linux consumer always builds from source (SwiftPM has no Linux library binary artifact). See SECURITY.md.
Prerequisites: a Swift 6.2 toolchain (Xcode 26+ on macOS, or the Swift 6.2 toolchain on Linux). The 6.2 floor comes from MPPMCP's dependency on the official MCP Swift SDK, whose transitive graph (swift-log 1.13) requires tools 6.2. The Rust FFI crate additionally needs a Rust toolchain >= 1.93 (rustup update stable), but only if you build that crate - the Swift package builds and tests with no Rust.
git clone https://github.com/amitach/mpp-swift && cd mpp-swift
swift build # builds all products
swift test # runs the full suite (hermetic; no network)Both must pass on macOS and Linux. Lint matches CI:
swiftformat --lint . # formatting
swiftlint --strict # style; zero warningsCross-SDK conformance (exercises the wire format against the reference TypeScript SDK; needs Node):
Scripts/conformance/run.sh # offline, vs a local mppx server
Scripts/conformance/run.sh --testnet # against the live Moderato nodeLive-chain tests (read path against the real Moderato testnet) are gated so the default suite stays hermetic; opt in with an env var:
MPP_MODERATO_E2E=1 swift test --filter ModeratoThe Rust FFI crate (the 0x76 transaction builder) is separate from the Swift package:
cd rust/tempo-tx-ffi
cargo test # builds the crate (clones tempo + compiles the tree) and runs the byte-golden test
cargo audit # scans the Rust dependency tree against the RustSec advisory DBCI runs all of the above; the Swift Tests jobs and the Rust FFI job both run on macOS and Linux. See the architecture overview for how the pieces fit and SECURITY.md for the supply-chain posture.
- Client and server are separate products: depend on one without the other.
- Spec is the source of truth. The SDK defaults to the published MPP drafts; where an interoperating peer diverges, the divergence is handled explicitly via a compatibility configuration, never silently inherited.
- Conventions follow the Swift ecosystem: Apple's API Design Guidelines and the
swift-*package norms.
Dual-licensed under either of Apache License 2.0 or MIT license at your option.
This product includes software developed by Marcin Krzyzanowski (https://krzyzanowskim.com/), the CryptoSwift library, used for Keccak-256.
Security policy: see SECURITY.md. Contributing: see CONTRIBUTING.md.