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

# Allowance Voucher

Allowance voucher (aka Session Key) allows owner of a ciphertext to delegate viewing/compute to someone else for a period of time completely off-chain.

<Note>Vouchers are like sessions and are revokable.</Note>

## Security model

A session voucher grants the holder access to **all** of the sharer's encrypted handles — it is not scoped to a specific dApp or contract. `canUseSession` checks only that the session has not expired and that the caller matches the authorized decrypter; it does not restrict access by which application the encrypted value came from.

This means a single voucher signed for one application also covers handles created by any other Inco-enabled dApp the user has interacted with (auction bids, game states, private messages, etc).

**To limit exposure:**

* Use the shortest expiry that makes sense for your use case (minutes rather than hours or days).
* Call `updateActiveVouchersSessionNonce()` as soon as the session is no longer needed to invalidate all outstanding vouchers immediately, regardless of their expiry.
* Never log, persist, or transmit the voucher outside of the narrowest scope that requires it.

### Wallet signing prompt

When a user signs a session voucher, wallets render all EIP-712 struct fields. The `AllowanceVoucher` includes a `warning` field that is part of the signed payload:

<Warning>
  Inco Warning: signing this message may leak your private data, including from
  unrelated apps. Sign only if you fully trust this app.
</Warning>

The `warning` field is placed **first** in the EIP-712 struct so wallets show it before the opaque byte fields, ensuring users see it prominently.

This message is included automatically when using `grantSessionKeyAllowanceVoucher`. **Do not suppress or alter it** — the on-chain verifier rejects any `AllowanceProof` whose voucher does not contain this exact text (reverts with `InvalidVoucherWarning`), and altering it also produces a different EIP-712 digest that will fail signature verification.

## Getting Started

To create a voucher and grant Bob permission to decrypt on Alice's behalf:

```javascript theme={null}
import { createWalletClient, custom } from 'viem'
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
import { getViemChain, supportedChains, type SupportedChainId } from '@inco/lightning-js';
import { Lightning } from '@inco/lightning-js/lite';

// Alice connects her wallet
const walletClient = createWalletClient({
  chain: getViemChain(supportedChains.baseSepolia),
  transport: custom(window.ethereum!)
})

// Alice creates an ephemeral Ethereum account (the session key)
const ephemeralAccount = privateKeyToAccount(generatePrivateKey());

// for Base mainnet: Lightning.baseMainnet()
// recommended: pass your own RPC URLs (otherwise viem's public RPC is used)
const zap = await Lightning.baseSepoliaTestnet({
  hostChainRpcUrls: ['https://your-rpc-url'],
});
// Default session verifier address
const sessionVerfier = "0xc34569efc25901bdd6b652164a2c8a7228b23005";

// Alice grants the ephemeral account permission to decrypt on her behalf
const voucherWithSig = await zap.grantSessionKeyAllowanceVoucher(
    walletClient,
    ephemeralAccount.address,
    new Date(Date.now() + 1000 * 60 * 60 * 24), // 1 day
    sessionVerfier,
);
```

<Note>
  Some specific use cases may require a custom session verifier logic, to for
  example check whether a payment has been made in order to access secrets.
  grantSessionKeyAllowanceVoucher() allows the owner to specify a custom session
  verifier contract address with a custom canUseSession() function that can
  either allow or deny access based on certain on-chain conditions.
</Note>

<Warning>
  Using the default session verifier will give the session key unverified access
  to all user handles for the specified time period.

  DEFAULT\_SESSION\_VERIFIER (Base Sepolia): 0xc34569efc25901bdd6b652164a2c8a7228b23005

  DEFAULT\_SESSION\_VERIFIER (Base mainnet): 0x68a5b59b4caf23416885859c1662746619a471f3
</Warning>

```Solidity theme={null}
import {ALLOWANCE_GRANTED_MAGIC_VALUE} from "@inco/lightning/src/Types.sol";

struct Session {
    address decrypter;
    uint256 expiresAt;
}

contract MyCustomSessionVerifier {
    function canUseSession(
        bytes32, /* handle */
        address account, /* user trying to use the session */
        bytes memory sharerArgData,
        bytes memory /* requesterArgData */
    ) external view returns (bytes32) {
        Session memory session = abi.decode(sharerArgData, (Session));
        if (session.expiresAt >= block.timestamp && session.decrypter == account) {
            return ALLOWANCE_GRANTED_MAGIC_VALUE;
        }
        return bytes32(0);
    }
}
```

## Attested Decrypt with Voucher/Session Key

The voucher and signature now allow Bob to decrypt any handle that Alice owns using the attestedDecryptWithVoucher() function:

```javascript theme={null}
import type { HexString } from '@inco/lightning-js';

// Bob calls the attestedDecryptWithVoucher function with voucher
const decrypted = await zap.attestedDecryptWithVoucher(
    ephemeralAccount,
    voucherWithSig,
    ['0x...' as HexString]
);
const plaintext = decrypted[0].plaintext.value;
```

## AttestedCompute with Voucher/Session Key

Bob can also perform attested compute on behalf of Alice using attestedComputeWithVoucher() to check if the value is equal to an expected value:

```javascript theme={null}
import type { AttestedComputeSupportedOps } from '@inco/lightning-js/lite';
import type { HexString } from '@inco/lightning-js';

const computed = await zap.attestedComputeWithVoucher(
    ephemeralAccount,
    voucherWithSig,
    '0x...' as HexString,
    AttestedComputeSupportedOps.Eq,
    100n
);

const plaintext = computed.plaintext.value;
```

## SessionKey Reencrypt

Similarly, Bob can also perform reencryption on behalf of Alice using attestedDecryptWithVoucher() with a reencrypt public key. This is similar to standard [attested decrypt reencryption](../attestations/attested-decrypt#reencryption-for-delegates).

```javascript theme={null}
import { generateXwingKeypair } from '@inco/lightning-js/lite';
import type { HexString } from '@inco/lightning-js';

// Generate an X-Wing keypair for reencryption
const reencryptKeypair = await generateXwingKeypair();
const reencryptPubKey = reencryptKeypair.encodePublicKey();

// Bob performs reencryption on behalf of Alice
const reencrypted = await zap.attestedDecryptWithVoucher(
    ephemeralAccount,
    voucherWithSig,
    ['0x...' as HexString],
    { reencryptPubKey, reencryptKeypair }
);

// Bob can decrypt the reencrypted data
const decrypted = await decrypt(
    reencryptKeypair,
    hexToBytes(reencrypted[0].encryptedPlaintext.ciphertext.value)
);
const plaintext = BigInt('0x' + Buffer.from(decrypted).toString('hex'));
```

## Revoking a Voucher

In order to invalidate existing vouchers, you can call this function:

```javascript theme={null}
const txHash = await zap.updateActiveVouchersSessionNonce(walletClient);
```

Which will invalidate all existing vouchers, despite their expiration time.

<Note>
  If you want to selectively revoke access to specific users, your dApp would
  need to handle the logic of reissuing new vouchers to users who still require
  access.
</Note>
