Foundational · 2 testnet runs · 0 mainnet

Move value.
Hold none of it.

patala is a sovereign, centreless payment-rail substrate — one interface to move money, fiat or crypto, that any product can vendor and self-host. The platform holds no funds, takes no cut, and no one owns the network. Patala is Sesotho/Setswana for "to pay."

TESTNET TWICE · MAINNET NEVER

293 offline tests pass in the default build and 572 more with every processor feature compiled in. Beyond that, exactly two runs have ever touched a live network — both patala-stellar, both Stellar testnet, both 2026-07-30.

No mainnet. No live merchant account. Read the evidence ladder before you point this at real money.

5rails 293offline tests 2testnet runs 0mainnet runs 0funds held 0GUI
The seam · one interface, five rails Line style and colour both carry the settlement class.
Non-custodial, final Custodial, reversible No class — offline mock

Nothing pools at the joint. patala quotes, charges and verifies every rail above and is never in the line value actually moves on — there is no balance to hold, so the tick where the interface meets each rail is drawn open, not filled.

§ 01The guarantee structural, not promised

Three claims a payment platform normally asks you to take on trust. Here each one is a consequence of the architecture — there is no code path that could break them, because the thing that would break them was never built.

Nothing below is a policy, a term of service or a promise. Each is a thing that was never built, and so cannot be turned on later without you seeing the commit.

  1. 0funds held

    Non-custodial by construction

    No balance table, no payout queue, no ledger of its own. Value moves over the rail directly; patala is the interface, never the account. There is nothing to freeze and nothing to run off with.

  2. 0platform fee

    No cut, no spread, no rent

    You pay the rail its own cost — a processor's fee, a chain's gas — and nothing extra to a middle patala doesn't occupy. No token, no protocol tax, nothing to meter.

  3. 0owners

    Centreless

    No registry to join, no operator to depend on, no network to be admitted to. Vendor the crate into your own product and self-host it — the substrate is the code, not a company.

§ 02Surface area a library and a sidecar · nothing else

There is no GUI, and that is a security property

patala isn't a product you sign into — it's a library and a sidecar. Everything it ships is a crate you vendor, a trait you implement, or a process you run next to your own app.

There is no admin panel to secure, no session to hijack, no dashboard that becomes the thing an attacker — or an outage — actually targets. A payment substrate with no web surface has no login page to phish, no cookie to steal, and nothing to keep available at 3am besides the process you already run.

The whole attack surface is the trait boundary, and that boundary is reviewable in an afternoon.

  • Cratepatala-core plus whichever rail feature you enable
  • Bindingone generated UniFFI surface — Python, Go, Kotlin
  • C ABIsix extern "C" symbols, eleven language packages
  • Sidecarloopback HTTP, token-gated, keys in one process
your host
  • your appno key
  • go serviceno key
  • python workerno key
patala-sidecar The only process that ever holds a signing key.
One key, one process. Every other process in a polyglot stack reaches the substrate over loopback HTTP — none of them ever holds the key, and nothing off this host can reach the sidecar at all.
shell~/patala
$ ls
patala-core/         patala-hyperswitch/   patala-sidecar/
patala-fiat/         patala-py/            patala-solana/
patala-go/                                 patala-stellar/

$ # no server to sign into, no dashboard to click through —
$ # a library and a sidecar, and nothing else in this tree.
§ 03Rails 5 shipped · 2 settlement classes

Fiat and crypto, and never a lie about which one you got

Fiat is custodial and reversible — chargebacks, KYC, T+2 settlement. Crypto is non-custodial and final — wallet-to-wallet, no reversal. patala never blurs which one a consumer is getting: the settlement class lives in the type, not in a flag, and it is the one thing this page gives its own colour.

