Signer Management
Comprehensive guide to managing wallet signers and authentication methods
Signer Management Guide
This guide covers comprehensive signer management including creating different signer types, managing permissions, multi-signer patterns, and signer recovery.
Understanding Signers
Signers are authentication mechanisms that authorize transactions for a wallet. Each wallet can have multiple signers with different authentication methods and permissions.
Signer Types Overview
| Type | Custody | Use Case | Security Level |
|---|---|---|---|
| Non-custodial | Web applications, email recovery | Medium | |
| Phone | Non-custodial | Mobile apps, phone verification | Medium |
| Passkey | Non-custodial | Modern web auth, biometric | High |
| External Wallet | Non-custodial | Existing wallet users | High |
| API Key | Custodial | Server-side operations | Medium |
Creating Signers
Email Signer
Email signers use email-based authentication with device keys for non-custodial control.
import { createEmailSignerHelper } from 'cilantro-sdk/helpers';
import { createLocalStorageAdapter } from 'cilantro-sdk/helpers';
const storage = createLocalStorageAdapter();
const signer = await createEmailSignerHelper('wallet-id', {
email: 'user@example.com',
deviceKeyManager: storage
});
console.log('Email signer created:', signer.signerId);
Phone Signer
Phone signers use SMS-based authentication with device keys.
import { createPhoneSignerHelper } from 'cilantro-sdk/helpers';
const signer = await createPhoneSignerHelper('wallet-id', {
phone: '+1234567890',
deviceKeyManager: storage
});
console.log('Phone signer created:', signer.signerId);
Passkey Signer
Passkey signers use WebAuthn/FIDO2 for hardware-backed authentication.
import { startPasskeyRegistration, verifyPasskeyRegistration } from 'cilantro-sdk/wallet';
// Step 1: Start registration
const options = await startPasskeyRegistration('wallet-id');
// Step 2: Create credential
const credential = await navigator.credentials.create({
publicKey: options.data
});
// Step 3: Verify and create signer
const signer = await verifyPasskeyRegistration('wallet-id', {
response: credential.response,
credentialId: credential.id
});
External Wallet Signer
Link an external wallet (Phantom, Solflare, etc.) as a signer.
import { createExternalWalletSigner } from 'cilantro-sdk/signers';
const signer = await createExternalWalletSigner('wallet-id', {
publicKey: 'external-wallet-public-key'
});
API Key Signer
Create a custodial API key signer for server-side operations.
import { createApiKeySigner } from 'cilantro-sdk/signers';
const signer = await createApiKeySigner('wallet-id', {
apiKeyName: 'Server Signer'
});
Managing Signer Permissions
Read-Only Signer
Create a signer that can only view wallet information.
import { updateSignerPermissions } from 'cilantro-sdk/signers';
await updateSignerPermissions('wallet-id', 'signer-public-key', {
permissions: {
canSendSol: false,
canSendTokens: false,
canExecuteCpi: false
}
});
Limited Transfer Signer
Create a signer with transfer limits.
await updateSignerPermissions('wallet-id', 'signer-public-key', {
permissions: {
canSendSol: true,
canSendTokens: true,
canExecuteCpi: false,
maxTransferAmount: 1000000000 // 1 SOL limit
}
});
Program-Specific Signer
Restrict signer to specific programs.
await updateSignerPermissions('wallet-id', 'signer-public-key', {
permissions: {
canSendSol: false,
canSendTokens: false,
canExecuteCpi: true,
allowedPrograms: [
'program-id-1',
'program-id-2'
]
}
});
Full Access Signer
Grant complete permissions to a signer.
await updateSignerPermissions('wallet-id', 'signer-public-key', {
permissions: {
canSendSol: true,
canSendTokens: true,
canExecuteCpi: true
}
});
Multi-Signer Patterns
Primary + Recovery Pattern
Set up a primary signer with recovery options.
// Create wallet
const wallet = await create({ label: 'Secure Wallet' });
// Add primary email signer
const primary = await createEmailSignerHelper(wallet.data.walletId, {
email: 'user@example.com',
deviceKeyManager: storage
});
// Add phone signer as recovery
const recovery = await createPhoneSignerHelper(wallet.data.walletId, {
phone: '+1234567890',
deviceKeyManager: storage
});
// Set primary signer
await updateSigner(wallet.data.walletId, primary.signerId, {
isPrimary: true
});
Multi-Factor Authentication
Require multiple signers for sensitive operations.
// Add email signer
const emailSigner = await createEmailSignerHelper(walletId, {
email: 'user@example.com',
deviceKeyManager: storage
});
// Add passkey signer for high-value transactions
const passkeySigner = await registerPasskey(walletId);
// Both signers must approve high-value transactions
// (Implementation depends on your wallet's multi-sig configuration)
Shared Wallet Pattern
Multiple users can sign transactions.
// User 1's signer
const user1Signer = await createEmailSignerHelper(walletId, {
email: 'user1@example.com',
deviceKeyManager: storage1
});
// User 2's signer
const user2Signer = await createEmailSignerHelper(walletId, {
email: 'user2@example.com',
deviceKeyManager: storage2
});
// User 3's signer
const user3Signer = await createEmailSignerHelper(walletId, {
email: 'user3@example.com',
deviceKeyManager: storage3
});
// Configure for 2-of-3 multi-sig
// (Requires wallet program configuration)
Signer Lifecycle Management
Listing Signers
import { listSigners } from 'cilantro-sdk/signers';
const signers = await listSigners('wallet-id');
console.log('Total signers:', signers.data.length);
signers.data.forEach(signer => {
console.log(`- ${signer.type}: ${signer.isActive ? 'Active' : 'Inactive'}`);
});
Getting Signer Details
import { getSignerById } from 'cilantro-sdk/signers';
const signer = await getSignerById('wallet-id', 'signer-id');
console.log('Signer type:', signer.data.type);
console.log('Is active:', signer.data.isActive);
console.log('Is primary:', signer.data.isPrimary);
Updating Signer Status
import { updateSigner } from 'cilantro-sdk/signers';
// Deactivate a signer temporarily
await updateSigner('wallet-id', 'signer-id', {
isActive: false
});
// Reactivate later
await updateSigner('wallet-id', 'signer-id', {
isActive: true
});
// Change primary signer
await updateSigner('wallet-id', 'new-primary-signer-id', {
isPrimary: true
});
Removing Signers
import { deleteSigner } from 'cilantro-sdk/signers';
// Remove a signer
await deleteSigner('wallet-id', 'signer-id');
At least one signer must remain active. You cannot delete the last active signer.
Signer Recovery
Device Recovery for Email/Phone Signers
import {
addNewDeviceToSigner,
replaceDeviceIdentity
} from 'cilantro-sdk/helpers';
// Add a new device to existing signer
const newDeviceKey = await generateDeviceKeyPair();
await addNewDeviceToSigner('wallet-id', 'signer-id', {
deviceKeyManager: storage
});
// Replace old device
await replaceDeviceIdentity('wallet-id', 'signer-id', {
oldDeviceId: 'old-device-id',
newIdentity: {
devicePublicKey: newDeviceKey.publicKey,
deviceId: 'new-device-id'
}
});
Passkey Recovery
// Register a backup passkey
const backupPasskey = await registerPasskey('wallet-id');
// If primary passkey is lost, use backup
const authResult = await authenticateWithPasskey('wallet-id', backupPasskey.credentialId);
Best Practices
Security
Always set up multiple signers (email + phone) for account recovery
Grant minimum necessary permissions to each signer
Regularly review and audit signer permissions
Maintain backup signers in case primary is lost
Organization
// Organize signers by purpose
async function organizeSigners(walletId: string) {
const signers = await listSigners(walletId);
const primary = signers.data.find(s => s.isPrimary);
const recovery = signers.data.filter(s => s.type === 'PHONE' || s.type === 'EMAIL');
const highSecurity = signers.data.filter(s => s.type === 'PASSKEY');
const external = signers.data.filter(s => s.type === 'EXTERNAL_WALLET');
const apiKeys = signers.data.filter(s => s.type === 'API_KEY');
return {
primary,
recovery,
highSecurity,
external,
apiKeys
};
}
Common Patterns
Tiered Security
// Low-value: Email signer
const emailSigner = await createEmailSignerHelper(walletId, {
email: 'user@example.com',
deviceKeyManager: storage
});
// Medium-value: Phone signer
const phoneSigner = await createPhoneSignerHelper(walletId, {
phone: '+1234567890',
deviceKeyManager: storage
});
// High-value: Passkey signer
const passkeySigner = await registerPasskey(walletId);
// Configure permissions based on value
await updateSignerPermissions(walletId, emailSigner.publicKey, {
permissions: {
canSendSol: true,
maxTransferAmount: 1000000000 // 1 SOL limit
}
});
await updateSignerPermissions(walletId, passkeySigner.publicKey, {
permissions: {
canSendSol: true,
canSendTokens: true,
canExecuteCpi: true
// No limit for high-security signer
}
});
Automated Operations
// Use API key signer for automated operations
const apiKeySigner = await createApiKeySigner(walletId, {
apiKeyName: 'Automated Bot'
});
// Configure for specific operations only
await updateSignerPermissions(walletId, apiKeySigner.publicKey, {
permissions: {
canSendSol: true,
canSendTokens: false,
canExecuteCpi: true,
allowedPrograms: ['specific-program-id']
}
});