Skip to main content

2 posts tagged with "encryption"

View All Tags

Building Decentralized Encryption with @mysten/seal: A Developer's Tutorial

· 13 min read
Dora Noda
Software Engineer

Privacy is becoming public infrastructure. In 2025, developers need tools that make encryption as easy as storing data. Mysten Labs' Seal provides exactly that—decentralized secrets management with onchain access control. This tutorial will teach you how to build secure Web3 applications using identity-based encryption, threshold security, and programmable access policies.


Introduction: Why Seal Matters for Web3

Traditional cloud applications rely on centralized key management systems where a single provider controls access to encrypted data. While convenient, this creates dangerous single points of failure. If the provider is compromised, goes offline, or decides to restrict access, your data becomes inaccessible or vulnerable.

Seal changes this paradigm entirely. Built by Mysten Labs for the Sui blockchain, Seal is a decentralized secrets management (DSM) service that enables:

  • Identity-based encryption where content is protected before it leaves your environment
  • Threshold encryption that distributes key access across multiple independent nodes
  • Onchain access control with time locks, token-gating, and custom authorization logic
  • Storage agnostic design that works with Walrus, IPFS, or any storage solution

Whether you're building secure messaging apps, gated content platforms, or time-locked asset transfers, Seal provides the cryptographic primitives and access control infrastructure you need.


Getting Started

Prerequisites

Before diving in, ensure you have:

  • Node.js 18+ installed
  • Basic familiarity with TypeScript/JavaScript
  • A Sui wallet for testing (like Sui Wallet)
  • Understanding of blockchain concepts

Installation

Install the Seal SDK via npm:

npm install @mysten/seal

You'll also want the Sui SDK for blockchain interactions:

npm install @mysten/sui

Project Setup

Create a new project and initialize it:

mkdir seal-tutorial
cd seal-tutorial
npm init -y
npm install @mysten/seal @mysten/sui typescript @types/node

Create a simple TypeScript configuration:

// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}

Core Concepts: How Seal Works

Before writing code, let's understand Seal's architecture:

1. Identity-Based Encryption (IBE)

Unlike traditional encryption where you encrypt to a public key, IBE lets you encrypt to an identity (like an email address or Sui address). The recipient can only decrypt if they can prove they control that identity.

2. Threshold Encryption

Instead of trusting a single key server, Seal uses t-of-n threshold schemes. You might configure 3-of-5 key servers, meaning any 3 servers can cooperate to provide decryption keys, but 2 or fewer cannot.

3. Onchain Access Control

Access policies are enforced by Sui smart contracts. Before a key server provides decryption keys, it verifies that the requestor meets the onchain policy requirements (token ownership, time constraints, etc.).

4. Key Server Network

Distributed key servers validate access policies and generate decryption keys. These servers are operated by different parties to ensure no single point of control.


Basic Implementation: Your First Seal Application

Let's build a simple application that encrypts sensitive data and controls access through Sui blockchain policies.

Step 1: Initialize the Seal Client

// src/seal-client.ts
import { SealClient } from '@mysten/seal';
import { SuiClient } from '@mysten/sui/client';

export async function createSealClient() {
// Initialize Sui client for testnet
const suiClient = new SuiClient({
url: 'https://fullnode.testnet.sui.io'
});

// Configure Seal client with testnet key servers
const sealClient = new SealClient({
suiClient,
keyServers: [
'https://keyserver1.seal-testnet.com',
'https://keyserver2.seal-testnet.com',
'https://keyserver3.seal-testnet.com'
],
threshold: 2, // 2-of-3 threshold
network: 'testnet'
});

return { sealClient, suiClient };
}

Step 2: Simple Encryption/Decryption

// src/basic-encryption.ts
import { createSealClient } from './seal-client';

