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

# Security & data residency

> Where your data lives, what leaves your network, and how identity and tags are protected. Full prompts, responses, and RAG stay in your VPC; only metadata reaches Firedog cloud.

Firedog is built so that the sensitive part of every AI call — the prompt, the retrieved context, the model's response — never leaves your network. The collector runs inside your own VPC, prices and records each call against a local Postgres you control, and emits only priced metadata to the Firedog control plane. This page describes that boundary in detail.

<Info icon="shield-halved">
  **No prompt, response, or RAG content ever leaves your VPC.** The collector emits priced metadata only. Message content and retrieved chunks are written to a Postgres database inside your network and are read back — for a dashboard drill-down — only from within that network.
</Info>

## The data-residency model

Firedog is split-plane. The two planes hold different data and sit in different trust boundaries.

* **Data plane — the collector, inside your VPC.** It exposes an OpenAI-compatible endpoint (`POST /v1/chat/completions`). Your applications route their LLM calls through it, it forwards to the upstream provider, prices the call from a shared pricing table, and writes the **full record** to a **local Postgres in your VPC**. This full record — the `CallRecord` — never leaves your network.
* **Control plane — Firedog cloud, the dashboard.** It receives only **metadata** (`CallMetadata`) through an ingest API: priced, attributed, and identity-protected numbers with no message content. This is what powers Home, Cost allocation, Workflow detail, Answer quality, and the Audit trail.

A `callId` (a client-side ULID) links the two. It lets a dashboard drill-down fetch the full record **from the collector, within your VPC** — never from the cloud, which does not hold it.

```mermaid theme={null}
flowchart LR
  App["Your apps<br/>(SDK or base-URL swap)"] -->|"POST /v1/chat/completions"| Collector

  subgraph VPC["Your VPC (data plane)"]
    Collector["Firedog collector"]
    PG[("Postgres<br/>full CallRecord<br/>prompt · RAG · response")]
    Collector --> PG
  end

  Collector -->|"upstream call"| Provider["LLM provider"]
  Collector -->|"CallMetadata only<br/>(no content)"| Cloud

  subgraph Firedog["Firedog cloud (control plane)"]
    Cloud["Ingest API + dashboard"]
  end

  Cloud -.->|"drill-down by callId,<br/>fetched inside VPC"| Collector
```

See [How it works](/how-it-works) for the end-to-end flow.

## What stays vs. what leaves

The `CallRecord` holds the content and stays in your Postgres. The `CallMetadata` holds priced, attributed numbers and is the only thing sent to Firedog cloud. Content fields have no counterpart on the cloud side.

| Field                               | `CallRecord` — stays in your VPC | `CallMetadata` — emitted to Firedog cloud |
| ----------------------------------- | :------------------------------: | :---------------------------------------: |
| Prompt messages                     |              Stored              |               — never leaves              |
| Retrieved RAG chunks                |              Stored              |               — never leaves              |
| Model response / completion text    |              Stored              |               — never leaves              |
| `callId` (ULID)                     |              Stored              |                  Emitted                  |
| `tenantId`                          |              Stored              |                  Emitted                  |
| `collectorId`                       |              Stored              |                  Emitted                  |
| `timestamp`                         |              Stored              |                  Emitted                  |
| `model`                             |              Stored              |                  Emitted                  |
| `promptTokens` / `completionTokens` |              Stored              |           Emitted (counts only)           |
| `cost`                              |              Stored              |                  Emitted                  |
| `latencyMs`                         |              Stored              |                  Emitted                  |
| `team`                              |              Stored              |        Emitted (subject to profile)       |
| `workflow`                          |              Stored              |        Emitted (subject to profile)       |
| `userHash` (salted hash)            |              Stored              |      Emitted (never the raw identity)     |
| `category`                          |              Stored              |        Emitted (subject to profile)       |
| `qaOverall` (quality score)         |              Stored              |                  Emitted                  |
| `shadowSaving`                      |              Stored              |                  Emitted                  |

<Warning>
  There is no field on the `CallMetadata` side that carries prompt text, response text, or RAG content. Token figures are **counts**, not content. If a call is shadow-tested against a cheaper model, only the resulting quality number (`qaOverall`) and the saving (`shadowSaving`) are emitted — never the shadow prompt or response.
</Warning>

## Identity and tag protection

