-
Notifications
You must be signed in to change notification settings - Fork 0
Open
Labels
module:coreCore infrastructureCore infrastructurestoryFeature story linked to epicFeature story linked to epic
Milestone
Description
User Story
As a developer, I want qctl to store my credentials in the OS keychain so I don't have tokens in plain text files and they're protected by my system's security.
Design
Command Interface
# Credentials are stored automatically on login
qctl auth login
# -> Stores token in macOS Keychain / Windows Credential Store / Linux secret-tool
# View credential storage location
qctl auth status
# -> Shows: "Credentials stored in: macOS Keychain"
# Configure storage backend
qctl config set auth.credential_store keychain # keychain | file | noneKeychain Entries
# macOS Keychain
Service: qctl
Account: voyage:access_token
Password: <token>
Service: qctl
Account: voyage:refresh_token
Password: <token>
# Windows Credential Store
Target: qctl:voyage:access_token
User: qctl
Password: <token>
# Linux secret-tool (libsecret)
Label: qctl voyage access_token
Attributes: application=qctl, provider=voyage, type=access_token
Secret: <token>
Fallback Strategy
1. Try OS keychain
2. If unavailable, fall back to encrypted file (~/.qctl/credentials.enc)
3. If encryption unavailable, warn and use plain file with strict permissions
Storage Configuration
# ~/.qctl/qctl.yaml
auth:
credential_store: keychain # keychain | file | memory
file_encryption: true # encrypt file-based storage
encryption_key_source: keychain # where to get encryption keyFiles to Create/Modify
| File | Action | Purpose |
|---|---|---|
qctl-core/src/main/java/io/qrun/qctl/core/auth/CredentialStore.java |
Create | Store interface |
qctl-core/src/main/java/io/qrun/qctl/core/auth/KeychainStore.java |
Create | OS keychain implementation |
qctl-core/src/main/java/io/qrun/qctl/core/auth/MacOSKeychain.java |
Create | macOS Keychain via Security.framework |
qctl-core/src/main/java/io/qrun/qctl/core/auth/WindowsCredStore.java |
Create | Windows Credential Store |
qctl-core/src/main/java/io/qrun/qctl/core/auth/LinuxSecretService.java |
Create | Linux libsecret/secret-tool |
qctl-core/src/main/java/io/qrun/qctl/core/auth/FileCredentialStore.java |
Create | Encrypted file fallback |
qctl-core/src/main/java/io/qrun/qctl/core/auth/CredentialStoreFactory.java |
Create | Platform detection |
Implementation Tasks
- Create CredentialStore interface
- Implement MacOSKeychain using JNA/native calls
- Implement WindowsCredStore using JNA/Credential Manager API
- Implement LinuxSecretService using secret-tool CLI
- Implement FileCredentialStore with AES encryption
- Create CredentialStoreFactory with platform detection
- Integrate with TokenManager
- Handle keychain access prompts gracefully
- Add fallback chain with warnings
- Support credential migration between stores
- Set strict file permissions (600) for fallback
- Write unit tests for each store implementation
- Write integration tests for credential lifecycle
Acceptance Criteria
- macOS: tokens stored in Keychain.app
- Windows: tokens in Credential Manager
- Linux: tokens via secret-tool (libsecret)
- Fallback to encrypted file if keychain unavailable
-
qctl auth statusshows storage location - Tokens survive qctl upgrades
- No plaintext tokens in ~/.qctl/
- Graceful handling of keychain access denied
- Works in headless/SSH environments with fallback
Metadata
Metadata
Assignees
Labels
module:coreCore infrastructureCore infrastructurestoryFeature story linked to epicFeature story linked to epic
Type
Projects
Status
No status