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

# Security

> How Vizkraft embeds stay multi-tenant safe—API keys on your backend, liquid tags on every query, and supporting controls for framing, sessions, and audit.

Embeds are built for B2B products that show **one** Vizkraft dashboard to **many** end customers. Security is not a single switch: it is two primary layers plus supporting controls.

Open the app: [app.vizkraft.com](https://app.vizkraft.com) · Product: [vizkraft.com](https://vizkraft.com)

## The two primary layers

### 1. API key (who may mint sessions)

* Format: `vk_embed_…`
* Lives **only** on your backend (env / secrets manager)
* Used solely to call `POST /api/embed/session`
* Never shipped to the browser, mobile app, or `@vizkraft/embed`

Anything in the browser is visible in DevTools. If tenant C1 had your API key, they could mint a session for tenant C2. The safe pattern is: API key on your server → short-lived session stamped with C1 → SDK uses only that session token.

### 2. Liquid tags (what data the session may see)

* Named placeholders you define in Vizkraft (e.g. `customer_id`, `department_id`)
* Your backend supplies values at mint time from **your** auth
* Vizkraft binds those claims into the session and applies them **server-side** to every chart query (direct column or foreign-key join)
* End-user filters in the iframe can only narrow further—they cannot widen past liquid scope

Mandatory tags must be present at mint or the request fails. Optional tags, if omitted, leave that dimension unscoped for that session—configure mandatory tags for true tenant isolation.

## End-to-end security flow

```mermaid theme={null}
sequenceDiagram
  participant EndUser as End_user_C1
  participant HostApp as Host_app_frontend
  participant HostApi as Host_backend
  participant Vizkraft as Vizkraft_API
  participant Viewer as Embed_iframe

  EndUser->>HostApp: Open analytics page
  HostApp->>HostApi: Request session no API key
  Note over HostApi: Resolve tenant C1 from your auth
  HostApi->>Vizkraft: POST /api/embed/session Bearer vk_embed liquid customer_id=C1
  Vizkraft-->>HostApi: Short-lived sessionToken
  HostApi-->>HostApp: sessionToken only
  HostApp->>Viewer: Mount SDK iframe with token
  Viewer->>Vizkraft: Bootstrap and query with session
  Note over Vizkraft: Liquid claims applied server-side to SQL
  Vizkraft-->>Viewer: Tenant-scoped chart data only
```

## Supporting controls

| Control              | What it does                                                                                                   |
| -------------------- | -------------------------------------------------------------------------------------------------------------- |
| Allowed origins      | Only listed hosts may iframe the embed (`frame-ancestors` CSP). Supports exact origins, `*.domain.com`, or `*` |
| Short-lived sessions | Session JWTs expire (\~5 minutes); server rows can be revoked                                                  |
| Uncovered charts     | Charts without a confirmed liquid mapping are **hidden by default**                                            |
| Rate limits          | Mint, query, and download endpoints are rate-limited                                                           |
| Audit log            | Mint, query, deny, and admin events with tenant fingerprints (not raw PII dumps)                               |
| postMessage          | Host ↔ iframe messages check origins; do not use `*` as a message target                                       |
| Public chart errors  | Embed/share viewers show friendly errors—not SQL or stack traces                                               |

## Threat model (short)

| Scenario                                | Outcome                                                                                                        |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| Attacker steals a browser session token | Token is short-lived and scoped to minted liquid claims; rotate / wait for expiry; revoke if needed            |
| Attacker steals the API key             | They can mint arbitrary liquid values—**rotate or delete the key immediately**; treat like a production secret |
| Mint omits a mandatory liquid tag       | Request rejected (`missing_liquid`)—no session, no data                                                        |
| Mint omits an optional tag              | Session succeeds but that filter is not applied—use mandatory tags for hard isolation                          |
| Hostile site tries to iframe your embed | Blocked unless their origin is allowlisted (or you set `*`)                                                    |
| End user tampers with filters in the UI | Filters AND with liquid scope; they cannot remove liquid RLS                                                   |
| Chart query fails in embed              | End user sees a friendly empty state; technical detail stays in Vizkraft admin / server logs                   |

## What product managers configure

1. Enable embedding and set allowed origins
2. Add liquid tags (prefer **mandatory** for tenant keys)
3. Confirm per-chart coverage (Direct or Via FK)—see [Liquid tags and coverage](/embed/liquid-and-coverage)
4. Create an API key; give it to engineering **out of band** (never in a public ticket)
5. Review audit for unexpected mints

Walkthrough: [Admin setup](/embed/admin-setup)

## What engineers must do

1. Store the API key on the server only
2. Mint with liquid values from your auth—never trust the browser for tenant id
3. Mount `@vizkraft/embed` with `getSessionToken`

Full steps: [Developer guide](/embed/developer-guide)

## How to verify

Use these checks before you go live with partners:

1. Mint two sessions with different liquid values (for example `customer_id=C1` and `customer_id=C2`) and confirm each session only shows that tenant’s data.
2. Confirm a request that omits a **mandatory** liquid tag is rejected.
3. Open the embed from an allowlisted origin, then from a non-allowlisted origin—and confirm the second is blocked.
4. Confirm uncovered charts stay hidden when that setting is off.
5. Optionally walk the same path in the [Partner demo](/embed/partner-demo) before you wire production.

## Related

* [Viewer options](/embed/viewer-options) — what end users can see and do
* [Partner demo](/embed/partner-demo) — try the mint path safely in Acme Analytics
* [Share links](/embed/share-links) — human URLs (not a substitute for multi-tenant SDK embeds)