async function basicExample() {
const { sealClient } = await createSealClient();

// Data to encrypt
const sensitiveData = "This is my secret message!";
const recipientAddress = "0x742d35cc6d4c0c08c0f9bf3c9b2b6c64b3b4f5c6d7e8f9a0b1c2d3e4f5a6b7c8";

try {
// Encrypt data for a specific Sui address
const encryptedData = await sealClient.encrypt({
data: Buffer.from(sensitiveData, 'utf-8'),
recipientId: recipientAddress,
// Optional: add metadata
metadata: {
contentType: 'text/plain',
timestamp: Date.now()
}
});

console.log('Encrypted data:', {
ciphertext: encryptedData.ciphertext.toString('base64'),
encryptionId: encryptedData.encryptionId
});

// Later, decrypt the data (requires proper authorization)
const decryptedData = await sealClient.decrypt({
ciphertext: encryptedData.ciphertext,
encryptionId: encryptedData.encryptionId,
recipientId: recipientAddress
});

console.log('Decrypted data:', decryptedData.toString('utf-8'));

} catch (error) {
console.error('Encryption/decryption failed:', error);
}
}

basicExample();

Access Control with Sui Smart Contracts

The real power of Seal comes from programmable access control. Let's create a time-locked encryption example where data can only be decrypted after a specific time.

Step 1: Deploy Access Control Contract

First, we need a Move smart contract that defines our access policy:

// contracts/time_lock.move
module time_lock::policy {
use sui::clock::{Self, Clock};
use sui::object::{Self, UID};
use sui::tx_context::{Self, TxContext};

public struct TimeLockPolicy has key, store {
id: UID,
unlock_time: u64,
authorized_user: address,
}

public fun create_time_lock(
unlock_time: u64,
authorized_user: address,
ctx: &mut TxContext
): TimeLockPolicy {
TimeLockPolicy {
id: object::new(ctx),
unlock_time,
authorized_user,
}
}

public fun can_decrypt(
policy: &TimeLockPolicy,
user: address,
clock: &Clock
): bool {
let current_time = clock::timestamp_ms(clock);
policy.authorized_user == user && current_time >= policy.unlock_time
}
}

Step 2: Integrate with Seal

// src/time-locked-encryption.ts
import { createSealClient } from './seal-client';
import { TransactionBlock } from '@mysten/sui/transactions';

async function createTimeLocked() {
const { sealClient, suiClient } = await createSealClient();

// Create access policy on Sui
const txb = new TransactionBlock();

const unlockTime = Date.now() + 60000; // Unlock in 1 minute
const authorizedUser = "0x742d35cc6d4c0c08c0f9bf3c9b2b6c64b3b4f5c6d7e8f9a0b1c2d3e4f5a6b7c8";

txb.moveCall({
target: 'time_lock::policy::create_time_lock',
arguments: [
txb.pure(unlockTime),
txb.pure(authorizedUser)
]
});

// Execute transaction to create policy
const result = await suiClient.signAndExecuteTransactionBlock({
transactionBlock: txb,
signer: yourKeypair, // Your Sui keypair
});

const policyId = result.objectChanges?.find(
change => change.type === 'created'
)?.objectId;

// Now encrypt with this policy
const sensitiveData = "This will unlock in 1 minute!";

const encryptedData = await sealClient.encrypt({
data: Buffer.from(sensitiveData, 'utf-8'),
recipientId: authorizedUser,
accessPolicy: {
policyId,
policyType: 'time_lock'
}
});

console.log('Time-locked data created. Try decrypting after 1 minute.');

return {
encryptedData,
policyId,
unlockTime
};
}

Practical Examples

Example 1: Secure Messaging Application

// src/secure-messaging.ts
import { createSealClient } from './seal-client';

