Security

Key scope = blast radius

Risk surfaceUse
Backend / edge functionServer API key (server). Keep in env vars / secrets manager.
Browser, native appNever ship a server key. Mint short-lived EUTs from your backend.
Public widgetWebsite API key (browser) + strict allowed-models + low budget cap.

Rotation

  • Rotate keys on staff change, suspected leak, or quarterly.
  • Workspace → API keys → Replace secret issues a new secret for the key. The old secret stops working at once — there is no grace period — so for zero downtime create a second key, move your apps to it, then revoke the first.
  • All key events appear under API keys → Key activity with IP + UA.

EUT (end-user tokens)

When a browser must call odnoga directly:

  1. Backend mints an EUT (short-lived JWT) for the signed-in user, scoped to a workspace + budget + allowed models.
  2. Browser sends Authorization: Bearer <eut>.
  3. odnoga enforces the embedded budget per end-user; cost still rolls up to the workspace.

EUTs cannot pin a version_id on prompts (use label).

Raw capture

Off by default. When on, vendor request and response bodies are stored for the configured TTL. Turn it on for debugging, turn it off when done. Sensitive prompts are visible to anyone with workspace admin.

Backend secrets stay on the backend

Never put a server-scoped API key, or any other privileged backend credential (for example a Supabase service_role key, if your own app runs on Supabase), in browser or mobile code. Browsers get an EUT or a browser-scoped key — nothing else.

PII in prompts

You're responsible for what you send to vendors. Strip PII before the call, or use a vendor whose terms allow it.