Architecture and Security

Oris sits between your AI agents and your payment providers. It verifies identity, enforces policy, screens for safety, and logs every action. This page covers the security architecture that protects your provider credentials and your agents' transactions.

BYOK Architecture

Oris follows a Bring Your Own Keys model. You maintain your own accounts with payment providers such as Pimlico, Circle, Turnkey, or Fireblocks. You register your provider API keys with Oris through the /oris/developers/provider-keys endpoint. Oris encrypts those keys with envelope encryption and stores only ciphertext in the database.

When your agent initiates a payment, Oris decrypts the key in ephemeral memory, calls your provider to execute the transaction, and wipes the plaintext key from the process heap. Oris never holds funds. Oris never maintains provider accounts on your behalf. The payment relationship exists between you and your provider.

Your Agent --> Oris API --> [ KYA Check ] --> [ Policy Engine ] --> [ Safety Screen ]
                                                          |
                                        [ Decrypt Provider Key ] --> Your Provider --> Blockchain
                                                          |
                                              [ Wipe Key + Audit Log ]

Oris provides the orchestration layer: identity, policy, safety, and audit. Your provider executes the on-chain transaction.

Envelope Encryption

Oris uses a two-layer encryption scheme to protect your provider credentials. The outer layer is a key encryption key (KEK) managed by HashiCorp Vault Transit. The inner layer is a per-developer data encryption key (DEK) that encrypts the actual provider keys.

  1. When you register provider keys, Oris generates a fresh AES-256-GCM data encryption key (DEK) for your developer account.
  2. The DEK encrypts your provider API keys. The resulting ciphertext is stored in the database.
  3. HashiCorp Vault Transit encrypts the DEK itself (the KEK operation). The plaintext DEK is destroyed immediately.
  4. Both ciphertexts are stored in the database: the encrypted provider keys and the encrypted DEK.
  5. On every payment request, Vault Transit decrypts the DEK, the DEK decrypts the provider keys, and the plaintext key is used for a single API call to your provider.
Encryption Flow
Provider Key --> AES-256-GCM (DEK) --> Ciphertext --> Database | DEK --> Vault Transit (KEK) --> Encrypted DEK --> Database
Key material never leaves Vault. The KEK exists only inside Vault Transit. Oris sends the DEK to Vault for encryption and decryption through the Transit API. The KEK itself is never exposed to application code.

Ephemeral Memory Management

After every request, Oris wipes the plaintext provider key from memory at the C level. Python's garbage collector does not handle sensitive data deterministically. A deallocated string can persist in heap memory for an unpredictable duration. Oris uses ctypes.memset to overwrite the memory backing the Python string object with null bytes before the reference is released.

The SecureProviderKeys context manager automates this process. Every key is wiped when the request scope exits, regardless of whether the request succeeded or failed.

Python
with SecureProviderKeys(keys_json) as secure: api_key = secure.get("pimlico", "api_key") result = await bundler.submit(api_key, user_op) # Keys wiped from memory at C level
No persistent key storage. Provider keys exist in plaintext only during a single request lifecycle. Between requests, keys exist only as AES-256-GCM ciphertext in the database and as Vault-encrypted DEKs.

Vault Transit

Oris uses HashiCorp Vault Transit as the key encryption key (KEK) layer. The Transit secret engine performs cryptographic operations on data in transit without storing the data. Key material never leaves Vault.

KEK rotation happens inside Vault with zero application downtime. When you rotate the KEK, old DEKs remain decryptable with their original KEK version. New encryption operations use the latest KEK version. This allows key rotation without re-encrypting every developer's stored credentials.

PropertyValue
Algorithmaes256-gcm96
Key derivationPer-developer, per-key-version
RotationZero-downtime, backward-compatible
Key exportDisabled (keys cannot leave Vault)
Audit loggingEvery encrypt/decrypt operation is logged

Authentication

Every API request is signed with Ed25519 asymmetric cryptography. The SDK constructs a canonical request string from the timestamp, nonce, HTTP method, path, and SHA-256 hash of the body. It signs this string with your private_key (the oris_pk_* seed) and sends the signature in the X-Request-Signature header. Oris stores only your public key; private keys never leave your environment.

The server verifies the signature with the developer's public key in constant time. A 30-second nonce window prevents replay attacks. Redis SETNX with a 30-second TTL enforces nonce uniqueness. If the same nonce appears twice within 30 seconds, the second request is rejected.

Canonical Request
canonical = f"{timestamp}.{nonce}.{method}.{path}.{sha256(body)}" signature = ed25519_private_key.sign(canonical.encode()).hex()
HeaderPurpose
AuthorizationDeveloper API key for identification
X-Request-SignatureEd25519 signature for integrity
X-TimestampUnix epoch seconds (30-second tolerance)
X-NonceUnique token (Redis SETNX enforced)

Safety Screening

Every transaction passes through the Veris safety engine before reaching your payment provider. The engine evaluates counterparty risk, sanctions exposure, and transaction patterns. Evaluation completes in under 100 milliseconds for the p95 case.

The architecture is fail-closed. If the safety engine is unavailable or returns an error, Oris blocks the transaction. There is no bypass mechanism. This behavior is enforced at the infrastructure level and cannot be overridden by API parameters or SDK configuration.

Fail-closed by design. If safety screening times out or encounters an internal error, the payment is automatically rejected. The system defaults to blocking when it cannot verify counterparty safety.

Safety screening powered by Veris.

Audit Trail

Every action in Oris is recorded in a SHA-256 hash chain. Each audit entry contains the action type, actor identity, timestamp, request parameters, and the result. Each entry also references the previous entry's hash value. This creates a tamper-evident chain where retroactive modification of any entry breaks the hash sequence from that point forward.

Every 100 entries, Oris writes the latest hash to HashiCorp Vault KV for independent cross-verification. An auditor can compare the chain's computed hash at any checkpoint against the value stored in Vault. If they diverge, the chain has been modified.

FieldDescription
actionOperation type (payment.send, policy.evaluate, agent.register)
actor_idDeveloper or agent UUID
timestampServer-side UTC timestamp
request_hashSHA-256 of the request payload
resultOutcome (success, rejected, error)
prev_hashSHA-256 of the previous audit entry
entry_hashSHA-256 of this entry (includes prev_hash)
Audit Entry Example
{ "action": "payment.send", "actor_id": "agent_8f3a2b...", "timestamp": "2026-03-24T14:30:00Z", "result": "success", "prev_hash": "a1b2c3d4...", "entry_hash": "e5f6g7h8..." }

Request Lifecycle

Every payment request passes through six stages in sequence. If any stage fails, the request is rejected and the audit log records the failure point.

StageOperationLatency (p95)
1Ed25519 signature verification + nonce check< 2ms
2KYA identity verification (agent trust level check)< 5ms
3Spending policy evaluation (Redis-cached rules)< 10ms
4Safety screening (counterparty risk, sanctions)< 100ms
5Provider key decryption + provider API call200ms - 2s
6Audit log entry + hash chain extension< 5ms

Stages 1 through 4 are internal to Oris. Stage 5 latency depends on your provider and the target blockchain. Stage 6 runs asynchronously after the response is returned to the caller.

Support

For architecture or security questions, email developers@fluxa.ventures.