# Security and data

This page tells where ShappyChat keeps data and how to keep credentials safe.

![Where secrets live: the app credential stays on your app server; provider and MCP keys are encrypted in the backend data folder; cells ask the backend for a key at each model request; the browser holds only the user's sign-in](/docs/diagrams/security.svg)

## Who owns which data

| Data | Owner | Location |
| --- | --- | --- |
| App records, for example notes | Your app | Your database |
| Conversation history | ShappyChat runtime | celld cells behind Shappy Backend, browser IndexedDB or your Node folder |
| App credentials | Shappy Backend | Hashes only, in `clients.json` |
| Provider and MCP keys entered in the backend | Shappy Backend | Data folder, encrypted with AES-256-GCM under `master.key` or `SHAPPYCHAT_MASTER_KEY` |
| Operator sign-in | Shappy Backend | A single-use link (10 minutes), then a 12-hour HttpOnly session cookie. The cookie is `Secure` when the public URL uses HTTPS. |
| celld service tokens | Shappy Backend | `celld.json` in the data folder |
| Chrome extension settings | Chrome extension | Extension storage |

The chat does not add tables to your database.

## Keep credentials on the server

- Do not put an app credential or an MCP key in a public JavaScript bundle.
- Give each app and each extension its own credential.
- Give each credential only the MCP connections that it needs.
- Grant host UI permission only to clients that must act on pages.
- Revoke a credential when you do not need it. Revoking cancels its running replies.
- An app credential is a long-term secret. It is not a short pairing code.

> **Warning:** A credential is for one app. It is not for one user. In a product that your customers use, your server must check the user session for each `/shappy/*` request. Your server must also make sure that each user gets access only to their own conversations. Version 0.1.0 of the backend does not have sessions for each user.

## Set the scope on the server

Each conversation belongs to an account and a workspace. Set this scope from trusted configuration, for example the signed-in session. Do not take the scope from a chat message or from a request body.

## Requests and retries

- Each message has a request ID. If the client sends the same request ID with the same message again, the runtime does not submit it twice. The runtime rejects a different message with the same ID.
- The remote runtime does not send a message again automatically after a network error.
- After a restart, a tool call that was running is shown as an error with an unknown result. It does not run again.

> **Tip:** Make operations that change data idempotent in your app. Then a repeated call does not change data twice.

## What the model receives

- The messages of the conversation
- The results of the tools that the chat calls
- In the Chrome extension: the title and URL of the page that the chat uses, and the page content that a tool reads

Page context and control descriptions are data for the model. They are not instructions. The model provider that you configure receives this data. Read the data policy of your provider.

## Cancellation

**Stop** cancels the reply and the tool calls that have not started. An operation that already started can complete in your app. Check the state of your app before you try again.
