How to Implement Secure Credential Storage and Encryption in Fincept Terminal
Fincept Terminal uses a two-layer architecture combining OS-native secret services with per-account key scoping to protect API keys and tokens, delegating encryption to Windows Credential Manager, macOS Keychain, and a temporary XOR obfuscation on Linux.
The Fincept Terminal trading platform stores sensitive broker credentials through a hardened abstraction layer that avoids custom cryptography in favor of operating-system native secure storage. The SecureStorage class in fincept-qt/src/storage/secure/SecureStorage.h provides a unified API for persisting secrets, while AccountManager in fincept-qt/src/trading/AccountManager.h orchestrates credential lifecycles with per-account isolation. Together, these components ensure that API keys, access tokens, and user secrets remain encrypted at rest without exposing plain text to the application layer.
SecureStorage Architecture and Key Scoping
The credential system centers on two primary classes that separate storage mechanics from business logic.
SecureStorage exposes three core operations—store(), retrieve(), and remove()—implemented in fincept-qt/src/storage/secure/SecureStorage.cpp. These methods delegate to platform-native backends:
- Windows: DPAPI-encrypted Credential Manager via
CredWriteW - macOS: Keychain Services via
SecKeychainAddGenericPassword - Linux: XOR-obfuscated
QSettings(with planned libsecret migration)
AccountManager consumes this API to manage BrokerCredentials structures defined in fincept-qt/src/trading/BrokerInterface.h. It enforces per-account key scoping using the format account.<account_id>.<field>, preventing cross-account leakage. For example, an API key for account 12345 stores under account.12345.api_key.
Storing Credentials with AccountManager
To persist credentials, populate a BrokerCredentials object and invoke AccountManager::save_credentials().
// Create and populate credentials
BrokerCredentials creds;
creds.broker_id = "alpaca";
creds.api_key = "PK_TEST_KEY";
creds.api_secret = "SECRET_VALUE";
creds.access_token = "jwt_token_123";
creds.user_id = "user456";
// Persist to secure storage
AccountManager::instance().save_credentials(account_id, creds);
Under the hood, save_credentials() constructs keys like account.<id>.api_key and delegates to SecureStorage::store().
- Windows: Calls
CredWriteWwith aCREDENTIALWstructure (lines 65-81 inSecureStorage.cpp) - macOS: Invokes
SecKeychainAddGenericPasswordafter deleting existing entries (lines 83-107) - Linux: XOR-obfuscates the payload and writes Base64 to
QSettingsunder thesecure/namespace (lines 109-117)
All operations return a Result object, allowing error handling without exceptions. Failed writes trigger LOG_ERROR entries while leaving the application state intact.
Retrieving and Clearing Credentials
Loading credentials mirrors the storage pattern with automatic decryption on supported platforms.
// Load credentials for a specific account
BrokerCredentials creds = AccountManager::instance().load_credentials(account_id);
if (!creds.api_key.isEmpty()) {
// Initialize broker client with decrypted credentials
}
On Windows and macOS, SecureStorage::retrieve() returns clear text after the OS automatically decrypts the secret. On Linux, the method decodes Base64 and reverses the XOR obfuscation (lines 55-64 in SecureStorage.cpp).
To remove credentials permanently:
AccountManager::instance().clear_credentials(account_id);
This calls SecureStorage::remove() for each stored field, deleting entries from the respective backend.
Platform-Specific Encryption Backends
Fincept Terminal leverages native OS security rather than implementing custom encryption algorithms.
Windows: Uses CredWriteW to store secrets in the Windows Credential Manager, automatically protected by DPAPI (Data Protection API). The implementation creates CREDENTIALW structures with the CRED_TYPE_GENERIC type.
macOS: Integrates with Keychain Services via SecKeychainAddGenericPassword. The code handles item updates by first deleting existing entries, ensuring atomic replacement of secrets.
Linux: Currently implements a deliberately weak XOR obfuscation only to prevent casual inspection of QSettings files. The source code at lines 112-117 of SecureStorage.cpp contains TODO markers indicating planned migration to libsecret or similar secret-service integration. Do not rely on the Linux implementation for production security without replacing this backend.
Migrating Legacy Credentials
Historical versions stored credentials under flat keys like broker.<broker_id>.api_key. The AccountManager constructor automatically invokes migrate_legacy_credentials() (lines 49-128 in AccountManager.cpp) to transition data to the scoped format.
The migration process:
- Checks a flag in
SettingsRepositoryto prevent duplicate runs - Iterates registered brokers from
BrokerRegistry - Reads legacy keys via
SecureStorage::retrieve - Creates new
BrokerAccountinstances with UUID-based IDs - Re-saves credentials using
save_credentials()under scoped keys - Updates the migration flag
This runs once per installation and is safe to execute on every startup.
Extending Linux Storage with libsecret
To replace the XOR fallback with proper Linux encryption:
- Create
SecureStorageLibsecret.cppimplementing the storage interface usinglibsecretAPIs - Guard with
#elif defined(Q_OS_LINUX)inSecureStorage.cpp - Replace the XOR block (lines 109-117) with calls to your new wrapper
- Update
CMakeLists.txtinfincept-qt/src/storage/secureto link againstlibsecret-1
Maintain the Result return pattern to preserve error-handling contracts with AccountManager.
Summary
- SecureStorage abstracts OS-native encryption via
store(),retrieve(), andremove()infincept-qt/src/storage/secure/SecureStorage.cpp - AccountManager provides per-account isolation using scoped keys like
account.<id>.api_key - Windows and macOS use DPAPI and Keychain respectively; Linux currently uses XOR obfuscation requiring replacement
- Legacy credentials automatically migrate to the new scoped format via
migrate_legacy_credentials() - Always use the
Result<T>pattern for error handling rather than exceptions
Frequently Asked Questions
How does Fincept Terminal encrypt stored API keys?
On Windows, Fincept Terminal uses the Data Protection API (DPAPI) through the Credential Manager. macOS leverages the Keychain Services framework. Linux currently applies XOR obfuscation as a placeholder, with the source code in SecureStorage.cpp marked for replacement with libsecret integration. No custom encryption algorithms are implemented.
Where are credentials physically stored on each operating system?
Windows stores secrets in the Credential Manager vault under generic credentials. macOS writes to the user Keychain as generic passwords. Linux persists Base64-encoded XOR data in QSettings files typically located in ~/.config/Fincept/ until the planned libsecret backend relocates them to the Secret Service.
Is the Linux XOR implementation secure for production use?
No. The XOR obfuscation in SecureStorage.cpp (lines 109-117) provides only casual obfuscation against file inspection. It lacks cryptographic security and should be replaced with a libsecret or D-Bus Secret Service implementation before deploying in production environments.
How do I add a new broker with custom credential fields?
Define the credential schema in your broker implementation of IBroker from fincept-qt/src/trading/BrokerInterface.h, specify the required fields in credential_fields, then collect values through AccountManagementDialog in fincept-qt/src/screens/equity_trading/AccountManagementDialog.cpp. Pass the populated BrokerCredentials struct to AccountManager::save_credentials() with a unique account ID to trigger automatic secure storage.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →