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

# Security model

> Who approves what in Connect with Chipi, what the SDK enforces in code, the wallet's error codes, and what a wallet host must do with message origins.

Connect with Chipi has two halves. The **connector** runs in the dApp's page and holds no keys.
The **wallet** (the hosted wallet at `wallet.chipipay.com`, or your own host built on
`createChipiWindowObject` / `useChipiWalletObject`) owns every approval and every signature.

## What the SDK enforces

These hold for any wallet built on `@chipi-stack/nextjs` / `@chipi-stack/chipi-react`
(14.16.0+):

* **Deny by default.** The wallet object asks `requestApproval` for three things: `connect`,
  `execute` and `signTypedData`. Without a `requestApproval` callback, all three are refused
  (`113`) and a single `console.warn` says why. Nothing is ever auto-approved.
* **Connect is explicit.** `wallet_requestAccounts` asks for a `connect` approval once per
  wallet object, even when the wallet is unlocked. `silent_mode` and `wallet_getPermissions`
  reveal nothing until then.
* **Typed data must be for this chain.** `wallet_signTypedData` refuses a SNIP-12 domain with no
  `chainId`, or one that differs from the wallet's (`SN_MAIN` and its hex form are the same chain),
  with `114` and without prompting. Bramble applies the same rule. (A missing `chainId` is refused
  from 14.17.0; 14.16.x let it through.)
* **No dApp can create a session.** No `wallet_*` method creates a session or asks for a
  `session` approval. Sessions come only from the host calling `createSession(policy)` on
  `useChipiWalletObject`. With `spendCaps`, the session and its spending policies are registered
  in the same owner-signed transaction (`sdk.sessions.setupSession`). A cap that would mean
  "unlimited" on-chain (`maxPerWindow = 0`) is refused. The on-chain whitelist is per entrypoint
  selector, not per contract, so caps are what bound token spend.

The connector side (`@chipi-stack/starknet-connector`) ignores any message that doesn't come
from the wallet window it opened (0.4.0+), times out requests that get no answer, never reopens
a closed wallet window outside a click, and (0.5.0+) answers passive calls such as
`wallet_getPermissions` itself while no wallet window is open, so a page-load wallet scan never
tries to open Chipi.

## Error codes of the Chipi wallet object

`createChipiWindowObject` rejects with an `Error` whose numeric `code` follows the Starknet
wallet API:

| Code | When |
| - | - |
| `113` | The user (or a missing `requestApproval`) refused `connect`, `execute` or `signTypedData`. |
| `114` | Invalid payload: `wallet_addInvokeTransaction` with no calls, or `wallet_signTypedData` with no domain `chainId` or one for another chain. |
| `115` | `wallet_deploymentData` for an account the host reports deployed (`getDeploymentData` throws `account_already_deployed`). |
| `116` | `wallet_deploymentData` when the host can't derive the data (`getDeploymentData` throws `account_deployment_data_unavailable`). From 14.17.0 `useChipiWalletObject` answers this unless you pass `isDeployed: true` (14.16.x always answered `115`). |
| `163` | Unsupported method: `wallet_addDeclareTransaction`, `wallet_watchAsset`, `wallet_addStarknetChain`, `wallet_switchStarknetChain`, and any unknown method including `wallet_disconnect`. Also any other `getDeploymentData` failure. |

Errors thrown by the host's `executeCalls` or `signTypedData` reach the dApp as thrown. Through
the connector, these codes arrive as `err.code` (see [Errors](/sdk/connector/api#errors)).

## What the hosted wallet adds

The hosted wallet at `wallet.chipipay.com` puts a review screen in front of each approval: the
dApp's origin and the address being shared on connect, decoded token intent on execute
(amounts, recipients, an **UNLIMITED** badge on large approvals, a warning on unrecognized
contracts), and labeled SNIP-12 fields on signTypedData. That screen lives in the hosted wallet,
not in this SDK; if you build your own host, `requestApproval` is where you render it.

## Message origins: a rule for wallet hosts

Every request the connector sends carries an `origin` field, but **the dApp writes that field
itself**. A wallet host must identify the dApp by the browser-provided `MessageEvent.origin` of
the message it received, and show and enforce that value. Never trust the `origin` inside the
message: a phishing page can put any origin it likes there.

## Where the trust boundary sits

`ChipiConnector` (running inside the dApp's page) holds no keys and signs nothing: it only
forwards `wallet_*` calls over `postMessage` to the hosted wallet, a separate origin the dApp
never controls. The same is true of the **browser extension**: it injects the provider so dApps
can discover Chipi, but every signature still happens on the hosted wallet's own origin, behind
the user's passkey. Neither surface can exfiltrate a key that was never present.

Other wallets on the same page, such as Bramble, are separate objects (`window.starknet_bramble`)
with their own approvals. The Chipi connector never reads or calls them.

> ✅ Verified against the SDK source on **2026-10-12**.


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