cifer
Scanned@TIP-citron
npx machina-cli add skill @TIP-citron/cifer-security --openclawCIFER SDK — Complete Integration Guide
Overview
CIFER SDK provides quantum-resistant encryption (ML-KEM-768 + AES-256-GCM) for blockchain apps. Secrets are on-chain key pairs: public key on IPFS, private key sharded across enclaves.
Package: cifer-sdk (npm)
Chains: Ethereum Mainnet (1), Sepolia (11155111), Ternoa (752025)
Blackbox URL: https://cifer-blackbox.ternoa.dev:3010
For the full API reference, see reference.md.
Quick Setup
npm install cifer-sdk ethers dotenv
package.json must have "type": "module" for ESM imports.
import 'dotenv/config';
import { createCiferSdk, keyManagement, blackbox } from 'cifer-sdk';
import { Wallet, JsonRpcProvider } from 'ethers';
Step 1: Initialize SDK
const sdk = await createCiferSdk({
blackboxUrl: 'https://cifer-blackbox.ternoa.dev:3010',
});
const chainId = 1; // Ethereum Mainnet (or 11155111 for Sepolia, 752025 for Ternoa)
const controllerAddress = sdk.getControllerAddress(chainId);
const rpcUrl = sdk.getRpcUrl(chainId);
sdk.getSupportedChainIds() returns all available chains.
Step 2: Create Wallet Signer (Server-Side)
const provider = new JsonRpcProvider(rpcUrl);
const wallet = new Wallet(process.env.PRIVATE_KEY, provider);
// Signer adapter — this is what the SDK expects
const signer = {
async getAddress() { return wallet.address; },
async signMessage(message) { return wallet.signMessage(message); },
};
For browser wallets, use the built-in adapter instead:
import { Eip1193SignerAdapter } from 'cifer-sdk';
const signer = new Eip1193SignerAdapter(window.ethereum);
Step 3: Create a Secret
A secret costs a fee in native token. Check balance first.
const fee = await keyManagement.getSecretCreationFee({
chainId, controllerAddress, readClient: sdk.readClient,
});
const txIntent = keyManagement.buildCreateSecretTx({ chainId, controllerAddress, fee });
const tx = await wallet.sendTransaction({
to: txIntent.to,
data: txIntent.data,
value: txIntent.value,
});
const receipt = await tx.wait();
const secretId = keyManagement.extractSecretIdFromReceipt(receipt.logs);
Step 4: Wait for Secret Sync
After creation, the enclave cluster generates keys (~30-120s on mainnet).
let ready = false;
while (!ready) {
ready = await keyManagement.isSecretReady(
{ chainId, controllerAddress, readClient: sdk.readClient },
secretId,
);
if (!ready) await new Promise(r => setTimeout(r, 5000));
}
Or read the full state:
const state = await keyManagement.getSecret(
{ chainId, controllerAddress, readClient: sdk.readClient },
secretId,
);
// state.owner, state.delegate, state.isSyncing, state.publicKeyCid
Step 5: Encrypt Text
const encrypted = await blackbox.payload.encryptPayload({
chainId,
secretId,
plaintext: 'Your secret message',
signer,
readClient: sdk.readClient,
blackboxUrl: sdk.blackboxUrl,
});
// Returns: { cifer, encryptedMessage }
Step 6: Decrypt Text
Caller must be secret owner or delegate.
const decrypted = await blackbox.payload.decryptPayload({
chainId,
secretId,
encryptedMessage: encrypted.encryptedMessage,
cifer: encrypted.cifer,
signer,
readClient: sdk.readClient,
blackboxUrl: sdk.blackboxUrl,
});
// Returns: { decryptedMessage }
Step 7: Encrypt File
File operations are async jobs. Works with Blob in Node.js 18+.
import { readFile, writeFile } from 'fs/promises';
const buffer = await readFile('myfile.pdf');
const blob = new Blob([buffer], { type: 'application/pdf' });
// Start encrypt job
const job = await blackbox.files.encryptFile({
chainId, secretId, file: blob, signer,
readClient: sdk.readClient, blackboxUrl: sdk.blackboxUrl,
});
// Poll until done
const status = await blackbox.jobs.pollUntilComplete(job.jobId, sdk.blackboxUrl, {
intervalMs: 2000,
maxAttempts: 120,
onProgress: (j) => console.log(`${j.progress}%`),
});
// Download encrypted .cifer file (no auth needed for encrypt jobs)
const encBlob = await blackbox.jobs.download(job.jobId, { blackboxUrl: sdk.blackboxUrl });
await writeFile('myfile.pdf.cifer', Buffer.from(await encBlob.arrayBuffer()));
Step 8: Decrypt File
const encBuffer = await readFile('myfile.pdf.cifer');
const encBlob = new Blob([encBuffer]);
const decJob = await blackbox.files.decryptFile({
chainId, secretId, file: encBlob, signer,
readClient: sdk.readClient, blackboxUrl: sdk.blackboxUrl,
});
const decStatus = await blackbox.jobs.pollUntilComplete(decJob.jobId, sdk.blackboxUrl, {
intervalMs: 2000, maxAttempts: 120,
});
// Download decrypted file (auth REQUIRED for decrypt jobs)
const decBlob = await blackbox.jobs.download(decJob.jobId, {
blackboxUrl: sdk.blackboxUrl,
chainId, secretId, signer, readClient: sdk.readClient,
});
await writeFile('myfile-decrypted.pdf', Buffer.from(await decBlob.arrayBuffer()));
List Existing Secrets
const secrets = await keyManagement.getSecretsByWallet(
{ chainId, controllerAddress, readClient: sdk.readClient },
wallet.address,
);
// secrets.owned: bigint[] — secrets you own
// secrets.delegated: bigint[] — secrets delegated to you
Delegation
Set a delegate (can decrypt but not encrypt or modify):
const txIntent = keyManagement.buildSetDelegateTx({
chainId, controllerAddress, secretId, newDelegate: '0xDelegateAddress',
});
await wallet.sendTransaction({ to: txIntent.to, data: txIntent.data });
Remove delegation:
const txIntent = keyManagement.buildRemoveDelegationTx({
chainId, controllerAddress, secretId,
});
Important Notes
- Minimum SDK version: Use
cifer-sdk@0.3.1or later. Earlier versions had incorrect function selectors. - Payload size limit: Text encryption max ~16KB (
encryptPayload). Use file encryption for larger data. - Block freshness: The SDK auto-retries up to 3 times if the block number becomes stale.
- Secret sync time: ~30-60s on Ternoa, ~60-120s on Ethereum mainnet.
- Auth for file download: Encrypt job downloads need no auth. Decrypt job downloads require signer + readClient.
- Fee: Secret creation requires a fee in native token (e.g. ~0.0005 ETH on mainnet). Query
getSecretCreationFee()first. - Private keys: Never expose private keys in frontend code. Use server-side signer for Node.js.
Error Handling
import { isCiferError, isBlockStaleError } from 'cifer-sdk';
try {
await blackbox.payload.encryptPayload({ ... });
} catch (error) {
if (isBlockStaleError(error)) {
// RPC returning stale blocks, SDK already retried 3x
} else if (error instanceof SecretNotReadyError) {
// Wait and retry
} else if (isCiferError(error)) {
console.error(error.code, error.message);
}
}
Complete Minimal Example
import 'dotenv/config';
import { createCiferSdk, keyManagement, blackbox } from 'cifer-sdk';
import { Wallet, JsonRpcProvider } from 'ethers';
const sdk = await createCiferSdk({ blackboxUrl: 'https://cifer-blackbox.ternoa.dev:3010' });
const chainId = 1;
const controllerAddress = sdk.getControllerAddress(chainId);
const provider = new JsonRpcProvider(sdk.getRpcUrl(chainId));
const wallet = new Wallet(process.env.PRIVATE_KEY, provider);
const signer = {
async getAddress() { return wallet.address; },
async signMessage(msg) { return wallet.signMessage(msg); },
};
// Create secret
const fee = await keyManagement.getSecretCreationFee({ chainId, controllerAddress, readClient: sdk.readClient });
const txIntent = keyManagement.buildCreateSecretTx({ chainId, controllerAddress, fee });
const tx = await wallet.sendTransaction({ to: txIntent.to, data: txIntent.data, value: txIntent.value });
const receipt = await tx.wait();
const secretId = keyManagement.extractSecretIdFromReceipt(receipt.logs);
// Wait for sync
let ready = false;
while (!ready) {
ready = await keyManagement.isSecretReady({ chainId, controllerAddress, readClient: sdk.readClient }, secretId);
if (!ready) await new Promise(r => setTimeout(r, 5000));
}
// Encrypt & decrypt
const enc = await blackbox.payload.encryptPayload({
chainId, secretId, plaintext: 'Hello CIFER!', signer,
readClient: sdk.readClient, blackboxUrl: sdk.blackboxUrl,
});
const dec = await blackbox.payload.decryptPayload({
chainId, secretId, encryptedMessage: enc.encryptedMessage, cifer: enc.cifer,
signer, readClient: sdk.readClient, blackboxUrl: sdk.blackboxUrl,
});
console.log(dec.decryptedMessage); // "Hello CIFER!"
Overview
CIFER SDK delivers quantum-resistant encryption for blockchain apps using ML-KEM-768 + AES-256-GCM. Secrets are on-chain key pairs with the public key on IPFS and the private key sharded across enclaves, enabling secure text and file encryption across Ethereum, Sepolia, and Ternoa via the CIFER blackbox.
How This Skill Works
Initialize the SDK with the blackbox URL, create a wallet signer, and register a secret on a chosen chain. After the enclave sync completes, use the blackbox payload APIs to encrypt and decrypt text or files, receiving a cifer payload alongside the encrypted data for secure on-chain workflows.
When to Use It
- You need post-quantum secure encryption for on-chain payloads and messages on supported chains.
- You must encrypt textual data before transmission or storage for blockchain apps.
- You require secure file encryption and decryption with on-chain management of keys.
- You want crypto-key material managed as on-chain secrets with IPFS-backed public keys.
- You are integrating CIFER into Ethereum Mainnet, Sepolia, or Ternoa using the cifer-sdk npm package.
Quick Start
- Step 1: npm install cifer-sdk ethers dotenv and prepare a .env with PRIVATE_KEY and RPCs
- Step 2: Import modules and initialize the SDK with the blackbox URL, then select a chainId
- Step 3: Create a wallet signer, register a secret, wait for sync, then encrypt or decrypt payloads
Best Practices
- Validate the chainId and controllerAddress before creating or using secrets.
- Check the secret creation fee and ensure sufficient native token balance prior to tx.
- Use the server-side signer for trusted environments, or the EIP-1193 signer adapter for browsers.
- Wait for secret sync to complete (state.isSyncing) before attempting encryption.
- Store and handle the blackboxUrl and readClient references securely; rotate keys as needed.
Example Use Cases
- Encrypt a sensitive on-chain message on Ethereum Mainnet and decrypt only by the designated delegate.
- Store an encrypted payload in IPFS with the public key CID, while keeping the private key shards across enclaves for recovery.
- Encrypt large files client-side, push encrypted payloads to a blockchain app, then decrypt on authorized devices.
- Use Sepolia as a staging environment to test key creation, sync, and encryption flows before mainnet deployment.
- Implement a workflow where a wallet signer signs the secret creation transaction and later decrypts a payload with the secret owner or delegate.