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

getOrCreateSelfPermit

Returns the active self permit if one exists. Otherwise creates and signs a new one. This is the recommended approach for most applications.

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

Issuer exports the permit

Export the permit as a JSON blob and share it with the recipient.
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.

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

Read / select the active permit

Removing permits

Validating permits

Since @cofhe/sdk@0.5.0, PermitUtils.validate enforces the full check: schema + signed + not-expired. The decrypt flows (decryptForView, decryptForTx with .withPermit(...)) call this for you and surface failures as typed errors — you only need to validate manually when you want to inspect or filter permits before using them.

Throwing helpers — PermitUtils.*

FunctionWhat it checksBehavior on failure
PermitUtils.validate(permit)Schema and signed and not-expired.Throws (Permit is expired / Permit is not signed / schema error).
PermitUtils.validateSchema(permit)Schema only (shape + invariants).Throws on schema failure; does not check expiry or signatures.
Use validateSchema when you’ve just received a permit from the wire (e.g. an imported sharing permit) and want to reject malformed payloads without yet caring about expiry.

Non-throwing helpers — ValidationUtils.*

For inspection without exception handling, use the ValidationUtils helpers. They return a typed ValidationResult:
FunctionReturnsUse case
ValidationUtils.isValid(permit)ValidationResultFull check (schema + signed + not-expired) without throwing.
ValidationUtils.isSignedAndNotExpired(permit)ValidationResultSkip the schema parse if you already validated the shape.
ValidationUtils.isSigned(permit)boolean”Does it carry a signature on the issuer / recipient side as appropriate?”
ValidationUtils.isExpired(permit)booleanCompare permit.expiration to current time.
Pattern-match on result.error to render a precise UI message:

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.