RailSettlement classWhat it is
Mock
patala-core
None The offline default. Deterministic, no network, no external crypto dependency — what keeps the default build and CI running with no chain and no processor reachable.
Solana
patala-solana
Final SPL-USDC on Solana. Ed25519 — the app's identity key doubles as the wallet key, no mapping table. Ported from an earlier in-house implementation (~1,760 lines).
Stellar
patala-stellar
Final Native USDC (Stellar Asset), Ed25519 (StrKey), built on SDF's own stellar-xdr/stellar-strkey. The cheapest measured fees of the two crypto rails, and the only rail that has ever touched a live network — see § 04.
Hyperswitch
patala-hyperswitch
Reversible A thin HTTP client to a self-hosted Hyperswitch instance, presenting its whole fiat processor set — Stripe, Paystack, Xendit and 100+ connectors — as one rail. Adopted, not rebuilt.
Direct fiat adapters
patala-fiat
Reversible Twenty processors talked to directly, one Cargo feature each, plus the ISO-4217 minor-unit currency table and an always-on offline manual rail.
Adyen · BTCPay · Checkout.com · Coinbase Commerce · Flutterwave · iyzico · LNbits · Mercado Pago · Midtrans · Mollie · OpenNode · PayFast · PayPal · Paystack · PayU · Razorpay · Square · Stripe · Xendit · Yoco
§ 04Evidence highest rung reached: live testnet

What has actually been proved, rung by rung

"Tested" is not one thing. Below, every crate sits on the highest rung it has genuinely reached — and the last rung is drawn empty because nothing in this repo has ever run against mainnet. Nothing here is coloured green for a thing it has not done.

293offline tests, default build
572more with every processor feature on
2runs against a live network — both Stellar testnet
0runs against mainnet, any rail
CrateSettlement classTestsHighest rung reached
patala-coreNone 38 + 3 doctests OFFLINE BY DESIGN
patala-fiatReversible 552 (all features) NOT RUN — NO LIVE MERCHANT ACCOUNT
patala-solanaFinal 56 (+1 gated) + 2 doctests NOT RUN — TESTNET STEP IN README
patala-stellarFinal 84 (+3 gated) + 5 doctests LIVE TESTNET — TWICE, 2026-07-30
patala-hyperswitchReversible 23 NOT RUN — NEEDS A LIVE INSTANCE
patala-py / patala-goNone 11 Rust (20 w/ fiat-all) + 19 Go EXECUTED, CI-ENFORCED
patala-sidecarNone 15 (12 HTTP + 3 unit) EXECUTED — RAIL REGISTRY IS MOCK-ONLY
What "testnet, twice" narrowly means: both runs were patala-stellar only, both on Stellar testnet, both 2026-07-30 — a single-leg USDC-shaped payment confirmed by StellarRail::verify reading it back from Horizon, and separately a 3-instalment recurring schedule whose second instalment was genuinely rejected by real Horizon when resubmitted too early, then accepted once the pacing floor elapsed. Mainnet is untouched, atomic multi-party splits have never run live, the sidecar's rail registry is still mock-only, and every other rail — including Stellar's own mainnet path — says plainly, in its own README, that it hasn't been run live. Anything specified in PATALA.md but not built is marked as such there. See the full status for the transaction hashes and every crate's caveats.
§ 05Consuming it 4 ways in · 15 language packages

One Rust core. Four ways in. Not twenty implementations.

Write every rail adapter once, in the core, and reach it four different ways — or write each rail again in each language and keep twenty copies in step forever. patala is the first shape, and it ships as fifteen language packages over those four ways.

M × Nthe usual shape
M by N: twenty separate rail implementations Four consumer nodes on the left, each connected to all five rail nodes on the right — twenty crossing edges, one per language-and-rail pair.
4 consumers5 rails
20 rail implementations to write, review and keep in sync — and twenty chances for two languages to disagree about what verify means.
M × 1patala
M by 1: one core, five adapters Four consumer nodes on the left all converge on a single core node in the middle, which fans out to five rail nodes on the right — nine edges in total, and each rail implemented exactly once.
4 consumerspatala-core5 rails
5 rail implementations. Python, Go and any HTTP client all dispatch through the same PaymentRail trait, so there is exactly one definition of verify to get right.
01 · Direct

