Signers Module
Manage wallet signers and authentication methods
Signers Module
The signers module provides comprehensive functionality for managing wallet signers, including creating different signer types, updating permissions, and managing signer lifecycle.
Import
import {
listSigners,
getSignerById,
createEmailSigner,
createPhoneSigner,
createPasskeySigner,
createExternalWalletSigner,
createApiKeySigner,
updateSigner,
updateSignerPermissions,
deleteSigner,
getCustodyInfo
} from 'cilantro-sdk/signers';
Basic Operations
listSigners(walletId)
List all signers for a wallet (both authentication and on-chain signers).
const signers = await listSigners('wallet-id');
console.log('Wallet signers:', signers.data);
getSignerById(walletId, signerId)
Get detailed information about a specific signer.
const signer = await getSignerById('wallet-id', 'signer-id');
console.log('Signer type:', signer.data.type);
console.log('Signer status:', signer.data.isActive);
getCustodyInfo(walletId)
Get wallet custody information and all signers.
const custodyInfo = await getCustodyInfo('wallet-id');
console.log('Custody type:', custodyInfo.data.custodyType);
console.log('Signers:', custodyInfo.data.signers);
Creating Signers
createEmailSigner(walletId, options)
Create an email-based signer (non-custodial). The user controls their keys via email authentication.
emailstringrequiredEmail address for the signer
devicePublicKeystringrequiredDevice public key (base64 encoded)
const signer = await createEmailSigner('wallet-id', {
email: 'user@example.com',
devicePublicKey: 'base64-encoded-device-public-key'
});
createPhoneSigner(walletId, options)
Create a phone-based signer (non-custodial). The user controls their keys via SMS authentication.
phonestringrequiredPhone number for the signer
devicePublicKeystringrequiredDevice public key (base64 encoded)
const signer = await createPhoneSigner('wallet-id', {
phone: '+1234567890',
devicePublicKey: 'base64-encoded-device-public-key'
});
createPasskeySigner(walletId)
Create a passkey signer using WebAuthn. This is a two-step process:
Step 1: Start Registration
const options = await startPasskeyRegistration('wallet-id');
Step 2: Complete Registration
// Use browser WebAuthn API
const credential = await navigator.credentials.create({
publicKey: options.data
});
// Verify and create signer
const signer = await verifyPasskeyRegistration('wallet-id', {
response: credential.response,
credentialId: credential.id
});
createExternalWalletSigner(walletId, options)
Link an external wallet (e.g., Phantom, Solflare) as a signer.
publicKeystringrequiredPublic key of the external wallet
const signer = await createExternalWalletSigner('wallet-id', {
publicKey: 'external-wallet-public-key'
});
createApiKeySigner(walletId, options)
Create an API key-based custodial signer. The platform manages the signing keys.
apiKeyNamestringrequiredName for the API key signer
const signer = await createApiKeySigner('wallet-id', {
apiKeyName: 'Server Signer'
});
Managing Signers
updateSigner(walletId, signerId, options)
Update signer configuration such as active status or primary designation.
isActivebooleanWhether the signer is active
isPrimarybooleanWhether this is the primary signer for the wallet
await updateSigner('wallet-id', 'signer-id', {
isActive: true,
isPrimary: false
});
updateSignerPermissions(walletId, signerPubkey, options)
Update signer permissions for on-chain signers.
permissionsobjectrequiredPermissions object
permissions.canSendSolbooleanCan send SOL
permissions.canSendTokensbooleanCan send SPL tokens
permissions.canExecuteCpibooleanCan execute CPI instructions
permissions.maxTransferAmountnumberMaximum transfer amount (optional)
permissions.allowedProgramsstring[]Allowed program public keys (optional)
// Read-only signer
await updateSignerPermissions('wallet-id', 'signer-public-key', {
permissions: {
canSendSol: false,
canSendTokens: false,
canExecuteCpi: false
}
});
// Transfer-only signer with limit
await updateSignerPermissions('wallet-id', 'signer-public-key', {
permissions: {
canSendSol: true,
canSendTokens: true,
canExecuteCpi: false,
maxTransferAmount: 1000000000 // 1 SOL limit
}
});
// Full access signer
await updateSignerPermissions('wallet-id', 'signer-public-key', {
permissions: {
canSendSol: true,
canSendTokens: true,
canExecuteCpi: true
}
});
deleteSigner(walletId, signerId)
Remove an authentication signer from the wallet. This will also remove it from on-chain registration. At least one signer must remain active.
await deleteSigner('wallet-id', 'signer-id');
At least one signer must remain active. You cannot delete the last active signer.
Device Identity Management
updateDeviceIdentity(walletId, signerId, options)
Replace a device for an email/phone signer while keeping the same signer.
oldDevicePublicKeystringOld device public key (optional if signer has only one device)
devicePublicKeystringrequiredNew device public key
encryptedMasterSecretstringrequiredEncrypted master secret for the new device
await updateDeviceIdentity('wallet-id', 'signer-id', {
devicePublicKey: 'new-device-public-key',
encryptedMasterSecret: 'encrypted-secret'
});
getDeviceEncryptedSecret(walletId, signerId, devicePublicKey?)
Retrieve the encrypted master secret for email/phone signers. Used for device-specific key recovery.
const secret = await getDeviceEncryptedSecret(
'wallet-id',
'signer-id',
'device-public-key'
);
Usage Examples
Multi-Signer Wallet Setup
// Create wallet
const wallet = await create({ label: 'Multi-Signer Wallet' });
// Add email signer (primary)
const emailSigner = await createEmailSigner(wallet.data.walletId, {
email: 'user@example.com',
devicePublicKey: deviceKey.publicKey
});
// Add phone signer (recovery)
const phoneSigner = await createPhoneSigner(wallet.data.walletId, {
phone: '+1234567890',
devicePublicKey: deviceKey.publicKey
});
// Add external wallet signer
const externalSigner = await createExternalWalletSigner(wallet.data.walletId, {
publicKey: 'phantom-wallet-public-key'
});
// List all signers
const allSigners = await listSigners(wallet.data.walletId);
console.log('Total signers:', allSigners.data.length);
Managing Signer Permissions
// Get all signers
const signers = await listSigners('wallet-id');
// Update permissions for each signer
for (const signer of signers.data) {
if (signer.type === 'EXTERNAL_WALLET') {
// Limit external wallet to read-only
await updateSignerPermissions('wallet-id', signer.publicKey, {
permissions: {
canSendSol: false,
canSendTokens: false,
canExecuteCpi: false
}
});
} else if (signer.type === 'API_KEY') {
// Full access for API key signer
await updateSignerPermissions('wallet-id', signer.publicKey, {
permissions: {
canSendSol: true,
canSendTokens: true,
canExecuteCpi: true
}
});
}
}
Signer Lifecycle Management
// Deactivate a signer temporarily
await updateSigner('wallet-id', 'signer-id', {
isActive: false
});
// Reactivate later
await updateSigner('wallet-id', 'signer-id', {
isActive: true
});
// Set primary signer
await updateSigner('wallet-id', 'signer-id', {
isPrimary: true
});
// Remove signer when no longer needed
await deleteSigner('wallet-id', 'signer-id');
Signer Types
| Type | Description | Custody | Use Case |
|---|---|---|---|
| Email-based authentication | Non-custodial | Web applications, email recovery | |
| Phone | SMS-based authentication | Non-custodial | Mobile apps, phone verification |
| Passkey | WebAuthn/FIDO2 passkeys | Non-custodial | Modern web auth, biometric |
| External Wallet | External wallet (Phantom, etc.) | Non-custodial | Existing wallet users |
| API Key | Platform-managed keys | Custodial | Server-side operations |
Best Practices
Add multiple signers (email + phone) for account recovery
Set granular permissions based on signer trust level
Regularly check signer status and activity
Maintain at least one active signer at all times