Building Decentralized Encryption with @mysten/seal: A Developer's Tutorial
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
};
}