Rust crate

Program against PaymentRail directly. The default build pulls no chain and no processor — opt into a rail with its feature flag.

02 · Generated binding

UniFFI

One #[uniffi::export] surface, generated per language: patala-py (synchronous — no asyncio loop to call charge()), patala-go over cgo, and the Kotlin package, which is the generated code.

03 · C ABI

Six symbols

patala-ffi — JSON in, JSON out, uint64 handles, six extern "C" functions. It is what eleven of the fifteen packages load, and it speaks the sidecar's own JSON.

04 · Loopback HTTP

Sidecar

patala-sidecarquote / charge / verify / webhook as JSON on 127.0.0.1 only, token-gated, fail-closed. Any language, zero FFI.

Whichever you pick, embedding patala puts no language runtime in your process. The core is Rust: no garbage collector to pause your threads, no second scheduler competing with yours, no signal handlers installed behind your back, no fork() hazard, nothing to initialise before your main. The costs that do exist are named rather than buried — a Tokio runtime created lazily inside the bindings so a Python caller never has to run an event loop, and cgo if you choose Go. Choosing a mode lays all four out side by side.

The fifteen · one core, two modes 11 of the fifteen recommend running it inside your own process.
  • rustdirecta Cargo dependency, no FFI
  • cdirectlinks libpatala_ffi
  • cppdirectheader-only RAII
  • swiftdirectdlopen, no module map
  • javadirectFFM, JDK 22+
  • kotlindirectgenerated UniFFI over JNA
  • nodedirectkoffi, with a working callAsync
  • denodirectDeno.dlopen, non-blocking
  • bundirectbun:ffi, synchronous
  • pythondirectpatala-py, over UniFFI
  • rubydirectfiddle, from the stdlib
  • phpdependsthe FFI extension — your php.ini decides
  • dotnetsidecarLibraryImport — but no Windows DLL exists
  • gosidecarpatala-go in-process, or keep a static binary
  • elixirsidecara dirty-IO NIF exists; BEAM reasons

Rust is the one with no boundary at all — a dependency line, no shared library, no unsafe, no platform matrix to ship. And Java and Kotlin default to in-process here, the reverse of the Go-cored products beside it: the same probe reports patala's library replacing 0 of HotSpot's signal handlers where the Go-cored control replaces 5, and the default library measures 844,656 bytes against that control's 12,787,504 — measured on one machine, not inferred from the language. The index that ships with the code is sdks/README.md; Fifteen language packages has a run command for every one.

rustpatala-core/src/lib.rs
// The settlement class lives in the type — never flattened to a bool.
pub enum RailClass { CustodialReversible, NonCustodialFinal }

#[async_trait]
pub trait PaymentRail {
    fn id(&self) -> &str;
    fn capabilities(&self) -> &RailCapabilities;
    async fn quote(&self, req: &PayRequest) -> Result<Quote>;
    async fn charge(&self, req: &PayRequest) -> Result<Receipt>;
    async fn verify(&self, receipt: &Receipt) -> Result<bool>;      // fail-closed
    async fn verify_webhook(&self, d: &WebhookDelivery) -> Result<WebhookEvent>;
}

The seam itself — one trait, six methods. Everything else in this repo is an implementation of it.

pythonpatala-py/examples/smoke_test.py
from patala_py import PatalaRail, PayRequest, RailClass

rail = PatalaRail.new_mock(
    id="mock",
    _class=RailClass.NON_CUSTODIAL_FINAL,
    currencies=["USDC"],
    fee_minor=0,
    failing=False,
)

req = PayRequest(
    amount_minor=1_250,       # int, never a float
    currency="USDC",
    destination="dest-anything",
    reference="order-1",
)

receipt = rail.charge(req)
assert rail.verify(receipt) is True   # fail-closed: a tampered receipt verifies False

The same charge round trip as the Rust above, over UniFFI — synchronous, no asyncio loop.

gopatala-go/examples/roundtrip/main.go
import patala "github.com/vul-os/patala/patala-go/bindings/patala"