class SecureMessenger {
private sealClient: any;

constructor(sealClient: any) {
this.sealClient = sealClient;
}

async sendMessage(
message: string,
recipientAddress: string,
senderKeypair: any
) {
const messageData = {
content: message,
timestamp: Date.now(),
sender: senderKeypair.toSuiAddress(),
messageId: crypto.randomUUID()
};

const encryptedMessage = await this.sealClient.encrypt({
data: Buffer.from(JSON.stringify(messageData), 'utf-8'),
recipientId: recipientAddress,
metadata: {
type: 'secure_message',
sender: senderKeypair.toSuiAddress()
}
});

// Store encrypted message on decentralized storage (Walrus)
return this.storeOnWalrus(encryptedMessage);
}

async readMessage(encryptionId: string, recipientKeypair: any) {
// Retrieve from storage
const encryptedData = await this.retrieveFromWalrus(encryptionId);

// Decrypt with Seal
const decryptedData = await this.sealClient.decrypt({
ciphertext: encryptedData.ciphertext,
encryptionId: encryptedData.encryptionId,
recipientId: recipientKeypair.toSuiAddress()
});

return JSON.parse(decryptedData.toString('utf-8'));
}

private async storeOnWalrus(data: any) {
// Integration with Walrus storage
// This would upload the encrypted data to Walrus
// and return the blob ID for retrieval
}

private async retrieveFromWalrus(blobId: string) {
// Retrieve encrypted data from Walrus using blob ID
}
}

Example 2: Token-Gated Content Platform

// src/gated-content.ts
import { createSealClient } from './seal-client';

class ContentGating {
private sealClient: any;
private suiClient: any;

constructor(sealClient: any, suiClient: any) {
this.sealClient = sealClient;
this.suiClient = suiClient;
}

async createGatedContent(
content: string,
requiredNftCollection: string,
creatorKeypair: any
) {
// Create NFT ownership policy
const accessPolicy = await this.createNftPolicy(
requiredNftCollection,
creatorKeypair
);

// Encrypt content with NFT access requirement
const encryptedContent = await this.sealClient.encrypt({
data: Buffer.from(content, 'utf-8'),
recipientId: 'nft_holders', // Special recipient for NFT holders
accessPolicy: {
policyId: accessPolicy.policyId,
policyType: 'nft_ownership'
}
});

return {
contentId: encryptedContent.encryptionId,
accessPolicy: accessPolicy.policyId
};
}

async accessGatedContent(
contentId: string,
userAddress: string,
userKeypair: any
) {
// Verify NFT ownership first
const hasAccess = await this.verifyNftOwnership(
userAddress,
contentId
);

if (!hasAccess) {
throw new Error('Access denied: Required NFT not found');
}

// Decrypt content
const decryptedContent = await this.sealClient.decrypt({
encryptionId: contentId,
recipientId: userAddress
});

return decryptedContent.toString('utf-8');
}

private async createNftPolicy(collection: string, creator: any) {
// Create Move contract that checks NFT ownership
// Returns policy object ID
}

private async verifyNftOwnership(user: string, contentId: string) {
// Check if user owns required NFT
// Query Sui for NFT ownership
}
}

Example 3: Time-Locked Asset Transfer

// src/time-locked-transfer.ts
import { createSealClient } from './seal-client';

async function createTimeLockTransfer(
assetData: any,
recipientAddress: string,
unlockTimestamp: number,
senderKeypair: any
) {
const { sealClient, suiClient } = await createSealClient();

// Create time-lock policy on Sui
const timeLockPolicy = await createTimeLockPolicy(
unlockTimestamp,
recipientAddress,
senderKeypair,
suiClient
);

// Encrypt asset transfer data
const transferData = {
asset: assetData,
recipient: recipientAddress,
unlockTime: unlockTimestamp,
transferId: crypto.randomUUID()
};

const encryptedTransfer = await sealClient.encrypt({
data: Buffer.from(JSON.stringify(transferData), 'utf-8'),
recipientId: recipientAddress,
accessPolicy: {
policyId: timeLockPolicy.policyId,
policyType: 'time_lock'
}
});

console.log(`Asset locked until ${new Date(unlockTimestamp)}`);

return {
transferId: encryptedTransfer.encryptionId,
unlockTime: unlockTimestamp,
policyId: timeLockPolicy.policyId
};
}

