Security

Beta

Your money and your keys stay yours.

model.cards is bring-your-own-key by design. Your providers bill you directly, so there is no balance for us to hold and nothing to top up. The most we ever store on your behalf is one encrypted, revocable provider key — and this page is exactly how it is kept.

Bring your own key

We never sit in the money path

BYOK is not just a pricing choice — it is the security posture. Because requests run on your own provider keys, model.cards holds no wallet, no credits and no float that could be lost, frozen or leaked.

No balance, no float

  • Your providers invoice you directly, at your account’s own rates — negotiated discounts and credits apply unchanged.
  • There is no top-up, no stored value and no credit balance held here between requests. There is no wallet in the system to hold one.
  • Per-key spend guardrails cap what a runaway loop can cost you at your provider — a limit on your own spending, not a balance we charge against.

One revocable secret, at most

  • The only credential we store is the provider key you connect, and only for the providers you choose to connect.
  • It is encrypted at rest and used solely to authenticate your own requests to that provider — never shared, never sold, never used to train anything.
  • You can replace or remove it at any time from the Providers page, and removing it deletes the stored key immediately.

Key handling

How a connected provider key is protected

A provider key is a bearer credential, so it is treated like one: encrypted before it touches the database, bound to the row it belongs to, and never returned to the browser once saved.

Encrypted at rest

Keys are encrypted with AES-256-GCM via Web Crypto before they are written. The encryption key is derived from a server-held secret through HKDF-SHA256, and the stored value uses a versioned wire format so the scheme can evolve without guessing at old rows.

Bound to your row

Each ciphertext is sealed to the (account, provider) row it was saved under, using AES-GCM additional authenticated data. A key copied into a different row simply fails to decrypt — so a stolen ciphertext cannot be replayed against another account.

Shown once, never logged

After you save a key, only a masked hint — the first few and last four characters — is ever displayed. The encrypted blob never leaves the server, the plaintext is never returned to the browser again, and keys are never written to logs.

Decrypted only in memory

A stored key is decrypted only on the server, in memory, at the moment a request actually needs it to reach the provider — and a key that no longer decrypts surfaces as an error to re-enter, never a silent fall-through.

Verified on your terms

Saving a key runs a free, authenticated check against the provider so you know it works. You can re-run that check any time with the Test button on a connected provider — the key is decrypted server-side and never exposed.

Revocable, immediately

Replace or remove a key whenever you like. Removal deletes the stored ciphertext on the spot, and deleting your account takes every stored key with it (below).

Data

What we store, and what we don't

The store is deliberately small: the encrypted keys above, and usage metadata for your own analytics. Message content is not part of it.

Usage events, metadata only

Each request records a usage event so you can see what ran and what it cost. That row holds the model and provider, token counts, an estimated cost, latency, status and timestamps — kept for your own usage and spend analytics, not billed by us.

  • No prompt or completion text is stored server-side. The usage row carries metadata about the request, not its content.
  • Cost figures are estimates priced from the catalog. Your provider’s invoice is the authoritative bill.
  • During beta, usage history older than 90 days is pruned when the app restarts — a stopgap until a scheduled job replaces it.

Deleting your account cascades

Closing your account from the dashboard removes your data with it. The account row is the parent of everything you own, and the schema deletes children on cascade:

  • Your API keys.
  • Your stored (encrypted) provider keys.
  • Your usage history.
  • Your sessions.

Live requests running on your keys start failing immediately, and the deletion cannot be undone. What we retain and for how long is set out in the privacy policy.

Deployment

Where it runs, and what beta means

An honest account of the current architecture. It is a small, single-tenant-per-instance deployment, and we would rather you know its shape than infer it.

A single Durable Object

  • The app runs inside a single Cloudflare SQLite-backed Durable Object — one instance the requests are forwarded to, with the database co-located rather than spread across nodes.
  • The secrets that sign your sessions and encrypt your provider keys are held as Cloudflare Worker secrets, set out of band and never committed to the repository.
  • Rotating the encryption secret forces stored keys to be re-entered rather than silently trusting an unverifiable ciphertext — a fail-closed choice on purpose.

Beta, stated plainly

model.cards is in beta. There is no uptime commitment and no SLA, features can change, and rate limits are fair-use rather than published quotas. We would rather under-promise here than dress the beta up as something it is not.

The full terms, including the beta section and the limits of our liability, live in the terms of service.

Found something, or have a question?

Security reports and questions about anything on this page are welcome. Email us and we will get back to you — please include enough detail to reproduce anything you have found.

support@model.cardsEmail security