rail := patala.PatalaRailNewMock(
    "mock", patala.RailClassNonCustodialFinal, []string{"USDC"}, 0, false,
)

req := patala.PayRequest{
    AmountMinor: 1_250, // uint64, never a float
    Currency:    "USDC",
    Destination: "dest-anything",
    Reference:   "order-1",
}

receipt := must(rail.Charge(req))
valid := must(rail.Verify(receipt)) // fail-closed: a tampered receipt verifies false

The same charge round trip again, over cgo — the same generated UniFFI surface.

bashpatala-sidecar
# No token, no process — there is no unauthenticated-by-default path.
export PATALA_SIDECAR_TOKEN=$(openssl rand -hex 32)
cargo run -p patala-sidecar
# patala-sidecar listening on 127.0.0.1:8420 (loopback only)

curl -s http://127.0.0.1:8420/v1/rails/mock/charge \
  -H "Authorization: Bearer $PATALA_SIDECAR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"amount_minor":1250,"currency":"USDC","destination":"dest","reference":"order-1"}'

# Then POST that receipt to /verify and read `valid`. An unverifiable
# receipt is 200 with {"valid": false} — a fail-closed answer is data,
# never an HTTP error.

The same round trip with no FFI and no generated binding — an HTTP client and a JSON parser are the whole dependency list. It also buys the one thing the in-process paths cannot: a signing key that lives in one process instead of every process that links the core.

tomlCargo.toml
[dependencies]
# SECURITY.md: patala is on no package registry — vendor by path or git.
patala-core    = { git = "https://github.com/vul-os/patala" }
patala-stellar = { git = "https://github.com/vul-os/patala" }  # opt in per rail

Vendoring it: nothing is published on crates.io. Depend by path or by git, and opt into rails one feature at a time.

§ 06Proof 5 captured runs · rung 03 · really executed

Every pane below is real output, captured from this repo

No mockups, no invented JSON. The sidecar actually booted, curl actually hit it, the offline suite actually ran, and the Python and Go bindings actually executed — moments before this page was built. Reproduce it yourself: scripts/capture-transcripts.sh && node scripts/render-shots.mjs.

Terminal: exporting PATALA_SIDECAR_TOKEN and running cargo run -p patala-sidecar, ending with the real log line patala-sidecar listening on 127.0.0.1:8420 (loopback only).
The sidecar — cargo run -p patala-sidecar, actually booting.720px capture — drag the pane sideways.
Terminal: curl round-trips against the running sidecar — healthz returns ok, capabilities, a charge, verify returning valid true, and a 401 when the Authorization header is missing.
Real HTTP round-trips against it: capabilities, charge, verify — and the fail-closed 401 when the token is missing.720px capture — drag the pane sideways.
Terminal: cargo test --workspace running every crate's tests, all reporting test result ok, followed by an awk one-liner that tallies the real total: 293 passed.
cargo test --workspace — the same 293 offline tests rung 02 cites, tallied here by a real awk one-liner reading the log, not typed in by hand. Scroll for the full run.920px capture — drag the pane sideways.
Terminal: the Python smoke test running under a real interpreter — capabilities, quote, charge, verify, tampered-receipt rejection, error mapping and webhook checks, all OK, ending ALL PYTHON SMOKE ASSERTIONS PASSED.
The Python binding, over a real interpreter — same MockRail round-trip, called from patala-py.720px capture — drag the pane sideways.
Terminal: cd patala-go && make test — 19 Go binding tests over real cgo, all PASS, ending go-test-gate: OK with 19 top-level tests passed and 10 required tests present.
The Go binding, over real cgo — the same UniFFI surface, generated for Go. Scroll for all 19 tests.720px capture — drag the pane sideways.

Vendor it. Self-host it.
Read it first.

patala is a library, not a service — there is nothing to sign up for and nobody to ask. Read exactly what has been proved, and what hasn't, before you point it at real money.

MIT / Apache-2.0 · no token, no protocol tax.