async function claimTimeLockTransfer(
transferId: string,
recipientKeypair: any
) {
const { sealClient } = await createSealClient();

try {
const decryptedData = await sealClient.decrypt({
encryptionId: transferId,
recipientId: recipientKeypair.toSuiAddress()
});

const transferData = JSON.parse(decryptedData.toString('utf-8'));

// Process the asset transfer
console.log('Asset transfer unlocked:', transferData);

return transferData;
} catch (error) {
console.error('Transfer not yet unlocked or access denied:', error);
throw error;
}
}

Integration with Walrus Decentralized Storage

Seal works seamlessly with Walrus, Sui's decentralized storage solution. Here's how to integrate both:

// src/walrus-integration.ts
import { createSealClient } from './seal-client';

class SealWalrusIntegration {
private sealClient: any;
private walrusClient: any;

constructor(sealClient: any, walrusClient: any) {
this.sealClient = sealClient;
this.walrusClient = walrusClient;
}

async storeEncryptedData(
data: Buffer,
recipientAddress: string,
accessPolicy?: any
) {
// Encrypt with Seal
const encryptedData = await this.sealClient.encrypt({
data,
recipientId: recipientAddress,
accessPolicy
});

// Store encrypted data on Walrus
const blobId = await this.walrusClient.store(
encryptedData.ciphertext
);

// Return reference that includes both Seal and Walrus info
return {
blobId,
encryptionId: encryptedData.encryptionId,
accessPolicy: encryptedData.accessPolicy
};
}

async retrieveAndDecrypt(
blobId: string,
encryptionId: string,
userKeypair: any
) {
// Retrieve from Walrus
const encryptedData = await this.walrusClient.retrieve(blobId);

// Decrypt with Seal
const decryptedData = await this.sealClient.decrypt({
ciphertext: encryptedData,
encryptionId,
recipientId: userKeypair.toSuiAddress()
});

return decryptedData;
}
}

// Usage example
async function walrusExample() {
const { sealClient } = await createSealClient();
const walrusClient = new WalrusClient('https://walrus-testnet.sui.io');

const integration = new SealWalrusIntegration(sealClient, walrusClient);

const fileData = Buffer.from('Important document content');
const recipientAddress = '0x...';

// Store encrypted
const result = await integration.storeEncryptedData(
fileData,
recipientAddress
);

console.log('Stored with Blob ID:', result.blobId);

// Later, retrieve and decrypt
const decrypted = await integration.retrieveAndDecrypt(
result.blobId,
result.encryptionId,
recipientKeypair
);

console.log('Retrieved data:', decrypted.toString());
}

Threshold Encryption Advanced Configuration

For production applications, you'll want to configure custom threshold encryption with multiple key servers:

// src/advanced-threshold.ts
import { SealClient } from '@mysten/seal';

async function setupProductionSeal() {
// Configure with multiple independent key servers
const keyServers = [
'https://keyserver-1.your-org.com',
'https://keyserver-2.partner-org.com',
'https://keyserver-3.third-party.com',
'https://keyserver-4.backup-provider.com',
'https://keyserver-5.fallback.com'
];

const sealClient = new SealClient({
keyServers,
threshold: 3, // 3-of-5 threshold
network: 'mainnet',
// Advanced options
retryAttempts: 3,
timeoutMs: 10000,
backupKeyServers: [
'https://backup-1.emergency.com',
'https://backup-2.emergency.com'
]
});

return sealClient;
}

async function robustEncryption() {
const sealClient = await setupProductionSeal();

const criticalData = "Mission critical encrypted data";

// Encrypt with high security guarantees
const encrypted = await sealClient.encrypt({
data: Buffer.from(criticalData, 'utf-8'),
recipientId: '0x...',
// Require all 5 servers for maximum security
customThreshold: 5,
// Add redundancy
redundancy: 2,
accessPolicy: {
// Multi-factor requirements
requirements: ['nft_ownership', 'time_lock', 'multisig_approval']
}
});

return encrypted;
}

