# Security model (/docs/security)

AI Wallet keeps the visitor's AI accounts on their computer and answers the sites they approve. This page describes what each party receives and how the wallet protects what it holds, as the code of this release does it.

## What a site sees [#what-a-site-sees]

A site talks to the wallet through four methods: `status`, `connect`, `disconnect` and `chat`. The [wallet protocol](/docs/protocol/wallet-provider) describes them. Through them a site receives:

- whether its origin may use the visitor's AI;
- the display name of the account that answers it, such as `ChatGPT`, or `Auto`, and that account's default model;
- the text of each answer as it streams, and the answer with its tool calls, its finish reason, the model that answered and the token counts the provider reported;
- a link notice when the visitor changes the account of the site or switches the site off.

`AiConnect.fetch`, which lets a site's AI SDK answer through the wallet, uses the same `chat` method and gives the site nothing more. It sends no request to the network: the address the SDK targets is never called, and the API key the SDK requires is the placeholder `aiwallet`, which the wallet never reads.

A site has no method to read the visitor's credentials, the list of accounts in the wallet, the other approved sites, the room each account has left, or another site's conversations.

Any page can see that AI Wallet is installed, since the wallet places `window.aiwallet` in every page it serves. Any page can call `status`, which answers `connected: false` for an origin the visitor has not approved, and `connect`, which opens the approval window.

### Who else sees the conversation [#who-else-sees-the-conversation]

- The provider of the account that answers, since the model runs on that account. The wallet sends it the messages and tool definitions the site sent, with the visitor's credential.
- Your MCP server, which receives the arguments of each tool call. The SDK calls it from the visitor's page.
- Your page, which builds the conversation and receives the answers.

## What the wallet holds [#what-the-wallet-holds]

The wallet keeps its data in Chrome's extension storage. Local storage persists on disk. Session storage lives in memory and is cleared when the browser closes or the extension reloads.

| Data | Storage | Protection |
| --- | --- | --- |
| The vault: each connected account with its credential and default model, the approved sites with the account and model of each, the default account | Local | Sealed with the vault key |
| The recovery phrase | Local | Sealed with a key derived from the password |
| The vault key, while the wallet is unlocked | Session | Held by Chrome in memory, out of reach of content scripts |
| A sign in in progress: the provider, the code to confirm, and the login's own secret such as a PKCE verifier or a device code | Session | Removed when the sign in finishes or is cancelled |
| Preferences: language, theme, lock delay, whether the recovery words were saved | Local | Plain |
| The room each account has left, by account | Local | Plain |
| Each account's model list, kept for a day | Local | Plain |
| The answers in progress (site and account) and the account Auto last used for each site | Session | Plain |

Credentials are stored in the vault only. The background reads them to call the provider they belong to, for answers, model lists and token renewal.

## Vault encryption [#vault-encryption]

The wallet works like a crypto wallet: a recovery phrase is the root of the vault key, and a password protects the phrase for daily use.

1. **Recovery phrase.** A new wallet creates a 12 word BIP39 phrase from 128 bits of randomness, with the English word list. The visitor sets a password only; the phrase waits in Settings.
2. **Vault key.** The wallet turns the phrase into its BIP39 seed, then derives 32 bytes with `HKDF` over `SHA-256`, a salt of 32 zero bytes and the info string `aiwallet vault key v1`.
3. **Vault.** The vault's contents are serialized as JSON and encrypted with `AES-GCM` under the vault key, with a new random 12 byte nonce on every write. `AES-GCM` authenticates what it decrypts, so decryption with a wrong key throws an error.
4. **Password seal.** The phrase is encrypted with `AES-GCM` under a key derived from the password with `PBKDF2`, `SHA-256`, 600,000 iterations and a random 16 byte salt. A password needs at least 8 characters.
5. **Unlock.** The password opens the phrase, the phrase gives the vault key, and the wallet keeps the key in `chrome.storage.session`. Chrome holds that storage in memory, clears it when the browser closes, and does not expose it to content scripts.
6. **Automatic lock.** An alarm locks the wallet after the delay the visitor chose in Settings: 5, 15 or 30 minutes, 1 hour or 4 hours, with 30 minutes by default. Every read of the vault pushes the alarm back. Locking removes the vault key from session storage.

