Skip to main content
Permits are EIP-712 signatures that authorize decryption of confidential data. The issuer field identifies who is accessing the data — the issuer must have been granted access on-chain via FHE.allow(handle, address). When a permit is used, CoFHE validates it against the ACL contract to confirm that the issuer has access to the requested encrypted handle. Each permit includes a sealing keypair. The public key is sent to CoFHE so it can re-encrypt the data for the permit holder. The private key stays client-side and is used to unseal the returned data.

When do you need a permit?

  • decryptForView: always requires a permit.
  • decryptForTx: depends on the contract’s ACL policy for that ctHash.
    • If the policy allows anyone to decrypt, you can use .withoutPermit().
    • If the policy restricts decryption, you must use .withPermit(...).

Prerequisites

Create and connect a client. Permits are scoped to a chainId + account.

Quick start

The examples below show two approaches. The client.permits API is the recommended approach — it automatically signs permits with the connected wallet and manages the permit store. The PermitUtils API is a lower-level alternative that gives you direct control over signing and storage.
await client.connect(publicClient, walletClient);

// Returns the active self permit if one exists, otherwise creates and signs a new one.
const permit = await client.permits.getOrCreateSelfPermit();
After this, the active permit is picked up automatically:
  • decryptForView(...).execute() uses the active permit.
  • decryptForTx(...).withPermit().execute() uses the active permit.

Permit types

TypeWho signsUse case
selfissuer onlyDecrypt your own data (most common)
sharingissuer onlyA shareable “offer” created by the issuer for a recipient
recipientrecipient (includes issuer signature)The imported permit after the recipient signs it
  • Permit expiration is a unix timestamp in seconds. The default is 7 days from creation.
  • When a permit is created via client.permits.*, it is automatically stored and set as the active permit.

Creating a self permit

A self permit lets you decrypt data that was allowed to your address.

createSelf

await client.connect(publicClient, walletClient);

const permit = await client.permits.createSelf({
  issuer: walletClient.account.address,
  name: 'My self permit',
});

permit.type; // 'self'
permit.hash; // deterministic hash

getOrCreateSelfPermit

Returns the active self permit if one exists. Otherwise creates and signs a new one. This is the recommended approach for most applications.
await client.connect(publicClient, walletClient);

const permit = await client.permits.getOrCreateSelfPermit();
permit.type; // 'self'

Sharing permits

Sharing permits let an issuer delegate their ACL access to a recipient. The recipient can then decrypt the issuer’s data without needing their own FHE.allow.
1

Issuer creates a sharing permit

The issuer creates a sharing permit specifying the recipient’s address.
await client.connect(publicClient, walletClient);

const sharingPermit = await client.permits.createSharing({
  issuer: walletClient.account.address,
  recipient,
  name: 'Share with recipient',
});
2

Issuer exports the permit

Export the permit as a JSON blob and share it with the recipient.
import { PermitUtils } from '@cofhe/sdk/permits';

const exported = PermitUtils.export(sharingPermit);
The exported JSON does not contain any sensitive data and can be shared via any channel.
Do not share serialize(permit) output — serialization is meant for local persistence and includes the sealing private key.
3

Recipient imports and signs

The recipient imports the exported JSON and signs it with their wallet. On import, a new sealing key is generated for the recipient.
await client.connect(publicClient, walletClient);

const recipientPermit = await client.permits.importShared(exported);

recipientPermit.type; // 'recipient'
recipientPermit.hash;

Active permit management

The SDK tracks all stored permits and an active permit hash per chainId + account. Creating or importing a permit via client.permits.* automatically stores it and selects it as active.

List stored permits

const permits = client.permits.getPermits();
Object.keys(permits); // permit hashes

Read / select the active permit

const active = client.permits.getActivePermit();
active?.hash;

client.permits.selectActivePermit(somePermitHash);

Removing permits

client.permits.removePermit(permitHash);
client.permits.removeActivePermit();

Persistence and security

  • The SDK persists permits in a store keyed by chainId + account.
  • In web environments, this store uses localStorage under the key cofhesdk-permits.
  • A stored permit includes the sealing private key. Treat it like a secret.
    • Never share serialized permits with other users.
    • To share access, use PermitUtils.export(...) which strips sensitive fields.