Security Best Practices

1. Key Management

// src/security-practices.ts

// GOOD: Use secure key derivation
import { generateKeypair } from '@mysten/sui/cryptography/ed25519';

const keypair = generateKeypair();

// GOOD: Store keys securely (example with environment variables)
const keypair = Ed25519Keypair.fromSecretKey(
process.env.PRIVATE_KEY
);

// BAD: Never hardcode keys
const badKeypair = Ed25519Keypair.fromSecretKey(
"hardcoded-secret-key-12345" // Don't do this!
);

2. Access Policy Validation

// Always validate access policies before encryption
async function secureEncrypt(data: Buffer, recipient: string) {
const { sealClient } = await createSealClient();

// Validate recipient address
if (!isValidSuiAddress(recipient)) {
throw new Error('Invalid recipient address');
}

// Check policy exists and is valid
const policy = await validateAccessPolicy(policyId);
if (!policy.isValid) {
throw new Error('Invalid access policy');
}

return sealClient.encrypt({
data,
recipientId: recipient,
accessPolicy: policy
});
}

3. Error Handling and Fallbacks

// Robust error handling
async function resilientDecrypt(encryptionId: string, userKeypair: any) {
const { sealClient } = await createSealClient();

try {
return await sealClient.decrypt({
encryptionId,
recipientId: userKeypair.toSuiAddress()
});
} catch (error) {
if (error.code === 'ACCESS_DENIED') {
throw new Error('Access denied: Check your permissions');
} else if (error.code === 'KEY_SERVER_UNAVAILABLE') {
// Try with backup configuration
return await retryWithBackupServers(encryptionId, userKeypair);
} else if (error.code === 'THRESHOLD_NOT_MET') {
throw new Error('Insufficient key servers available');
} else {
throw new Error(`Decryption failed: ${error.message}`);
}
}
}

4. Data Validation

// Validate data before encryption
function validateDataForEncryption(data: Buffer): boolean {
// Check size limits
if (data.length > 1024 * 1024) { // 1MB limit
throw new Error('Data too large for encryption');
}

// Check for sensitive patterns (optional)
const dataStr = data.toString();
if (containsSensitivePatterns(dataStr)) {
console.warn('Warning: Data contains potentially sensitive patterns');
}

return true;
}

Performance Optimization

1. Batching Operations

// Batch multiple encryptions for efficiency
async function batchEncrypt(dataItems: Buffer[], recipients: string[]) {
const { sealClient } = await createSealClient();

const promises = dataItems.map((data, index) =>
sealClient.encrypt({
data,
recipientId: recipients[index]
})
);

return Promise.all(promises);
}

2. Caching Key Server Responses

// Cache key server sessions to reduce latency
class OptimizedSealClient {
private sessionCache = new Map();

async encryptWithCaching(data: Buffer, recipient: string) {
let session = this.sessionCache.get(recipient);

if (!session || this.isSessionExpired(session)) {
session = await this.createNewSession(recipient);
this.sessionCache.set(recipient, session);
}

return this.encryptWithSession(data, session);
}
}

Testing Your Seal Integration

Unit Testing

// tests/seal-integration.test.ts
import { describe, it, expect } from 'jest';
import { createSealClient } from '../src/seal-client';

describe('Seal Integration', () => {
it('should encrypt and decrypt data successfully', async () => {
const { sealClient } = await createSealClient();
const testData = Buffer.from('test message');
const recipient = '0x742d35cc6d4c0c08c0f9bf3c9b2b6c64b3b4f5c6d7e8f9a0b1c2d3e4f5a6b7c8';

const encrypted = await sealClient.encrypt({
data: testData,
recipientId: recipient
});

expect(encrypted.encryptionId).toBeDefined();
expect(encrypted.ciphertext).toBeDefined();

const decrypted = await sealClient.decrypt({
ciphertext: encrypted.ciphertext,
encryptionId: encrypted.encryptionId,
recipientId: recipient
});

expect(decrypted.toString()).toBe('test message');
});

it('should enforce access control policies', async () => {
// Test that unauthorized users cannot decrypt
const { sealClient } = await createSealClient();

const encrypted = await sealClient.encrypt({
data: Buffer.from('secret'),
recipientId: 'authorized-user'
});

await expect(
sealClient.decrypt({
ciphertext: encrypted.ciphertext,
encryptionId: encrypted.encryptionId,
recipientId: 'unauthorized-user'
})
).rejects.toThrow('Access denied');
});
});

