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

# Protect and restore

> Protect a request, call the model, and restore the response.

## `protect`

`guard.protect` checks a payload for attacks and personal data in one request and rewrites it once.

<CodeGroup>
  ```ts TypeScript theme={null}
  const context = { subject: user.id };

  const result = await fw.guard.protect(
    { system, messages },
    { cloak: { context, restore: true } },
  );
  if (result.blocked) return refuse(result.reason);

  const reply = await openai.chat.completions.create({
    model: "gpt-4o-mini",
    messages: result.openai!.messages,
  });
  const { value } = await result.restore(reply);
  ```

  ```python Python theme={null}
  context = CloakContext(subject=user.id)

  result = fw.guard.protect(
      {"system": system, "messages": messages},
      ProtectOptions(cloak=CloakOptions(context=context, restore=True)),
  )
  if result.blocked:
      return refuse(result.reason)

  reply = openai.chat.completions.create(
      model="gpt-4o-mini",
      messages=[m.__dict__ for m in result.openai.messages],
  )
  value = result.restore(reply).value
  ```
</CodeGroup>

The attack check and detection run side by side on the control plane, so the call is as slow as the slower of the two, not the sum.

## The result

| Field | Meaning |
| - | - |
| `blocked` | Whether the request must not be forwarded. This is the field to act on. |
| `reason` | A rule id, a classifier label, `scan_unavailable`, or `cloak_unavailable`. Null when not blocked. |
| `attack` | The attack verdict. Null when you turned the attack check off. |
| `cloak` | What Cloak did: `ran`, the replaced `entities`, and any `error`. |
| `openai` / `anthropic` | The cleaned payload for each provider SDK. **Null when blocked.** |
| `eventId` | Correlates with the console. |
| `restore` | Restores a response under the context this call used. |

<Warning>
  When `blocked` is true the payloads are null. That is deliberate. A payload that reached this point with personal data still in it must not be something you can forward by mistake.
</Warning>

Each entry in `cloak.entities` has a `type`, `category`, `start`, `end`, the `strategy` used, and the `token` for a tokenized value. Offsets index into the text you submitted. The values themselves are not in the result.

## The rewrite

Attack redactions and Cloak replacements are applied in one pass. A value that sits inside an attack span is replaced by the attack's replacement and is never tokenized or stored. Images, provider parameters, message roles, and any other non-text content pass through untouched. A value that straddles two messages is replaced in each message separately.

## Restoring

`result.restore(value)` accepts a string or any JSON-shaped value, such as a provider response, and returns the same shape with its tokens replaced.

```ts theme={null}
const { value, restored, unknown, denied } = await result.restore(reply);
```

| Field | Meaning |
| - | - |
| `restored` | Tokens replaced. |
| `unknown` | Tokens the vault did not know, left as they are. A model may invent a token-shaped string. |
| `denied` | Tokens that belong to another context, left in place. Non-zero only with `onDenied: "leave"`. |

Only tokens the vault actually holds are ever replaced. A token-shaped string in a response proves nothing, because a token is a keyed digest and cannot be written without the key.

Restoring by context, without a `protect` result in hand:

```ts theme={null}
await fw.guard.cloak.restore(reply, { context });
```

## Wrap

`wrap` protects the payload before your callback sees it. With `restore: true` it also restores whatever the callback returns.

```ts theme={null}
const reply = await fw.guard.wrap(
  { system, messages },
  (safe) =>
    openai.chat.completions.create({
      model: "gpt-4o-mini",
      messages: safe.openai.messages,
    }),
  { cloak: { context, restore: true } },
);
```

The callback also receives `safe.cloak` (the report) and, when you gave a context, `safe.restore`.

## Decorate

```ts theme={null}
const create = fw.guard.decorate(
  (body: OpenAI.Chat.ChatCompletionCreateParamsNonStreaming) =>
    openai.chat.completions.create(body),
  { cloak: { context, restore: true } },
);
```

The request is forwarded protected, provider parameters intact. A non-streamed response is restored when `restore` is set.

<Note>
  A stream is not restored. Its chunks reach you before the call ends. Restore the text you assemble with `fw.guard.cloak.restore`.
</Note>

## What is recorded

With capture on, the request body is recorded **as it was forwarded**, with tokens in place of values, and the response is recorded **before** it was restored. A request that was refused is recorded only if its personal data can be redacted out of it, and its body is dropped otherwise. Telemetry carries counts only. See [Telemetry and capture](/operations/telemetry-and-capture).

## Cloak on its own

To protect personal data without the attack check:

<CodeGroup>
  ```ts TypeScript theme={null}
  const result = await fw.guard.cloak.protect(input, { context });
  ```

  ```python Python theme={null}
  result = fw.guard.cloak.protect(input, CloakOptions(context=context))
  ```
</CodeGroup>

`fw.guard.cloak.scan(input)` finds entities without changing anything. It returns `ran`, and a payload the detector never answered for has `ran: false`. Read that as "not checked", never as "nothing found".


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.