orvacon
Security

Verify it. Don't just trust it.

orvacon keeps money out of its own path, signs every event with asymmetric keys, and treats the database as hostile by default. Here's exactly how each flow is secured — drawn out, not asserted.

No money custody/Ed25519 webhooks/Default-deny RLS/Hash-chained ledger
01 — Custody boundary

Money never flows through orvacon.

Funds move on the cardyour gateway account path. orvacon only ever sits on the control path — coordinating the call, persisting state, signing events. It holds no balance and never custodies money.

fig.01 · payment custody
Cardholder
Your application
orvacon orchestratorcontrol only · no funds
Your gateway account
funds · cardgateway
orva.authorize()
control API

Why it matters: because the money path never touches orvacon, it falls outside money-transmitter regulation and stays off your PCI-custody scope. A compromise of the orchestrator cannot move or divert funds — there's no balance to move.

02 — Webhook integrity

A leaked key can't forge an event.

Outbound webhooks are signed with an Ed25519 private key that never leaves orvacon; your endpoint verifies with the public key. A shared secret can be replayed by anyone who reads it — an asymmetric signature can't.

fig.02 · asymmetric webhooks
orvaconsign · private key
signed eventpayload + sig
your endpointverify · public key
forged event
emit
verify()
no private key

Ed25519

RFC 8032
EdDSA · Curve25519 · SHA-512
Public key
32 bytes
Signature
64 bytes · R ∥ S
Nonce
deterministic
Security
≈128-bit
sign(privateKey, event) // in orvacon
64-byte signature
verify(publicKey, event, sig) // in your app
true | false

Constant-time comparison

Every signature and token is compared in constant time — an attacker can't recover it byte-by-byte by timing how fast verification fails.

sig === expected
timingSafeEqual(sig, expected)

Both Iyzico's and PayTR's official samples compare with ==. orvacon never does.

Two signature worlds, never crossed

inbound · gatewayorvacon
Verify the gateway's own MAC.
IyzicoHMAC-SHA256 · hex
PayTRHMAC-SHA256 · base64
symmetric · shared merchant secret
outbound · orvaconyou
Sign with Ed25519.
signprivate key · in orvacon
verifypublic key · in your app
asymmetric · the public key can't forge
03 — 3-D Secure trust

The callback POST is never trusted.

After the 3DS challenge the browser POSTs back to your callback — but that request is attacker-controllable. orvacon ignores its body and re-reads the result from a signed finalize call to the gateway before committing.

fig.03 · 3-d secure finalize
authorize()cardgateway
requires_actionstate persisted
3DS challengein the browser
finalizesignature verified
browser callback POSTnever trusted
04 — Defaults

Secure by default, not by checklist.

The safe choice is the one you get without configuring anything.

Default-deny RLS

The Supabase adapter generates a schema with row-level security on and everything denied until you grant it.

Hash-chained ledger

Every entry references the prior hash. The ledger is append-only and tamper-evident — edits break the chain.

Idempotency enforced

A database unique constraint — not application logic — guarantees a retried request can't double-charge.

No card data stored

Raw card details go straight to the gateway. orvacon persists references and state — never a PAN.

Secrets via environment

API keys and signing keys are read from the environment, never committed and never written to the database.

Runs in your runtime

No orvacon-hosted service sees your traffic. There is no third party in the request path to trust or breach.

05 — Responsible disclosure

Found something? Tell us privately.

orvacon is v0.3 and the API is still 0.x. If you find a vulnerability, please report it through the security policy rather than a public issue — we'll coordinate a fix and disclosure.

rotate signing keys
$ npx orvacon keys   # generates an Ed25519 keypair
# prints ORVACON_WEBHOOK_SIGNING_KEY=…
# set it in your environment, never commit it

Rotating a key is config-only — no redeploy of application logic. The public verification key can be distributed freely.

Verifiable by design

Read the code,
not the promises.

Every boundary on this page is enforced in open source. Audit it, run it in your own runtime, and verify it yourself.