Deployment to Production

Environment Configuration

// config/production.ts
export const productionConfig = {
keyServers: [
process.env.KEY_SERVER_1,
process.env.KEY_SERVER_2,
process.env.KEY_SERVER_3,
process.env.KEY_SERVER_4,
process.env.KEY_SERVER_5
],
threshold: 3,
network: 'mainnet',
suiRpc: process.env.SUI_RPC_URL,
walrusGateway: process.env.WALRUS_GATEWAY,
// Security settings
maxDataSize: 1024 * 1024, // 1MB
sessionTimeout: 3600000, // 1 hour
retryAttempts: 3
};

Monitoring and Logging

// utils/monitoring.ts
export class SealMonitoring {
static logEncryption(encryptionId: string, recipient: string) {
console.log(`[SEAL] Encrypted data ${encryptionId} for ${recipient}`);
// Send to your monitoring service
}

static logDecryption(encryptionId: string, success: boolean) {
console.log(`[SEAL] Decryption ${encryptionId}: ${success ? 'SUCCESS' : 'FAILED'}`);
}

static logKeyServerHealth(serverUrl: string, status: string) {
console.log(`[SEAL] Key server ${serverUrl}: ${status}`);
}
}

Resources and Next Steps

Official Documentation

Community and Support

  • Sui Discord: Join the #seal channel for community support
  • GitHub Issues: Report bugs and request features
  • Developer Forums: Sui community forums for discussions

Advanced Topics to Explore

  1. Custom Access Policies: Build complex authorization logic with Move contracts
  2. Cross-Chain Integration: Use Seal with other blockchain networks
  3. Enterprise Key Management: Set up your own key server infrastructure
  4. Audit and Compliance: Implement logging and monitoring for regulated environments

Sample Applications

  • Secure Chat App: End-to-end encrypted messaging with Seal
  • Document Management: Enterprise document sharing with access controls
  • Digital Rights Management: Content distribution with usage policies
  • Privacy-Preserving Analytics: Encrypted data processing workflows

Conclusion

Seal represents a fundamental shift toward making privacy and encryption infrastructure-level concerns in Web3. By combining identity-based encryption, threshold security, and programmable access control, it provides developers with powerful tools to build truly secure and decentralized applications.

The key advantages of building with Seal include:

  • No Single Point of Failure: Distributed key servers eliminate central authorities
  • Programmable Security: Smart contract-based access policies provide flexible authorization
  • Developer-Friendly: TypeScript SDK integrates seamlessly with existing Web3 tooling
  • Storage Agnostic: Works with Walrus, IPFS, or any storage solution
  • Production Ready: Built by Mysten Labs with enterprise security standards

Whether you're securing user data, implementing subscription models, or building complex multi-party applications, Seal provides the cryptographic primitives and access control infrastructure you need to build with confidence.

Start building today, and join the growing ecosystem of developers making privacy a fundamental part of public infrastructure.


Ready to start building? Install @mysten/seal and begin experimenting with the examples in this tutorial. The decentralized web is waiting for applications that put privacy and security first.

Seal on Sui: A Programmable Secrets Layer for On-Chain Access Control

· 4 min read
Dora Noda
Software Engineer

Public blockchains give every participant a synchronized, auditable ledger—but they also expose every piece of data by default. Seal, now live on Sui Mainnet as of September 3, 2025, addresses this by pairing on-chain policy logic with decentralized key management so that Web3 builders can decide exactly who gets to decrypt which payloads.

