> ## 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.

Smart Sessions 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:

```ts theme={null}
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>
