This document describes the admin signing service and best practices for handling admin/operator keys in the backend.
The admin signing service isolates admin operations behind a clear service boundary and enforces security best practices:
- Feature flag protection: Admin signing is disabled by default (
SOROBAN_ADMIN_SIGNING_ENABLED=false) - Operation whitelist: Only specific operations can be executed with admin keys
- Audit logging: All admin operations are logged (no secrets included)
- Service boundary: Admin operations are isolated from general request handlers
The following operations require admin signing and are controlled by the AdminSigningService:
pause- Pause a contract (emergency stop)unpause- Unpause a contract (resume operations)set_operator- Set or clear the operator address for a contractinit- Initialize a contract (if done via backend)
pause(contractId: string): Promise<string>- Returns transaction hashunpause(contractId: string): Promise<string>- Returns transaction hashsetOperator(contractId: string, operatorAddress: string | null): Promise<string>- Returns transaction hashinit(contractId: string, adminAddress: string, operatorAddress?: string): Promise<string>- Returns transaction hash
# Required: Admin secret key for signing transactions
SOROBAN_ADMIN_SECRET=your_admin_secret_key_here
# Required: Feature flag to enable admin signing (default: false)
SOROBAN_ADMIN_SIGNING_ENABLED=true-
Feature Flag: Admin signing is disabled by default. Set
SOROBAN_ADMIN_SIGNING_ENABLED=trueonly when admin operations are needed. -
Secret Management:
- Admin secrets should be stored securely (e.g., environment variables, secret management systems)
- Never log or expose admin secrets
- Rotate admin secrets regularly
-
Access Control:
- Admin operations should only be triggered by:
- Admin-only API endpoints (protected by authentication/authorization)
- Background jobs with proper access controls
- Manual scripts run by authorized personnel
- Admin operations should only be triggered by:
// In environment configuration
SOROBAN_ADMIN_SIGNING_ENABLED=true
SOROBAN_ADMIN_SECRET=your_admin_secret_keyimport { RealSorobanAdapter } from './soroban/real-adapter.js'
const adapter = new RealSorobanAdapter(config)
// Pause a contract
const txHash = await adapter.pause(contractId)
// Unpause a contract
const txHash = await adapter.unpause(contractId)
// Set operator
const txHash = await adapter.setOperator(contractId, operatorAddress)
// Clear operator (set to null)
const txHash = await adapter.setOperator(contractId, null)
// Initialize contract
const txHash = await adapter.init(contractId, adminAddress, operatorAddress)All admin operations are automatically logged with the following information:
- Timestamp: When the operation was executed
- Operation: The operation name (pause, unpause, set_operator, init)
- Contract ID: The contract being operated on
- Admin Public Key: The public key of the admin (derived from secret)
- Transaction Hash: The on-chain transaction hash (on success)
- Success/Failure: Whether the operation succeeded
- Error: Error message if the operation failed
Important: No secrets are included in audit logs. Only the public key is logged.
{
"timestamp": "2024-01-15T10:30:00.000Z",
"operation": "pause",
"contractId": "C...",
"adminPublicKey": "G...",
"transactionHash": "abc123...",
"success": true
}❌ Bad: Admin operations triggered by user requests
// DON'T: Allow any user request to trigger admin operations
app.post('/api/user/action', async (req, res) => {
await adapter.pause(contractId) // DANGEROUS!
})✅ Good: Admin operations isolated to admin-only endpoints
// DO: Protect admin operations behind authentication/authorization
app.post('/api/admin/pause', requireAdminAuth, async (req, res) => {
await adapter.pause(contractId) // Safe - requires admin auth
})Always check the feature flag before enabling admin operations:
if (!env.SOROBAN_ADMIN_SIGNING_ENABLED) {
throw new Error('Admin signing is disabled')
}Admin operations should be protected by:
- Authentication (verify user identity)
- Authorization (verify user has admin role)
- Rate limiting (prevent abuse)
- Audit trails (log who did what)
Create dedicated admin endpoints or background jobs for admin operations:
// Admin-only route
app.post('/api/admin/contracts/:contractId/pause',
requireAdminAuth,
async (req, res) => {
const { contractId } = req.params
const txHash = await adapter.pause(contractId)
res.json({ transactionHash: txHash })
}
)Set up alerts for:
- Unusual admin operation patterns
- Failed admin operations
- Admin operations outside business hours
The recordReceipt operation is NOT an admin operation. It's a regular operation that records transaction receipts.
Current Implementation: recordReceipt currently uses the admin secret for signing, but this is a legacy implementation. In the future, this may be refactored to use:
- Operator key (if operator is configured)
- Dedicated receipt-signing key
- User-signed transactions
Note: recordReceipt does not require SOROBAN_ADMIN_SIGNING_ENABLED=true because it's not an admin operation.
The admin signing service throws specific errors:
ConfigurationError: When admin signing is disabled or admin secret is missingTransactionError: When the on-chain transaction failsContractError: When the contract rejects the operation
Always handle these errors appropriately:
try {
const txHash = await adapter.pause(contractId)
logger.info('Contract paused', { txHash })
} catch (err) {
if (err instanceof ConfigurationError) {
logger.error('Admin signing not configured', { error: err.message })
} else if (err instanceof TransactionError) {
logger.error('Transaction failed', { error: err.message, txHash: err.txHash })
} else {
logger.error('Unexpected error', { error: err })
}
throw err
}When testing admin operations:
- Use a test admin secret (never use production secrets in tests)
- Mock the admin signing service in unit tests
- Use integration tests with a test network for end-to-end testing
- Verify audit logs are generated correctly
If you're migrating from the old admin secret usage:
- Enable the feature flag: Set
SOROBAN_ADMIN_SIGNING_ENABLED=true - Update code: Replace direct
invokeTransactioncalls with admin operation methods - Add access control: Ensure admin operations are behind proper authentication/authorization
- Review audit logs: Verify all admin operations are being logged