TL;DR

  • What it is: Seal is a secrets-management network that lets Sui smart contracts enforce decryption policies on-chain while clients encrypt data with identity-based encryption (IBE) and rely on threshold key servers for key derivation.
  • Why it matters: Instead of custom backends or opaque off-chain scripts, privacy and access control become first-class Move primitives. Builders can store ciphertexts anywhere—Walrus is the natural companion—but still gate who can read.
  • Who benefits: Teams shipping token-gated media, time-locked reveals, private messaging, or policy-aware AI agents can plug into Seal’s SDK and focus on product logic, not bespoke crypto plumbing.

Policy Logic Lives in Move

Seal packages come with seal_approve* Move functions that define who can request keys for a given identity string and under which conditions. Policies can mix NFT ownership, allowlists, time locks, or custom role systems. When a user or agent asks to decrypt, key servers evaluate these policies via Sui full-node state and only approve if the chain agrees.

Because the access rules are part of your on-chain package, they are transparent, auditable, and versionable alongside the rest of your smart contract code. Governance updates can be rolled out like any other Move upgrade, with community review and on-chain history.

Threshold Cryptography Handles the Keys

Seal encrypts data to application-defined identities. A committee of independent key servers—chosen by the developer—shares the IBE master secret. When a policy check passes, each server derives a key share for the requested identity. Once a quorum of t servers responds, the client combines the shares into a usable decryption key.

You get to set the trade-off between liveness and confidentiality by picking committee members (Ruby Nodes, NodeInfra, Overclock, Studio Mirai, H2O Nodes, Triton One, or Mysten’s Enoki service) and selecting the threshold. Need stronger availability? Choose a larger committee with a lower threshold. Want higher privacy assurances? Tighten the quorum and lean on permissioned providers.

Developer Experience: SDKs and Session Keys

Seal ships a TypeScript SDK (npm i @mysten/seal) that handles encrypt/decrypt flows, identity formatting, and batching. It also issues session keys so wallets are not constantly spammed with prompts when an app needs repeated access. For advanced workflows, Move contracts can request on-chain decryption via specialized modes, allowing logic like escrow reveals or MEV-resistant auctions to run directly in smart contract code.

Because Seal is storage-agnostic, teams can pair it with Walrus for verifiable blob storage, with IPFS, or even with centralized stores when that fits operational realities. The encryption boundary—and its policy enforcement—travels with the data regardless of where the ciphertext lives.

Designing with Seal: Best Practices

  • Model availability risk: Thresholds such as 2-of-3 or 3-of-5 map directly to uptime guarantees. Production deployments should mix providers, monitor telemetry, and negotiate SLAs before entrusting critical workflows.
  • Be mindful of state variance: Policy evaluation depends on full nodes performing dry_run calls. Avoid rules that hinge on rapidly changing counters or intra-checkpoint ordering to prevent inconsistent approvals across servers.
  • Plan for key hygiene: Derived keys live on the client. Instrument logging, rotate session keys, and consider envelope encryption—use Seal to protect a symmetric key that encrypts the larger payload—to limit blast radius if a device is compromised.
  • Architect for rotation: A ciphertext’s committee is fixed at encryption time. Build upgrade paths that re-encrypt data through new committees when you need to swap providers or adjust trust assumptions.

What Comes Next

Seal’s roadmap points toward validator-operated MPC servers, DRM-style client tooling, and post-quantum KEM options. For builders exploring AI agents, premium content, or regulated data flows, today’s release already provides a clear blueprint: encode your policy in Move, compose a diverse key committee, and deliver encrypted experiences that respect user privacy without leaving Sui’s trust boundary.

If you are considering Seal for your next launch, start by prototyping a simple NFT-gated policy with a 2-of-3 open committee, then iterate toward the provider mix and operational controls that match your app’s risk profile.