This document describes the modular command architecture introduced in December 2025 to improve maintainability and organization.
The CLI has been refactored from a monolithic 842-line main.rs to a clean, modular architecture with only 35 lines in the main entry point.
- main.rs (35 lines)
- Configuration loading
- Tracing initialization
- Command dispatch to handlers
Commands are organized by functional category in the commands/ directory:
| Module | Purpose | Commands |
|---|---|---|
| info.rs | Information & listing | info, list-slots, list-mechanisms, list-objects |
| token.rs | Token management | init-token, init-pin, delete-token |
| keys.rs | Key management | gen-keypair, delete-key, inspect-key, export-pubkey |
| crypto.rs | Asymmetric crypto | sign, verify, encrypt, decrypt |
| symmetric.rs | Symmetric crypto | gen-symmetric-key, encrypt-symmetric, decrypt-symmetric |
| key_wrap.rs | Key wrapping | wrap-key, unwrap-key |
| mac.rs | Message authentication | gen-hmac-key, hmac-sign, hmac-verify, gen-cmac-key, cmac-sign, cmac-verify |
| util.rs | Utilities | benchmark, audit-keys, explain-error, find-key, diff-keys, gen-random, hash, gen-csr |
| analyze.rs | Observability | analyze |
- Focused modules: Each handler focuses on a specific command category
- Separation of concerns: Clear boundaries between different functionality
- Reduced complexity: No more 842-line files
- Isolated testing: Each command handler can be tested independently
- Mocking: Easier to mock dependencies for unit tests
- Integration: Clear interfaces for integration testing
- Shared utilities: Common PIN handling, configuration, and context management
- DRY principle: Eliminates duplicate PIN and config handling code
- Consistent patterns: Standardized error handling and logging
- Easy additions: New commands just add handlers to appropriate modules
- Plugin architecture: Commands are cleanly separated and self-contained
- Future-proof: Structure supports future command categories
The CommandContext struct provides shared state and utilities:
pub struct CommandContext {
pub module_path: String,
pub config: Config,
}Centralized PIN handling supports multiple input methods:
- Command-line arguments
- Stdin input (
--pin-stdinflag) - Environment variables (through config)
Consistent error handling across all command handlers using anyhow::Result.
- Add to CLI definition: Update
Commandsenum incli.rs - Choose handler module: Add to existing category or create new module
- Implement handler: Use shared utilities from
common.rs - Update dispatcher: Add routing in
commands/mod.rs - Add tests: Update integration test scripts
- Document: Update command documentation
- ✅ All 55 tests pass - No functionality lost
- ✅ 35-line main.rs - 96% reduction in main file size
- ✅ Modular structure - Commands organized by purpose
- ✅ Shared utilities - DRY principle applied
- ✅ Future-ready - Easy to extend and maintain
The refactoring maintains full backward compatibility while dramatically improving code organization and maintainability.