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

# Session signing

> Control what a session key may sign off-chain over ERC-1271.

Session keys control ERC-1271 signatures separately from transaction permissions. Set `signing` on `createSession` to choose a mode:

* **Disabled** (`{ mode: 'disabled' }`): The session cannot sign messages or typed data.
* **Unrestricted** (`{ mode: 'unrestricted' }`): The session can sign any message or typed data.
* **Scoped** (`{ mode: 'scoped', allowedContents: [...] }`): The session can sign only EIP-712 typed data matching an allowed domain and primary-type schema. Plain messages and arbitrary hashes are not allowed.

If you omit `signing`, the SDK uses unrestricted signing with no validity window. This is identical to explicitly setting `{ mode: 'unrestricted' }`, preserving existing session identities and behavior. Disable signing unless your session needs ERC-1271 signatures.

Both unrestricted and scoped signing accept optional `validAfter` and `validUntil` dates. One window applies to the entire signing capability. With neither bound, the SDK selects the unrestricted policy. If you provide either bound, it selects the time-frame policy.

<Note>A [restricted](./restrict-a-session) or [swap-scoped](./swap-sessions) session defaults to `{ mode: 'disabled' }`. Pass `signing` explicitly to opt back in.</Note>

## Unrestricted, time-boxed signing

This session can sign any message or typed data for one hour:

Examples on this page use `@rhinestone/sdk`, where `rhinestone` is the `RhinestoneSDK` instance from [Create a session with a custom setup](/wallets/session-keys/custom-setup/create-a-session).

```ts theme={null}
import { base } from "viem/chains";

const validAfter = new Date();
const validUntil = new Date(validAfter.getTime() + 60 * 60 * 1000);

const session = await rhinestone.createSession({
  chain: base,
  owners: {
    type: "ecdsa",
    accounts: [sessionOwnerAccount],
  },
  signing: {
    mode: "unrestricted",
    validAfter,
    validUntil,
  },
});
```

## Scoped Permit2 typed data

This session allows the Permit2 `PermitSingle` domain and schema. Provide the human-readable EIP-712 `domain`, `types`, and `primaryType`; the SDK derives the domain separator and canonical type encoding.

```ts theme={null}
const permit2Address = "0x000000000022D473030F116dDEE9F6B43aC78BA3";

const session = await rhinestone.createSession({
  chain: base,
  owners: {
    type: "ecdsa",
    accounts: [sessionOwnerAccount],
  },
  signing: {
    mode: "scoped",
    allowedContents: [
      {
        domain: {
          name: "Permit2",
          chainId: base.id,
          verifyingContract: permit2Address,
        },
        types: {
          PermitDetails: [
            { name: "token", type: "address" },
            { name: "amount", type: "uint160" },
            { name: "expiration", type: "uint48" },
            { name: "nonce", type: "uint48" },
          ],
          PermitSingle: [
            { name: "details", type: "PermitDetails" },
            { name: "spender", type: "address" },
            { name: "sigDeadline", type: "uint256" },
          ],
        },
        primaryType: "PermitSingle",
      },
    ],
  },
});
```

The scope matches the exact EIP-712 domain and canonical primary-type schema. It does not constrain message values such as the token, amount, spender, or deadline. Add transaction permissions or use separate sessions when those values need different controls.

<Warning>
  You can configure and enable scoped sessions today, but direct scoped signing
  through `signTypedData` remains unavailable until safe ERC-7739 signature
  emission is restored. The SDK fails fast instead of emitting an unsafe
  signature.
</Warning>
