> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rivermarkets.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Security

> How River Markets protects your account, credentials, and data.

This page summarizes how River Markets is designed so that even in worst-case
scenarios, the blast radius stays small.

## API key signing (Ed25519)

API authentication is **asymmetric**. When you create an API key, the server
generates an Ed25519 keypair, stores the **public key**, and returns the
**private key** to you exactly once.

We picked Ed25519 over the alternatives because it signs roughly 30× faster
than RSA, has small (32-byte) keys, signs **deterministically** (no random
number generator required at signing time, which removes a whole class of
implementation bugs), and is hardened against timing and side-channel attacks.

* **The private key never traverses the wire** after creation. Every request is
  signed locally; the server only ever verifies the signature.
* **A full database compromise cannot forge requests.** An attacker with read
  access to our DB sees public keys, which doesn't enable to sign orders.
* **Replay protection.** Ed25519 signatures are deterministic — the same
  request always produces the same signature — so the signature alone is not
  replay-proof. We dedupe each signature in Redis with a 60-second TTL: a
  captured request cannot be resubmitted, even within the clock-skew window.
* **Rotation is in-place.** Rotating a key swaps the underlying material but
  keeps the `key_id` stable : you only update one secret, not your config.

See [Authentication](/api-reference/authentication) for the protocol details.

## Row-Level Security (RLS)

Every table in our database has **Row-Level Security** policies that
scope queries to the authenticated user. RLS is enforced **at the database
layer**, not in application code : so even if a code path forgets to filter
by `user_id`, the DB refuses to return rows that don't belong to the caller.

* Each request runs as a `authenticated` role, never as a
  superuser.
* Subaccounts, orders, fills, positions, balances, credentials, withdrawals,
  all gated by the same identity primitive.

The practical effect: there is no API path, supported or otherwise, that can
return another user's data.

## Exchange credentials

When you add Polymarket or Kalshi credentials to a subaccount, they are
**encrypted with asymmetric encryption** before being stored. The decryption
key lives only in the trading service that needs them at execution time,
they are never in plaintext at rest, never logged, never returned by any
API endpoint.

* Credentials are bound to the subaccount they were added to.
* **Deleting a subaccount deletes its credentials.** No archival, no soft
  delete.
* Credentials are rotated by replacing them; the old version is overwritten,
  not retained.

## Transport

All traffic, REST, WebSocket, and database connections , runs over TLS.
The API and the database are not reachable on the public internet without TLS
termination.

## Database hardening

Beyond RLS, the database instance is locked down at the platform level:

* Dangerous procedural languages (`plpython3u`, `plperlu`, `plsh`) are
  removed and revoked at install time, no path to OS-level execution from a
  SQL injection, even if one existed.
* Function creation is audited via DDL event triggers.
* The service-account role used by the API has `BYPASSRLS` revoked, so it
  cannot accidentally read across users.

## What this means for you

* Treat your API private key like a password: store it in an env var or
  secret manager, never in source control.
* Rotate keys if you suspect exposure, rotation is instant and atomic.
* Exchange credentials added via the dashboard are safer than committing them
  to your own configuration, since we encrypt them and never let them out of
  the trading service.