Even though only metadata leaves your VPC, that metadata can still carry labels. Firedog is designed to protect them before they are emitted.

* **User identity is never sent raw.** The `userId` you attach for attribution is emitted as a **salted hash** (`userHash`), so per-user cost views work without the control plane holding an identity you can reverse.
* **Team and workflow tags can be hashed.** Labels like `Project-Titan` can be emitted as opaque tokens (for example `wf_7c2a`) rather than the human-readable name, so a project's existence isn't exposed by its cost line.
* **Category can be dropped** where it could itself identify a desk or initiative.

What actually gets emitted is governed by the collector's **[sensitivity profile](/sensitivity-profiles)** — `strict`, `standard`, or `open`:

<CardGroup cols={3}>
  <Card title="strict" icon="lock">
    Maximal redaction: tags hashed, user always hashed, identifying category dropped.
  </Card>

  <Card title="standard" icon="scale-balanced">
    Balanced: raw workflow and team labels, salted user hash, quality signal retained.
  </Card>

  <Card title="open" icon="lock-open">
    Richest tags, for teams whose labels are not sensitive.
  </Card>
</CardGroup>

<Note>
  Sensitivity profiles are configurable defaults for what the collector is allowed to emit, not hard guarantees for every possible label. Choose the profile per collector to match the desk's data-classification policy.
</Note>

## Self-hosted collector, metadata-only egress

The collector is the data plane and it runs where your data already lives — inside your VPC, alongside a Postgres you own.

Two properties follow from that placement:

<Steps>
  <Step title="Content is written locally">
    Full `CallRecord` rows — prompt, RAG chunks, response — are written to your in-VPC Postgres. They are queried in place for dashboard drill-downs, keyed by `callId`.
  </Step>

  <Step title="Egress is metadata-only">
    The only outbound path to Firedog cloud carries `CallMetadata`. If you inspect the collector's egress, you will see priced, attributed, identity-protected numbers — and no message content.
  </Step>
</Steps>

<Tip>
  Because the collector is OpenAI-compatible, adopting it is a base-URL swap or an SDK change on your side — the security boundary does not require you to hand message content to a third party. See the [quickstart](/quickstart).
</Tip>

## Your data, our role

Under GDPR, the **client is the controller** and holds the content; **Firedog is a processor** on usage metadata only; the **LLM providers are the client's own processors** — content reaches them from the client, never via Firedog. The only personal data that leaves the client is a collaborator identifier, emitted as a salted hash. See [Data processing & residency](/data-processing) for the full treatment.

## Access and retention

Retention of the full `CallRecord` is governed by your own Postgres, which you operate and can age out or purge on your own schedule. On the control plane, Firedog keeps raw metadata for **30–90 days** and aggregates in longer retention; content is **never** retained by Firedog.

<Note>
  Exact windows and control-plane access controls (authentication, roles, per-client isolation, access logging) are being formalised — see the *\[Placeholders]* on [Data processing & residency](/data-processing).
</Note>

## Data residency and subprocessors

The Firedog control plane — the metadata store and web app — is hosted in the **EU only**: Railway, region EU West (Amsterdam), on GCP `europe-west4`. Client content stays in your VPC and is never hosted by Firedog or its subprocessors.

The processing chain is **Firedog → Railway → Google Cloud**. Railway is SOC 2 Type II + SOC 3; Google Cloud is SOC 2 / ISO 27001 and DPF-certified. The full subprocessor table, transfer basis, retention and deletion model live on [Data processing & residency](/data-processing).

<Note>
  *\[Placeholder — confirm with the Firedog team: any attestations held by Firedog itself, and the client DPA.]*
</Note>

## Related

<CardGroup cols={2}>
  <Card title="Data processing & residency" icon="scale-balanced" href="/data-processing">
    GDPR roles, subprocessors, retention, deletion, and EU hosting.
  </Card>

  <Card title="How it works" icon="diagram-project" href="/how-it-works">
    The split-plane architecture end to end.
  </Card>

  <Card title="Sensitivity profiles" icon="sliders" href="/sensitivity-profiles">
    What each profile emits, and how to choose one.
  </Card>

  <Card title="Attribution" icon="tags" href="/attribution">
    How tags reach the metadata, and how they're hashed.
  </Card>

  <Card title="Quickstart" icon="play" href="/quickstart">
    Route your first call through the collector with the SDK or a base-URL swap.
  </Card>
</CardGroup>