### Password and recovery words [#password-and-recovery-words]

- Settings shows the 12 words after the visitor types the password. Once an account is connected, the wallet reminds the visitor to save them until they confirm.
- Changing the password seals the same phrase under the new password. The vault key does not change.
- Restoring from the words sets the wallet up again on that phrase with a new password. In a browser that still holds the sealed vault, the phrase opens it and the accounts come back. The sealed vault lives in this browser's extension storage only, so a restore in another browser starts with an empty vault.

## Site approval and revocation [#site-approval-and-revocation]

- **First connection.** A site's first `connect` opens the approval window. A locked wallet shows its unlock form first. The window shows the site's host and the account that will answer it. Approve saves the origin; Decline, or closing the window, refuses it.
- **Account per site.** An approved site starts on the visitor's default account. The visitor can give each site another account, another model of that account, or Auto, from the side panel.
- **Later visits.** An approved site reads its link with `status` and connects without a prompt. While the wallet is locked, `status` reports every site as not connected, and `connect` or `chat` opens the unlock window first.
- **Visibility.** The side panel lists the sites that use the visitor's AI and shows which account is answering a site at the moment, with a Stop button that cancels the answer.
- **Revocation.** Switch off in a site's view removes the site from the vault. A site can also remove itself with `disconnect`. The site's open pages receive a link notice of `connected: false`, and its next `connect` asks the visitor again.
- **Removing an account.** The sites that answered with it move to the default account that remains.

## Origin binding [#origin-binding]

The wallet identifies a site by the origin Chrome reports for the runtime port the page's content script opens. The page's own messages carry no origin, so one site cannot act as another. Each origin, with its scheme, host and port, is approved on its own.

## The wallet's own messages [#the-wallets-own-messages]

The side panel, the approval window and the unlock window talk to the background with runtime messages that can unlock the vault, reveal the phrase or approve a site. The background accepts them only from the extension's own pages: it checks that the sender is this extension and that the sender's URL is one of its pages. Content scripts, whose sender is the web page they run in, and other extensions are refused.

## Calls to providers [#calls-to-providers]

- The background service worker calls each provider directly, with the account's credential, from the extension's host permissions.
- When a call needs an access token that is about to expire, the wallet trades the account's refresh token for a new one and saves the new credential in the vault.
- Some providers expect headers a browser request cannot set, such as the `User-Agent` of their own command line tool, and some reject requests that carry an `Origin` header. The wallet sets and removes these headers through Chrome's `declarativeNetRequest` session rules, which apply only to requests the extension starts.
- The wallet reads the usage and rate limit headers of its own provider calls to show how much room each account has left. It matches each call to its account by the credential the call carries.

## Permissions [#permissions]

| Permission | What the wallet uses it for |
| --- | --- |
| `storage` | The sealed vault, the sealed phrase, the vault key in session storage, and the preferences. |
| `alarms` | The automatic lock. |
| `declarativeNetRequest` | Setting and removing provider headers on the wallet's own calls. |
| `identity` | Chrome's sign in window for logins that come back to the wallet's callback address. |
| `tabs` | Finding the open pages of approved sites to send link notices, following provider sign in windows, and showing the site of the current tab in the side panel. |
| `sidePanel` | Opening the wallet in Chrome's side panel from the toolbar button. |
| `webRequest` | Reading the usage and rate limit headers of the wallet's own provider calls. |

The host permissions list the sign in and API domains of the [supported providers](/docs/providers). The content scripts that place `window.aiwallet` run on every HTTPS page, and on `http://localhost` and `http://127.0.0.1`, so any site can offer the wallet to its visitors.
